#!/usr/bin/env bash set -euo pipefail repo_root="${1:-$(pwd)}" output_root="${2:-$repo_root/artifacts}" package_version="${LSERP_PACKAGE_VERSION:-0.4.0}" if [[ ! "$package_version" =~ ^[0-9]{1,4}\.[0-9]{1,4}\.[0-9]{1,4}$ ]]; then echo 'LSERP_PACKAGE_VERSION must be a three-part numeric version.' >&2 exit 3 fi dotnet6="${LSERP_DOTNET6:-dotnet}" dotnet8="${LSERP_DOTNET8:-dotnet}" python_bin="${LSERP_PYTHON:-python3}" pwsh_bin="${LSERP_PWSH:-pwsh}" npm_bin="${LSERP_NPM:-npm}" astrbot_contract_python="${LSERP_ASTRBOT_CONTRACT_PYTHON:-}" astrbot_source="${LSERP_ASTRBOT_SOURCE:-}" host_certificate_thumbprint="${LSERP_HOST_CERT_THUMBPRINT:-}" host_timestamp_url="${LSERP_HOST_TIMESTAMP_URL:-}" host_certificate_store="${LSERP_HOST_CERT_STORE:-CurrentUser}" host_signtool_path="${LSERP_HOST_SIGNTOOL:-}" host_signing_requested=false if [[ -z "$astrbot_contract_python" || ! -f "$astrbot_contract_python" || \ -z "$astrbot_source" || ! -d "$astrbot_source" ]]; then echo 'Commercial packaging requires LSERP_ASTRBOT_CONTRACT_PYTHON and LSERP_ASTRBOT_SOURCE for the reviewed AstrBot 4.27.2 checkout.' >&2 exit 3 fi if [[ -n "$host_certificate_thumbprint" || -n "$host_timestamp_url" || \ -n "$host_signtool_path" ]]; then if [[ ! "$host_certificate_thumbprint" =~ ^[A-Fa-f0-9]{40}$ || \ ! "$host_timestamp_url" =~ ^https:// || \ ( "$host_certificate_store" != CurrentUser && \ "$host_certificate_store" != LocalMachine ) ]]; then echo 'Host signing parameters are incomplete or invalid.' >&2 exit 3 fi host_signing_requested=true fi repo_root="$(cd "$repo_root" && pwd)" mkdir -p "$output_root" output_root="$(cd "$output_root" && pwd)" package_name="Lserp-AgentPet-${package_version}-win-x64" final_directory="$output_root/$package_name" final_archive="$output_root/$package_name.zip" if [[ -e "$final_directory" || -e "$final_archive" ]]; then echo "Refusing to overwrite an existing package: $package_name" >&2 exit 2 fi temporary_root="$(mktemp -d "${TMPDIR:-/tmp}/lserp-commercial-package.XXXXXX")" cleanup() { rm -rf -- "$temporary_root" } trap cleanup EXIT logs="$temporary_root/logs" stage="$temporary_root/$package_name" mkdir -p "$logs" "$stage/Host" "$stage/AstrBotPlugin" \ "$stage/Deployment/SqlServer" "$stage/Deployment/customer-profiles" \ "$stage/PythonWheels" "$stage/Contracts" plugin_root="$repo_root/插件库/astrbot_plugin_lserp" python_test_venv="$temporary_root/python-test-venv" python_test_wheels="$temporary_root/python-test-wheels" mkdir -p "$python_test_wheels" "$python_bin" -m pip download \ --only-binary=:all: \ --require-hashes \ --dest "$python_test_wheels" \ --requirement "$plugin_root/requirements.txt" "$python_bin" -m pip download \ --only-binary=:all: \ --platform win_amd64 \ --require-hashes \ --dest "$stage/PythonWheels" \ --requirement "$plugin_root/requirements.txt" "$python_bin" -m venv "$python_test_venv" if [[ -f "$python_test_venv/bin/python" ]]; then test_python="$python_test_venv/bin/python" elif [[ -f "$python_test_venv/Scripts/python.exe" ]]; then test_python="$python_test_venv/Scripts/python.exe" else echo 'Isolated Python test runtime was not created.' >&2 exit 3 fi "$test_python" -m pip install \ --no-index \ --require-hashes \ --find-links "$python_test_wheels" \ --requirement "$plugin_root/requirements.txt" "$test_python" -m pip check astrbot_contract_evidence="$logs/astrbot-runtime-contract.json" "$astrbot_contract_python" "$plugin_root/verify_astrbot_contract.py" \ --astrbot-source "$astrbot_source" \ --output "$astrbot_contract_evidence" \ 2>&1 | tee "$logs/astrbot-runtime-contract.log" if [[ ! -s "$astrbot_contract_evidence" ]]; then echo 'Actual AstrBot runtime contract evidence was not created.' >&2 exit 4 fi "$dotnet6" run --project "$repo_root/插件库/Lskj.CommandKernel.Tests/Lskj.CommandKernel.Tests.csproj" -c Release 2>&1 | tee "$logs/kernel.log" "$dotnet8" build \ "$repo_root/插件库/Lskj.LegacyApiCompatibility.Tests/Lskj.LegacyApiCompatibility.Tests.csproj" \ -c Release 2>&1 | tee "$logs/legacy-api.log" "$dotnet8" run \ --project "$repo_root/插件库/Lskj.SqlContract.Tests/Lskj.SqlContract.Tests.csproj" \ -c Release -- "$repo_root" 2>&1 | tee "$logs/sql-contract.log" "$dotnet8" run --project "$repo_root/插件库/Lskj.AgentPet.Host.Tests/Lskj.AgentPet.Host.Tests.csproj" -c Release 2>&1 | tee "$logs/host.log" (cd "$repo_root/插件库/Lskj.AgentPet" && "$npm_bin" test) 2>&1 | tee "$logs/node.log" (cd "$plugin_root" && "$test_python" -m unittest discover -s tests -v) 2>&1 | tee "$logs/python.log" if grep -Eq 'skipped=[1-9][0-9]*' "$logs/python.log"; then echo 'Commercial AstrBot verification must not skip worker tests.' >&2 exit 4 fi "$pwsh_bin" -NoLogo -NoProfile -File \ "$repo_root/插件库/Lskj.AgentBridge/Deployment/CommercialPackage/Test-DeploymentContracts.ps1" \ -RepoRoot "$repo_root" 2>&1 | tee "$logs/deployment.log" host_publish="$temporary_root/host-publish" "$dotnet8" publish \ "$repo_root/插件库/Lskj.AgentPet.Host/Lskj.AgentPet.Host.csproj" \ -c Release \ -p:PublishProfile=WinX64 \ --output "$host_publish" 2>&1 | tee "$logs/publish.log" if [[ ! -s "$host_publish/Lskj.AgentPet.Host.exe" ]]; then echo 'Windows host publish output is missing.' >&2 exit 3 fi bridge_cli_publish="$temporary_root/bridge-cli-publish" "$dotnet8" publish \ "$repo_root/插件库/Lskj.BridgeCli/Lskj.BridgeCli.csproj" \ -c Release \ -r win-x64 \ --self-contained true \ -p:Version="$package_version" \ -p:PublishSingleFile=true \ -p:IncludeNativeLibrariesForSelfExtract=true \ -p:EnableCompressionInSingleFile=true \ -p:PublishTrimmed=false \ -p:PublishReadyToRun=false \ -p:DebugType=None \ -p:DebugSymbols=false \ --output "$bridge_cli_publish" 2>&1 | tee "$logs/bridge-cli-publish.log" if [[ ! -s "$bridge_cli_publish/lserp-agent-cli.exe" ]] || \ find "$bridge_cli_publish" -mindepth 1 -maxdepth 1 \ ! -name 'lserp-agent-cli.exe' | grep -q .; then echo 'Windows bridge-only CLI must be one self-contained executable.' >&2 exit 3 fi if [[ -e "$host_publish/lserp-agent-cli.exe" ]]; then echo 'Host publish unexpectedly contains the bridge CLI name.' >&2 exit 3 fi cp "$bridge_cli_publish/lserp-agent-cli.exe" "$host_publish/" if [[ ! -s "$host_publish/lserp-agent-cli.exe" ]]; then echo 'Merged commercial bridge-only CLI is missing.' >&2 exit 3 fi host_authenticode_signed=false if [[ "$host_signing_requested" == true ]]; then sign_arguments=( -NoLogo -NoProfile -File "$repo_root/插件库/Lskj.AgentBridge/Deployment/Sign-LserpAgentPetHost.ps1" -HostDirectory "$host_publish" -CertificateThumbprint "$host_certificate_thumbprint" -CertificateStoreLocation "$host_certificate_store" -TimestampUrl "$host_timestamp_url" ) if [[ -n "$host_signtool_path" ]]; then sign_arguments+=( -SignToolPath "$host_signtool_path" ) fi "$pwsh_bin" "${sign_arguments[@]}" 2>&1 | tee "$logs/host-signing.log" host_authenticode_signed=true fi if [[ -e "$host_publish/Web" ]] || \ find "$host_publish" -maxdepth 1 -type f \( \ -name 'index.html' -o -name 'pet.css' -o -name 'pet-runtime.js' \ -o -name 'bridge-client.js' -o -name 'pet-shell.js' \) | grep -q .; then echo 'Commercial host publish must not contain mutable external Web assets.' >&2 exit 3 fi cp -R "$host_publish/." "$stage/Host/" astrbot_plugin_files=( README.md __init__.py _conf_schema.json astrbot-contract.json astrbot_contract.py attachment_extract.py attachment_provenance.py attachment_sandbox.py attachment_worker.py bridge_protocol.py main.py metadata.yaml pdf_render_sandbox.py pdf_render_worker.py pdf_vision.py plan_chain.py prompt.py purchase_tabular_binding.py purchase_vision_binding.py requirements.txt session_auth.py tools.py verify_astrbot_contract.py verify_minimax_vlm_contract.py vision.py ) for relative in "${astrbot_plugin_files[@]}"; do source="$plugin_root/$relative" if [[ ! -f "$source" || -L "$source" ]]; then echo "Required AstrBot plugin source is missing or linked: $relative" >&2 exit 3 fi cp "$source" "$stage/AstrBotPlugin/$relative" done deployment_root="$repo_root/插件库/Lskj.AgentBridge/Deployment" cp "$deployment_root/CUSTOMER_ACCEPTANCE.md" "$stage/Deployment/" cp "$deployment_root/WRITE_ACCEPTANCE.md" "$stage/Deployment/" cp "$deployment_root/DYNAMIC_MODULE_WRITE_ACCEPTANCE.md" "$stage/Deployment/" cp "$deployment_root/FIELD_VALIDATION_RUNBOOK.md" "$stage/Deployment/" cp "$deployment_root/THIRD_PARTY_COMPLIANCE.md" "$stage/Deployment/" cp "$deployment_root/guga-upstream-audit.v1.json" "$stage/Deployment/" cp "$deployment_root/New-WorkflowAcceptanceEvidence.ps1" "$stage/Deployment/" cp "$deployment_root/New-DynamicModuleWriteAcceptance.ps1" "$stage/Deployment/" cp "$deployment_root/dynamic-module-write-modules.example.json" "$stage/Deployment/" cp "$deployment_root/New-DynamicModuleUpdateAcceptance.ps1" "$stage/Deployment/" cp "$deployment_root/dynamic-module-update-modules.example.json" "$stage/Deployment/" cp "$deployment_root/New-WorkflowWriteCasesTemplate.ps1" "$stage/Deployment/" cp "$deployment_root/New-WorkflowWriteIntegrationEvidence.ps1" "$stage/Deployment/" cp "$deployment_root/Invoke-WorkflowWriteCaseCapture.ps1" "$stage/Deployment/" cp "$deployment_root/Invoke-LserpFieldReadOnlyValidation.ps1" "$stage/Deployment/" cp "$deployment_root/field-readonly-validation.example.json" "$stage/Deployment/" cp "$deployment_root/Invoke-LserpReadOnlySessionPreflight.ps1" "$stage/Deployment/" cp "$deployment_root/Invoke-LserpSelectOnlyCatalogSnapshot.ps1" "$stage/Deployment/" cp "$deployment_root/Invoke-LserpSelectOnlyProfilePreflight.ps1" "$stage/Deployment/" cp "$deployment_root/New-WorkflowUatAuthorization.ps1" "$stage/Deployment/" cp "$deployment_root/New-WorkflowWriteUatCampaign.ps1" "$stage/Deployment/" cp "$deployment_root/Test-WorkflowWriteUatCampaign.ps1" "$stage/Deployment/" cp "$deployment_root/workflow-write-uat-case-catalog.v1.json" "$stage/Deployment/" cp "$deployment_root/New-CustomerAcceptanceBundle.ps1" "$stage/Deployment/" cp "$deployment_root/Build-LegacyErpAcceptance.ps1" "$stage/Deployment/" cp "$deployment_root/Sign-LserpAgentPetHost.ps1" "$stage/Deployment/" cp "$deployment_root/CommercialPackage/Test-DeploymentContracts.ps1" "$stage/Deployment/" cp "$deployment_root/business-adapters.example.json" "$stage/Deployment/" cp "$deployment_root/command-rollout.example.json" "$stage/Deployment/" cp "$deployment_root/SqlServer/"*.sql "$stage/Deployment/SqlServer/" cp "$deployment_root/customer-profiles/"*.json "$stage/Deployment/customer-profiles/" cp "$deployment_root/customer-profiles/"*.sql "$stage/Deployment/customer-profiles/" cp "$deployment_root/customer-profiles/README.md" "$stage/Deployment/customer-profiles/" cp "$deployment_root/CommercialPackage/README.md" "$stage/README.md" cp "$deployment_root/CommercialPackage/Start-LserpAgentPet.ps1" "$stage/Start-LserpAgentPet.ps1" cp "$deployment_root/CommercialPackage/Verify-LserpCommercialPackage.ps1" "$stage/Verify-LserpCommercialPackage.ps1" cp "$repo_root/插件库/Contracts/erp-agent-wire-contract-v1.json" "$stage/Contracts/" # The package version is also the version reported by the bundled bridge CLI. # Keep customer-facing runbooks and handoff templates bound to that exact # version; a source checkout may be used to produce more than one package # version, so these documents must not carry a stale release number. "$python_bin" - "$stage" "$package_version" <<'PY' import json import pathlib import sys stage = pathlib.Path(sys.argv[1]) package_version = sys.argv[2] marker = "@LSERP_PACKAGE_VERSION@" relative_paths = [ "README.md", "Deployment/CUSTOMER_ACCEPTANCE.md", "Deployment/WRITE_ACCEPTANCE.md", "Deployment/field-readonly-validation.example.json", ] for relative in relative_paths: path = stage / relative if not path.is_file(): raise SystemExit("package version binding source is missing: " + relative) text = path.read_text(encoding="utf-8") if marker not in text: raise SystemExit("package version marker is missing: " + relative) rendered = text.replace(marker, package_version) if marker in rendered: raise SystemExit("package version marker was not fully rendered: " + relative) path.write_text(rendered, encoding="utf-8") template = json.loads( (stage / "Deployment/field-readonly-validation.example.json") .read_text(encoding="utf-8") ) expected_cli_version = template.get("cli", {}).get("version") expected_cli_path = template.get("cli", {}).get("path") if expected_cli_version != package_version or package_version not in expected_cli_path: raise SystemExit("rendered field validation template is not package-version bound") PY git_commit="unknown" worktree_dirty=true if git -C "$repo_root" rev-parse --is-inside-work-tree >/dev/null 2>&1; then git_commit="$(git -C "$repo_root" rev-parse HEAD)" if [[ -z "$(git -C "$repo_root" status --porcelain)" ]]; then worktree_dirty=false fi fi export LSERP_STAGE="$stage" export LSERP_LOGS="$logs" export LSERP_PACKAGE_VERSION_VALUE="$package_version" export LSERP_GIT_COMMIT_VALUE="$git_commit" export LSERP_WORKTREE_DIRTY_VALUE="$worktree_dirty" export LSERP_HOST_AUTHENTICODE_SIGNED_VALUE="$host_authenticode_signed" export LSERP_HOST_CERT_THUMBPRINT_VALUE="$host_certificate_thumbprint" export LSERP_ASTRBOT_CONTRACT_EVIDENCE="$astrbot_contract_evidence" "$python_bin" - <<'PY' import hashlib import json import os import pathlib import re import sys from datetime import datetime, timezone stage = pathlib.Path(os.environ["LSERP_STAGE"]) logs = pathlib.Path(os.environ["LSERP_LOGS"]) def parse_pair(name: str) -> tuple[int, int]: text = (logs / name).read_text(encoding="utf-8", errors="replace") matches = re.findall(r"passed=(\d+) failed=(\d+)", text) if not matches: raise SystemExit(f"missing test summary in {name}") return tuple(map(int, matches[-1])) kernel = parse_pair("kernel.log") host = parse_pair("host.log") deployment = parse_pair("deployment.log") sql_contract = parse_pair("sql-contract.log") node_text = (logs / "node.log").read_text(encoding="utf-8", errors="replace") python_text = (logs / "python.log").read_text(encoding="utf-8", errors="replace") sql_contract_text = (logs / "sql-contract.log").read_text( encoding="utf-8", errors="replace" ) node_match = re.findall( r"(?m)^\s*(?:#|ℹ)\s+tests\s+(\d+)\s*$", node_text, ) python_match = re.findall(r"Ran (\d+) tests", python_text) if not node_match or not python_match: raise SystemExit("missing Node or Python test summary") python_skipped_match = re.findall(r"skipped=(\d+)", python_text) python_skipped = int(python_skipped_match[-1]) if python_skipped_match else 0 if python_skipped != 0: raise SystemExit("commercial AstrBot verification contains skipped tests") astrbot_contract = json.loads( pathlib.Path(os.environ["LSERP_ASTRBOT_CONTRACT_EVIDENCE"]).read_text( encoding="utf-8" ) ) expected_astrbot_contract_keys = { "schemaVersion", "passed", "repository", "sourceTag", "sourceCommit", "runtimeVersion", "versionSpecifier", "pluginVersion", "licenseExpression", "licenseSha256", "eulaSha256", "criticalSourceFilesVerified", "registeredTools", } if ( not isinstance(astrbot_contract, dict) or set(astrbot_contract) != expected_astrbot_contract_keys or astrbot_contract["schemaVersion"] != "1.1" or astrbot_contract["passed"] is not True or astrbot_contract["repository"] != "https://github.com/AstrBotDevs/AstrBot.git" or astrbot_contract["sourceTag"] != "v4.27.2" or astrbot_contract["sourceCommit"] != "ad4fbfa90ca0c4ac2b30b3250e34dbf8fe7babbf" or astrbot_contract["runtimeVersion"] != "4.27.2" or astrbot_contract["versionSpecifier"] != "==4.27.2" or astrbot_contract["pluginVersion"] != "0.4.0" or astrbot_contract["licenseExpression"] != "AGPL-3.0-or-later" or astrbot_contract["licenseSha256"] != "ccf7d08f932af3e813848881731113afbb7c80d0fd6d958e8d319002bf344d02" or astrbot_contract["eulaSha256"] != "c332de7781e87c67d6d3beda463fa04705075a6bae9e52a252f7c639f6defd80" or astrbot_contract["criticalSourceFilesVerified"] != 20 or astrbot_contract["registeredTools"] != ["erp_get_context", "erp_get_capabilities", "erp_plan_command"] ): raise SystemExit("actual AstrBot runtime contract evidence is invalid") guga_audit_path = stage / "Deployment" / "guga-upstream-audit.v1.json" guga_audit_bytes = guga_audit_path.read_bytes() guga_audit = json.loads(guga_audit_bytes.decode("utf-8")) asset = guga_audit.get("asset") if isinstance(guga_audit, dict) else None installer = guga_audit.get("installer") if isinstance(guga_audit, dict) else None service_source = ( guga_audit.get("serviceSource") if isinstance(guga_audit, dict) else None ) commercial_decision = ( guga_audit.get("commercialDecision") if isinstance(guga_audit, dict) else None ) if ( not isinstance(guga_audit, dict) or set(guga_audit) != { "schemaVersion", "auditedAtUtc", "asset", "installer", "serviceSource", "commercialDecision", } or guga_audit["schemaVersion"] != "1.0" or guga_audit["auditedAtUtc"] != "2026-08-13T19:31:24+00:00" or not isinstance(asset, dict) or set(asset) != { "id", "displayName", "ownerHandle", "ownerName", "uploadedAtUtc", "shareUrl", "shareDataUrl", "downloadUrl", "packageSha256", "packageSizeBytes", "manifestSha256", "spriteSha256", "spriteSizeBytes", "atlasSize", "licenseMetadataPresent", "licenseFilePresent", } or asset["id"] != "guga" or asset["ownerHandle"] != "circus" or asset["shareUrl"] != "https://codex-pets.net/share/guga" or asset["packageSha256"] != "3ebd971ba59a0c988a6be0924669b4c5db9234bcc5d17d506e34eba332e6021f" or asset["packageSizeBytes"] != 1946012 or asset["manifestSha256"] != "f9f715811c26ca610764a7698e28f2e182882f097f4c60a3f00a79dd7530bd20" or asset["spriteSha256"] != "1b61ea2af98717b9ebe55beb4c6b820b89e9c42d4fdfeca21cf63ed3ad4e38da" or asset["spriteSizeBytes"] != 1945586 or asset["atlasSize"] != "1536x1872" or asset["licenseMetadataPresent"] is not False or asset["licenseFilePresent"] is not False or not isinstance(installer, dict) or set(installer) != { "packageName", "version", "registryUrl", "tarballUrl", "tarballSha1", "tarballSha256", "npmIntegrity", "declaredLicense", "repositoryDeclared", "defaultApiBase", "installRoot", "writtenFiles", "assetDigestVerification", "assetSignatureVerification", "assetLicenseVerification", } or installer["packageName"] != "codex-pets" or installer["version"] != "0.3.0" or installer["tarballSha1"] != "82e41349ae63eb9e63099f2e06a56468182e2c90" or installer["tarballSha256"] != "9ec8bf1ea09e6d8fdc17b33a594a178a9b20bd3dc6decbb22973758394c9c1c7" or installer["npmIntegrity"] != "sha512-b7PjV0phEK7jn0rnyXzh3LMIsAdqSUf75mCdSxZEyIuFScCxwOeUZoDxnWj97rfg4ihk6XLaKvg6fgWD+CWcAQ==" or installer["declaredLicense"] != "MIT" or installer["repositoryDeclared"] is not False or installer["installRoot"] != "$CODEX_HOME/pets/{pet-id}" or installer["writtenFiles"] != ["pet.json", "spritesheet.webp"] or installer["assetDigestVerification"] is not False or installer["assetSignatureVerification"] is not False or installer["assetLicenseVerification"] is not False or not isinstance(service_source, dict) or set(service_source) != { "repository", "commit", "softwareLicense", "licenseSha256", "termsSourceSha256", "termsEffectiveDate", "uploadTermsScope", } or service_source["repository"] != "https://github.com/portons/codex-pet-share" or service_source["commit"] != "22725091da2787e8e525c9289cb7826a34be4950" or service_source["softwareLicense"] != "MIT" or service_source["licenseSha256"] != "13e779572adacb503b7e7a0c676571fcd86114a73f6aa000412c24a9a06a97d3" or service_source["termsSourceSha256"] != "70ad12414864566b8cd469a7d2ca39fe60050686cecacc126ff1aca587f790bb" or service_source["uploadTermsScope"] != "public-sharing-through-service" or not isinstance(commercial_decision, dict) or set(commercial_decision) != {"status", "code", "reason", "requiredEvidence"} or commercial_decision["status"] != "external-license-required" or commercial_decision["code"] != "guga_commercial_license_missing" or commercial_decision["requiredEvidence"] != [ "rights-holder-identity", "commercial-product-use", "customer-deployment-and-copying", "product-display", "territory-and-term", "asset-sha256-binding", "authorized-legal-review", ] ): raise SystemExit("guga upstream supply-chain audit is invalid") guga_audit_sha256 = hashlib.sha256(guga_audit_bytes).hexdigest() banned_guga_hashes = { asset["packageSha256"], asset["manifestSha256"], asset["spriteSha256"], } for path in stage.rglob("*"): if not path.is_file(): continue lower_name = path.name.lower() if lower_name == "spritesheet.webp" or lower_name.endswith(".codex-pet.zip"): raise SystemExit( f"external guga asset must not be bundled: {path.relative_to(stage)}" ) if path.stat().st_size <= 20 * 1024 * 1024: digest = hashlib.sha256(path.read_bytes()).hexdigest() if digest in banned_guga_hashes: raise SystemExit( f"external guga asset bytes must not be bundled: {path.relative_to(stage)}" ) minimum_pass_counts = { "commandKernel": 284, "desktopHost": 50, "petWebUi": 24, "astrbotPlugin": 96, "deploymentContracts": 76, "sqlServerCompatibility100": 16, } actual_pass_counts = { "commandKernel": kernel[0], "desktopHost": host[0], "petWebUi": int(node_match[-1]), "astrbotPlugin": int(python_match[-1]), "deploymentContracts": deployment[0], "sqlServerCompatibility100": sql_contract[0], } if kernel[1] != 0 or host[1] != 0 or deployment[1] != 0 or sql_contract[1] != 0: raise SystemExit("commercial verification contains failed tests") if ( "parserPackage=Microsoft.SqlServer.TransactSql.ScriptDom " "parserVersion=180.59.2 dialect=TSql100" ) not in sql_contract_text: raise SystemExit("commercial SQL Server compatibility parser metadata is invalid") for suite, minimum in minimum_pass_counts.items(): if actual_pass_counts[suite] < minimum: raise SystemExit( f"commercial verification test baseline regressed: {suite}" ) report = { "schemaVersion": "1.1", "packageVersion": os.environ["LSERP_PACKAGE_VERSION_VALUE"], "generatedAtUtc": datetime.now(timezone.utc).isoformat(), "sourceCommit": os.environ["LSERP_GIT_COMMIT_VALUE"], "sourceWorktreeDirty": os.environ["LSERP_WORKTREE_DIRTY_VALUE"] == "true", "deliveryTopology": { "desktopBundleContainsLserpCli": False, "desktopBundleContainsBridgeCli": True, "bridgeCliPath": "Host/lserp-agent-cli.exe", "bridgeCliDatabaseDirectAccess": False, "bridgeCliPublishMode": "win_x64_single_file_self_contained", "desktopBundleContainsLegacyErp": False, "legacyArtifactMode": "separate_signed_windows_build", "legacyBuildTool": "Deployment/Build-LegacyErpAcceptance.ps1", "runtimeRequiresLegacyArtifact": True, }, "automatedVerification": { "commandKernel": {"passed": actual_pass_counts["commandKernel"], "failed": kernel[1]}, "desktopHost": {"passed": actual_pass_counts["desktopHost"], "failed": host[1]}, "petWebUi": {"passed": actual_pass_counts["petWebUi"], "failed": 0}, "astrbotPlugin": { "passed": actual_pass_counts["astrbotPlugin"], "failed": 0, "skipped": python_skipped, }, "astrbotRuntimeContract": astrbot_contract, "miniMaxVision": { "mode": "direct_https_vlm", "allowedRegions": ["global", "cn"], "bundledCli": False, }, "gugaSupplyChainAudit": { "passed": True, "auditFile": "Deployment/guga-upstream-audit.v1.json", "auditSha256": guga_audit_sha256, "assetId": "guga", "installerPackage": "codex-pets", "installerVersion": "0.3.0", "installerTarballSha256": installer["tarballSha256"], "observedSpriteSha256": asset["spriteSha256"], "upstreamCommercialLicensePresent": False, "assetBundled": False, }, "deploymentContracts": { "passed": actual_pass_counts["deploymentContracts"], "failed": deployment[1], }, "sqlServerCompatibility100": { "passed": actual_pass_counts["sqlServerCompatibility100"], "failed": sql_contract[1], "parserPackage": "Microsoft.SqlServer.TransactSql.ScriptDom", "parserVersion": "180.59.2", "dialect": "TSql100", }, "legacyNet40ApiCompile": True, "winX64SelfContainedPublish": True, "bridgeCliWinX64SelfContainedPublish": True, "hostAuthenticode": { "signed": os.environ["LSERP_HOST_AUTHENTICODE_SIGNED_VALUE"] == "true", "certificateThumbprint": ( os.environ["LSERP_HOST_CERT_THUMBPRINT_VALUE"].upper() or None ), }, }, "releaseReadiness": False, "remainingHardGates": [ "clean reviewed source commit", "valid Windows Authenticode signature and signed installer", "written commercial license for the external guga artwork", "approved AstrBot AGPL-3.0-or-later and EULA compliance plan", "approved MiniMax API service terms, data processing, deployment region and billing plan", "customer Windows/.NET Framework 4/DevExpress 15.2 integration", "customer SQL Server transactional write, rollback, idempotency and audit evidence", "signed workflow acceptance manifests bound to final business-adapters.json", ], } (stage / "BUILD-VERIFICATION.json").write_text( json.dumps(report, ensure_ascii=False, indent=2) + "\n", encoding="utf-8" ) secret_patterns = [ re.compile("sk" + r"-cp-[A-Za-z0-9_-]{20,}"), re.compile(r"(?i)(?:api[_-]?key|password)\s*[=:]\s*['\"][A-Za-z0-9_-]{16,}['\"]"), ] for path in stage.rglob("*"): if not path.is_file() or path.stat().st_size > 2 * 1024 * 1024: continue if path.suffix.lower() not in {".json", ".yaml", ".yml", ".md", ".ps1", ".py", ".js", ".sql", ".txt"}: continue text = path.read_text(encoding="utf-8", errors="ignore") if any(pattern.search(text) for pattern in secret_patterns): raise SystemExit(f"possible secret in staged text file: {path.relative_to(stage)}") files = [] for path in sorted(stage.rglob("*"), key=lambda item: item.as_posix()): if not path.is_file() or path.name == "SHA256SUMS.json": continue digest = hashlib.sha256(path.read_bytes()).hexdigest() files.append({ "path": path.relative_to(stage).as_posix(), "sizeBytes": path.stat().st_size, "sha256": digest, }) manifest = { "schemaVersion": "1.0", "packageVersion": os.environ["LSERP_PACKAGE_VERSION_VALUE"], "generatedAtUtc": datetime.now(timezone.utc).isoformat(), "files": files, } (stage / "SHA256SUMS.json").write_text( json.dumps(manifest, ensure_ascii=False, indent=2) + "\n", encoding="utf-8" ) PY archive_base="$temporary_root/$package_name" "$python_bin" - "$stage" "$archive_base" <<'PY' import pathlib import shutil import sys stage = pathlib.Path(sys.argv[1]) archive_base = pathlib.Path(sys.argv[2]) shutil.make_archive(str(archive_base), "zip", root_dir=stage.parent, base_dir=stage.name) PY mv "$stage" "$final_directory" mv "$archive_base.zip" "$final_archive" "$python_bin" - "$final_archive" <<'PY' import hashlib import pathlib import sys path = pathlib.Path(sys.argv[1]) print(f"package={path}") print(f"sizeBytes={path.stat().st_size}") print(f"sha256={hashlib.sha256(path.read_bytes()).hexdigest()}") PY