This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
.git
|
||||
.gitea
|
||||
deploy
|
||||
scripts
|
||||
README.md
|
||||
AGENTS.md
|
||||
VERSION
|
||||
node_modules
|
||||
package-lock.json
|
||||
@@ -0,0 +1,154 @@
|
||||
name: Build and publish static assets
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
tags: ["v*"]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: large
|
||||
if: ${{ !startsWith(github.ref, 'refs/heads/') || !contains(github.event.head_commit.message, '[auto-version-bump]') }}
|
||||
steps:
|
||||
- name: Check out repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.GITEA_TOKEN }}
|
||||
persist-credentials: true
|
||||
|
||||
- name: Check source and release metadata
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
python3 scripts/bump-version.py --check
|
||||
scripts/check-static.sh
|
||||
|
||||
- name: Fetch and verify pinned static assets
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
npm install --no-audit --no-fund
|
||||
npm run build:assets
|
||||
test -s public/static/piper-wasm-v1/piper/piper_phonemize.wasm
|
||||
test -s public/static/piper-wasm-v1/piper/piper_phonemize.data
|
||||
test -s public/static/piper-wasm-v1/onnx/ort-wasm-simd-threaded.mjs
|
||||
test -s public/static/piper-wasm-v1/onnx/ort-wasm-simd-threaded.wasm
|
||||
test -s public/static/piper-wasm-v1/voices/sv/sv_SE/alma/medium/sv_SE-alma-medium.onnx
|
||||
test -s public/static/piper-wasm-v1/voices/sv/sv_SE/lisa/medium/sv_SE-lisa-medium.onnx
|
||||
test -s public/static/piper-wasm-v1/voices/sv/sv_SE/nst/medium/sv_SE-nst-medium.onnx
|
||||
test -s public/static/piper-wasm-v1/voices/ATTRIBUTION.txt
|
||||
|
||||
- name: Ensure Helm is available
|
||||
shell: bash
|
||||
env:
|
||||
HELM_VERSION: v3.17.3
|
||||
run: |
|
||||
set -euo pipefail
|
||||
if command -v helm >/dev/null 2>&1; then helm version --short; exit 0; fi
|
||||
mkdir -p "$HOME/.local/bin" "$RUNNER_TEMP/helm"
|
||||
archive="$RUNNER_TEMP/helm/helm-${HELM_VERSION}-linux-amd64.tar.gz"
|
||||
curl -fsSLo "$archive" "https://get.helm.sh/helm-${HELM_VERSION}-linux-amd64.tar.gz"
|
||||
curl -fsSLo "$archive.sha256sum" "https://get.helm.sh/helm-${HELM_VERSION}-linux-amd64.tar.gz.sha256sum"
|
||||
(cd "$RUNNER_TEMP/helm" && sha256sum -c "$(basename "$archive").sha256sum")
|
||||
tar -xzf "$archive" -C "$RUNNER_TEMP/helm"
|
||||
install "$RUNNER_TEMP/helm/linux-amd64/helm" "$HOME/.local/bin/helm"
|
||||
echo "$HOME/.local/bin" >> "$GITHUB_PATH"
|
||||
|
||||
- name: Lint and render Helm chart
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
helm lint deploy/helm/ai-spel-static
|
||||
helm template ai-spel-static deploy/helm/ai-spel-static --namespace ai-spel > "$RUNNER_TEMP/static-manifest.yaml"
|
||||
grep -q 'kind: Deployment' "$RUNNER_TEMP/static-manifest.yaml"
|
||||
grep -q 'kind: Service' "$RUNNER_TEMP/static-manifest.yaml"
|
||||
grep -q 'path: /static' "$RUNNER_TEMP/static-manifest.yaml"
|
||||
|
||||
- name: Build and push commit image
|
||||
if: startsWith(github.ref, 'refs/heads/')
|
||||
shell: bash
|
||||
env:
|
||||
HARBOR_USERNAME: ${{ secrets.HARBOR_USERNAME }}
|
||||
HARBOR_PASSWORD: ${{ secrets.HARBOR_PASSWORD }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
printf '%s' "$HARBOR_PASSWORD" | docker login harbor.k3s.k0.nu --username "$HARBOR_USERNAME" --password-stdin
|
||||
unset HARBOR_PASSWORD
|
||||
version="$(cat VERSION)"
|
||||
tag="harbor.k3s.k0.nu/ai-spel/static:edge-${GITHUB_SHA:0:12}"
|
||||
docker build --platform linux/amd64 --pull --build-arg "STATIC_VERSION=$version" --tag "$tag" .
|
||||
docker image push "$tag"
|
||||
|
||||
- name: Bump version and tag release commit
|
||||
id: bump
|
||||
if: startsWith(github.ref, 'refs/heads/')
|
||||
shell: bash
|
||||
run: |
|
||||
set -euo pipefail
|
||||
git fetch origin main --quiet
|
||||
test "$(git rev-parse origin/main)" = "$GITHUB_SHA"
|
||||
version="$(python3 scripts/bump-version.py)"
|
||||
git config user.name "Gitea Actions"
|
||||
git config user.email "actions@users.noreply.gitea"
|
||||
git add VERSION deploy/helm/ai-spel-static/Chart.yaml package-lock.json
|
||||
git commit -m "Release $version [auto-version-bump]"
|
||||
git tag -a "v$version" -m "v$version"
|
||||
git push origin HEAD:main "refs/tags/v$version"
|
||||
echo "version=$version" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Publish versioned image, chart, and manifest
|
||||
if: ${{ startsWith(github.ref, 'refs/tags/') || steps.bump.outputs.version != '' }}
|
||||
shell: bash
|
||||
env:
|
||||
AUTO_RELEASE_VERSION: ${{ steps.bump.outputs.version }}
|
||||
HARBOR_USERNAME: ${{ secrets.HARBOR_USERNAME }}
|
||||
HARBOR_PASSWORD: ${{ secrets.HARBOR_PASSWORD }}
|
||||
ORAS_VERSION: 1.2.3
|
||||
run: |
|
||||
set -euo pipefail
|
||||
version="$(cat VERSION)"
|
||||
if [[ "$GITHUB_REF" == refs/tags/* ]]; then test "$GITHUB_REF_NAME" = "v$version"; else test "$AUTO_RELEASE_VERSION" = "$version"; fi
|
||||
python3 scripts/bump-version.py --check
|
||||
printf '%s' "$HARBOR_PASSWORD" | docker login harbor.k3s.k0.nu --username "$HARBOR_USERNAME" --password-stdin
|
||||
unset HARBOR_PASSWORD
|
||||
image="harbor.k3s.k0.nu/ai-spel/static:$version"
|
||||
docker build --platform linux/amd64 --pull --build-arg "STATIC_VERSION=$version" --tag "$image" .
|
||||
docker image push "$image"
|
||||
publish_dir="$(mktemp -d)"
|
||||
trap 'rm -rf "$publish_dir"' EXIT
|
||||
helm lint deploy/helm/ai-spel-static
|
||||
helm package deploy/helm/ai-spel-static --destination "$publish_dir" --version "$version" --app-version "$version"
|
||||
registry_config="$RUNNER_TEMP/helm/registry.json"
|
||||
mkdir -p "$(dirname "$registry_config")"
|
||||
printf '{}' > "$registry_config"
|
||||
printf '%s' "$HARBOR_PASSWORD" | helm registry login harbor.k3s.k0.nu --username "$HARBOR_USERNAME" --password-stdin --registry-config "$registry_config"
|
||||
helm push "$publish_dir/ai-spel-static-$version.tgz" oci://harbor.k3s.k0.nu/ai-spel --registry-config "$registry_config"
|
||||
chart_metadata="$(helm show chart oci://harbor.k3s.k0.nu/ai-spel/ai-spel-static --version "$version" --registry-config "$registry_config")"
|
||||
test "$(printf '%s\n' "$chart_metadata" | awk '$1 == "version:" { print $2 }')" = "$version"
|
||||
helm template ai-spel-static "$publish_dir/ai-spel-static-$version.tgz" --namespace ai-spel > "$publish_dir/static-manifest.yaml"
|
||||
mkdir -p "$RUNNER_TEMP/oras"
|
||||
archive="$RUNNER_TEMP/oras/oras_${ORAS_VERSION}_linux_amd64.tar.gz"
|
||||
curl -fsSLo "$archive" "https://github.com/oras-project/oras/releases/download/v${ORAS_VERSION}/oras_${ORAS_VERSION}_linux_amd64.tar.gz"
|
||||
echo 'b4efc97a91f471f323f193ea4b4d63d8ff443ca3aab514151a30751330852827 '"$archive" | sha256sum -c -
|
||||
tar -xzf "$archive" -C "$RUNNER_TEMP/oras" oras
|
||||
printf '%s' "$HARBOR_PASSWORD" | "$RUNNER_TEMP/oras/oras" login harbor.k3s.k0.nu --username "$HARBOR_USERNAME" --password-stdin
|
||||
unset HARBOR_PASSWORD
|
||||
(cd "$publish_dir" && "$RUNNER_TEMP/oras/oras" push --artifact-type application/vnd.ai-spel.static-manifest.v1 "harbor.k3s.k0.nu/ai-spel/static-manifest:$version" "static-manifest.yaml:application/yaml")
|
||||
|
||||
ready=false
|
||||
for attempt in $(seq 1 120); do
|
||||
deployed="$(curl -fsS https://ai-spel.k3s.k0.nu/static/version.txt 2>/dev/null || true)"
|
||||
if [[ "$deployed" == "$version" ]]; then ready=true; break; fi
|
||||
sleep 5
|
||||
done
|
||||
[[ "$ready" == true ]] || { echo "Static service did not report version $version"; exit 1; }
|
||||
for path in \
|
||||
/static/piper-wasm-v1/onnx/ort-wasm-simd-threaded.mjs \
|
||||
/static/piper-wasm-v1/onnx/ort-wasm-simd-threaded.wasm \
|
||||
/static/piper-wasm-v1/piper/piper_phonemize.data \
|
||||
/static/piper-wasm-v1/voices/sv/sv_SE/nst/medium/sv_SE-nst-medium.onnx; do
|
||||
curl -fsSI "https://ai-spel.k3s.k0.nu$path" >/dev/null
|
||||
done
|
||||
@@ -0,0 +1,2 @@
|
||||
public/static/
|
||||
node_modules/
|
||||
@@ -0,0 +1,10 @@
|
||||
# AI-spel static asset service
|
||||
|
||||
- Own only public, version-pinned Piper and ONNX speech assets.
|
||||
- Serve under `/static/` on `https://ai-spel.k3s.k0.nu`.
|
||||
- `VERSION` is the only release version source. Use `scripts/bump-version.py`.
|
||||
- Gitea Actions on the existing `large` runner verifies, builds, and publishes. Never run tests, builds, containers, images, or binaries locally; do not create runners.
|
||||
- Git release commit/tag, image, Helm chart, and manifest use the same version.
|
||||
- Use Harbor project `ai-spel`, Argo CD project `ai-spel`, and Kubernetes namespace `ai-spel`.
|
||||
- `/static/piper-wasm-v1/` is immutable and cached for one year. Incompatible asset updates require a new path and coordinated frontend change.
|
||||
- No secrets or runtime network access.
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
FROM nginxinc/nginx-unprivileged:1.27-alpine
|
||||
|
||||
ARG STATIC_VERSION
|
||||
RUN test -n "$STATIC_VERSION"
|
||||
COPY nginx/default.conf /etc/nginx/conf.d/default.conf
|
||||
COPY public/static/ /usr/share/nginx/html/static/
|
||||
USER root
|
||||
RUN printf '%s\n' "$STATIC_VERSION" > /usr/share/nginx/html/static/version.txt && \
|
||||
chmod -R a+rX /usr/share/nginx/html/static
|
||||
USER 101
|
||||
EXPOSE 8080
|
||||
@@ -0,0 +1,26 @@
|
||||
Swedish Piper voice models
|
||||
==========================
|
||||
|
||||
This service distributes the Swedish medium Piper models Alma, Lisa, and NST
|
||||
from the rhasspy/piper-voices repository, pinned to revision
|
||||
c10ece1aade47bb51c153c893d14e5bf8e5b7117. Model weights are published under
|
||||
/static/piper-wasm-v1/voices/ after integrity verification in CI.
|
||||
|
||||
Alma (sv_SE-alma-medium)
|
||||
Source: https://huggingface.co/rhasspy/piper-voices/tree/c10ece1aade47bb51c153c893d14e5bf8e5b7117/sv/sv_SE/alma/medium
|
||||
The model card states CC BY 4.0 and identifies the upstream voice project at
|
||||
https://huggingface.co/yeagersthlm/piper-voice-sv-alma.
|
||||
|
||||
Lisa (sv_SE-lisa-medium)
|
||||
Source: https://huggingface.co/rhasspy/piper-voices/tree/c10ece1aade47bb51c153c893d14e5bf8e5b7117/sv/sv_SE/lisa/medium
|
||||
The upstream model card describes this as fine-tuned from the Norwegian
|
||||
talesyntese medium voice.
|
||||
|
||||
NST (sv_SE-nst-medium)
|
||||
Source: https://huggingface.co/rhasspy/piper-voices/tree/c10ece1aade47bb51c153c893d14e5bf8e5b7117/sv/sv_SE/nst/medium
|
||||
The upstream model card identifies the NST Swedish TTS dataset as CC0 and
|
||||
credits KBLab at the National Library of Sweden for training the voice.
|
||||
|
||||
The upstream model files and model cards remain the source of truth for license
|
||||
terms. Exact source revision and per-file integrity checks are in
|
||||
scripts/assets.mjs.
|
||||
@@ -0,0 +1,30 @@
|
||||
# AI-spel static assets
|
||||
|
||||
This repository publishes Piper/ONNX runtime assets and Swedish voice models through a dedicated Nginx service. The game keeps the same origin: ingress routes `/static/` here, `/` to the frontend, and `/api` to Rust.
|
||||
|
||||
The service contains only public, version-pinned files. Gitea Actions on the existing `large` runner downloads and verifies model files, runs checks, builds a `linux/amd64` image, bumps the patch version, and publishes the image, Helm chart, and rendered manifest to Harbor. Argo CD tracks the latest chart in the existing `ai-spel` project and namespace.
|
||||
|
||||
## Asset URLs and caching
|
||||
|
||||
Assets under `/static/piper-wasm-v1/` are served with `Cache-Control: public, max-age=31536000, immutable`. Keep this path and its contents stable. To change assets, publish under a new asset path and update the frontend to reference it after the new static release is healthy.
|
||||
|
||||
- `/static/piper-wasm-v1/voices/` — Alma, Lisa, and NST Swedish voices, plus attribution.
|
||||
- `/static/piper-wasm-v1/onnx/` — ONNX Runtime WASM and ES module loader.
|
||||
- `/static/piper-wasm-v1/piper/` — Piper phonemizer WASM and data.
|
||||
- `/static/version.txt` — active deployment version, without long-lived caching.
|
||||
|
||||
The frontend worker remains bundled with the frontend; it fetches these large assets from same-origin static URLs. The browser does not request models from an external host.
|
||||
|
||||
## Argo CD bootstrap
|
||||
|
||||
After the first chart has been published, bootstrap the app once from `deploy/argocd/application.yaml`:
|
||||
|
||||
```sh
|
||||
kubectl -n argocd apply -f deploy/argocd/application.yaml
|
||||
```
|
||||
|
||||
It tracks the newest chart (`*`), uses automated sync/self-heal without pruning, and deploys to `ai-spel`. Later releases do not need manual Argo actions.
|
||||
|
||||
## Local policy
|
||||
|
||||
Do not build or run locally. CI runs static checks, tests, image build, and Helm lint/render on the existing runner; runtime verification happens on Kubernetes.
|
||||
@@ -0,0 +1,22 @@
|
||||
apiVersion: argoproj.io/v1alpha1
|
||||
kind: Application
|
||||
metadata:
|
||||
name: ai-spel-static
|
||||
namespace: argocd
|
||||
spec:
|
||||
project: ai-spel
|
||||
source:
|
||||
repoURL: harbor.k3s.k0.nu/ai-spel
|
||||
chart: ai-spel-static
|
||||
targetRevision: "*"
|
||||
helm:
|
||||
releaseName: ai-spel-static
|
||||
destination:
|
||||
server: https://kubernetes.default.svc
|
||||
namespace: ai-spel
|
||||
syncPolicy:
|
||||
automated:
|
||||
prune: false
|
||||
selfHeal: true
|
||||
syncOptions:
|
||||
- CreateNamespace=false
|
||||
@@ -0,0 +1,6 @@
|
||||
apiVersion: v2
|
||||
name: ai-spel-static
|
||||
description: Long-lived static Piper assets for the Swedish life simulator
|
||||
type: application
|
||||
version: 0.1.0
|
||||
appVersion: "0.1.0"
|
||||
@@ -0,0 +1,64 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: {{ .Release.Name }}
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ .Chart.Name }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
spec:
|
||||
replicas: {{ .Values.replicaCount }}
|
||||
strategy:
|
||||
type: RollingUpdate
|
||||
rollingUpdate:
|
||||
maxUnavailable: {{ .Values.deployment.maxUnavailable }}
|
||||
maxSurge: {{ .Values.deployment.maxSurge }}
|
||||
minReadySeconds: {{ .Values.deployment.minReadySeconds }}
|
||||
progressDeadlineSeconds: {{ .Values.deployment.progressDeadlineSeconds }}
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ .Chart.Name }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
spec:
|
||||
imagePullSecrets:
|
||||
- name: {{ .Values.image.pullSecretName | quote }}
|
||||
containers:
|
||||
- name: static
|
||||
image: "{{ .Values.image.repository }}:{{ .Chart.AppVersion }}"
|
||||
imagePullPolicy: {{ .Values.image.pullPolicy }}
|
||||
ports:
|
||||
- name: http
|
||||
containerPort: {{ .Values.service.targetPort }}
|
||||
readinessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: http
|
||||
initialDelaySeconds: 2
|
||||
periodSeconds: 5
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
path: /healthz
|
||||
port: http
|
||||
initialDelaySeconds: 10
|
||||
periodSeconds: 10
|
||||
resources:
|
||||
{{- toYaml .Values.resources | nindent 12 }}
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
capabilities:
|
||||
drop: [ALL]
|
||||
readOnlyRootFilesystem: true
|
||||
runAsNonRoot: true
|
||||
volumeMounts:
|
||||
- name: nginx-cache
|
||||
mountPath: /var/cache/nginx
|
||||
- name: nginx-run
|
||||
mountPath: /var/run
|
||||
volumes:
|
||||
- name: nginx-cache
|
||||
emptyDir: {}
|
||||
- name: nginx-run
|
||||
emptyDir: {}
|
||||
@@ -0,0 +1,24 @@
|
||||
{{- if .Values.ingress.enabled }}
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: Ingress
|
||||
metadata:
|
||||
name: {{ .Release.Name }}
|
||||
annotations:
|
||||
spec:
|
||||
ingressClassName: {{ .Values.ingress.className | quote }}
|
||||
tls:
|
||||
- hosts:
|
||||
- {{ .Values.ingress.host | quote }}
|
||||
secretName: {{ .Values.ingress.tlsSecret | quote }}
|
||||
rules:
|
||||
- host: {{ .Values.ingress.host | quote }}
|
||||
http:
|
||||
paths:
|
||||
- path: /static
|
||||
pathType: Prefix
|
||||
backend:
|
||||
service:
|
||||
name: {{ .Release.Name }}
|
||||
port:
|
||||
number: {{ .Values.service.port }}
|
||||
{{- end }}
|
||||
@@ -0,0 +1,16 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: {{ .Release.Name }}
|
||||
labels:
|
||||
app.kubernetes.io/name: {{ .Chart.Name }}
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app.kubernetes.io/instance: {{ .Release.Name }}
|
||||
ports:
|
||||
- name: http
|
||||
port: {{ .Values.service.port }}
|
||||
targetPort: http
|
||||
protocol: TCP
|
||||
@@ -0,0 +1,25 @@
|
||||
replicaCount: 1
|
||||
deployment:
|
||||
maxUnavailable: 0
|
||||
maxSurge: 1
|
||||
minReadySeconds: 5
|
||||
progressDeadlineSeconds: 600
|
||||
image:
|
||||
repository: harbor.k3s.k0.nu/ai-spel/static
|
||||
pullPolicy: IfNotPresent
|
||||
pullSecretName: harbor-pull
|
||||
service:
|
||||
port: 80
|
||||
targetPort: 8080
|
||||
ingress:
|
||||
enabled: true
|
||||
className: nginx
|
||||
host: ai-spel.k3s.k0.nu
|
||||
tlsSecret: ai-spel-tls
|
||||
resources:
|
||||
requests:
|
||||
cpu: 5m
|
||||
memory: 16Mi
|
||||
limits:
|
||||
cpu: 100m
|
||||
memory: 64Mi
|
||||
@@ -0,0 +1,33 @@
|
||||
server {
|
||||
listen 8080;
|
||||
server_name _;
|
||||
root /usr/share/nginx/html;
|
||||
|
||||
location = /healthz {
|
||||
access_log off;
|
||||
return 204;
|
||||
}
|
||||
|
||||
location = /static/version.txt {
|
||||
default_type text/plain;
|
||||
add_header Cache-Control "no-store, no-cache, must-revalidate, max-age=0" always;
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location ^~ /static/ {
|
||||
types {
|
||||
application/wasm wasm;
|
||||
application/javascript js mjs;
|
||||
application/json json;
|
||||
application/octet-stream onnx data;
|
||||
text/plain txt;
|
||||
}
|
||||
add_header Cache-Control "public, max-age=31536000, immutable" always;
|
||||
add_header Cross-Origin-Resource-Policy "same-origin" always;
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
location / {
|
||||
return 404;
|
||||
}
|
||||
}
|
||||
Generated
+610
@@ -0,0 +1,610 @@
|
||||
{
|
||||
"name": "ai-spel-static-assets",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "ai-spel-static-assets",
|
||||
"dependencies": {
|
||||
"@diffusionstudio/piper-wasm": "1.0.0",
|
||||
"@mintplex-labs/piper-tts-web": "1.0.3",
|
||||
"esbuild": "0.25.5",
|
||||
"onnxruntime-web": "1.20.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@diffusionstudio/piper-wasm": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/@diffusionstudio/piper-wasm/-/piper-wasm-1.0.0.tgz",
|
||||
"integrity": "sha512-4Hgh7wJ1PuGEVgVi1CpducESnyXwjFRJN4zXjYRpxgquM+Zh6Eqq8ZaiGpCKm4yXvT1Y2SPQXbv0dEckFD9NaQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@esbuild/aix-ppc64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz",
|
||||
"integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"aix"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz",
|
||||
"integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/android-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"android"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/darwin-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/freebsd-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"freebsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz",
|
||||
"integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==",
|
||||
"cpu": [
|
||||
"arm"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ia32": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz",
|
||||
"integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-loong64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz",
|
||||
"integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==",
|
||||
"cpu": [
|
||||
"loong64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-mips64el": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz",
|
||||
"integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==",
|
||||
"cpu": [
|
||||
"mips64el"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-ppc64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz",
|
||||
"integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==",
|
||||
"cpu": [
|
||||
"ppc64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-riscv64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz",
|
||||
"integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==",
|
||||
"cpu": [
|
||||
"riscv64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-s390x": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz",
|
||||
"integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==",
|
||||
"cpu": [
|
||||
"s390x"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/linux-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/netbsd-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"netbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/openbsd-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"openbsd"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/sunos-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"sunos"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-arm64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz",
|
||||
"integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==",
|
||||
"cpu": [
|
||||
"arm64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-ia32": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz",
|
||||
"integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==",
|
||||
"cpu": [
|
||||
"ia32"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@esbuild/win32-x64": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz",
|
||||
"integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==",
|
||||
"cpu": [
|
||||
"x64"
|
||||
],
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/@mintplex-labs/piper-tts-web": {
|
||||
"version": "1.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@mintplex-labs/piper-tts-web/-/piper-tts-web-1.0.3.tgz",
|
||||
"integrity": "sha512-vjYiEgigm0JHt9yKxj0euQd2eHIhtbGgWyeySLTPG+CdCH9RMf10WtczpeslboUYAvX5Eaxh6RMK+IuHU2rNhw==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"onnxruntime-web": "^1.18.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@protobufjs/aspromise": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
||||
"integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/base64": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
|
||||
"integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/codegen": {
|
||||
"version": "2.0.5",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.5.tgz",
|
||||
"integrity": "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/eventemitter": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.1.tgz",
|
||||
"integrity": "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/fetch": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.1.tgz",
|
||||
"integrity": "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw==",
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@protobufjs/aspromise": "^1.1.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@protobufjs/float": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
|
||||
"integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/path": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
|
||||
"integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/pool": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
|
||||
"integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@protobufjs/utf8": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.2.tgz",
|
||||
"integrity": "sha512-b1UQwcEZ4yCnMCD8DAL1VlbvBJE9/IX4FTIp7BG1xYpf29SLazLSrqUkj4w7Y5y7cCVP6E5tcqqcI0xemPkHug==",
|
||||
"license": "BSD-3-Clause"
|
||||
},
|
||||
"node_modules/@types/node": {
|
||||
"version": "26.6.5",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-26.6.5.tgz",
|
||||
"integrity": "sha512-uMzsv9stDdofIZBx4UGh4Z9ZgXnGC9seRDscilBcTHAeOFk67MZg/k9bWVfO+bk4qZmG+l52hRnLwUiH2LnJTg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"undici-types": "~8.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/esbuild": {
|
||||
"version": "0.25.5",
|
||||
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz",
|
||||
"integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"bin": {
|
||||
"esbuild": "bin/esbuild"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"@esbuild/aix-ppc64": "0.25.5",
|
||||
"@esbuild/android-arm": "0.25.5",
|
||||
"@esbuild/android-arm64": "0.25.5",
|
||||
"@esbuild/android-x64": "0.25.5",
|
||||
"@esbuild/darwin-arm64": "0.25.5",
|
||||
"@esbuild/darwin-x64": "0.25.5",
|
||||
"@esbuild/freebsd-arm64": "0.25.5",
|
||||
"@esbuild/freebsd-x64": "0.25.5",
|
||||
"@esbuild/linux-arm": "0.25.5",
|
||||
"@esbuild/linux-arm64": "0.25.5",
|
||||
"@esbuild/linux-ia32": "0.25.5",
|
||||
"@esbuild/linux-loong64": "0.25.5",
|
||||
"@esbuild/linux-mips64el": "0.25.5",
|
||||
"@esbuild/linux-ppc64": "0.25.5",
|
||||
"@esbuild/linux-riscv64": "0.25.5",
|
||||
"@esbuild/linux-s390x": "0.25.5",
|
||||
"@esbuild/linux-x64": "0.25.5",
|
||||
"@esbuild/netbsd-arm64": "0.25.5",
|
||||
"@esbuild/netbsd-x64": "0.25.5",
|
||||
"@esbuild/openbsd-arm64": "0.25.5",
|
||||
"@esbuild/openbsd-x64": "0.25.5",
|
||||
"@esbuild/sunos-x64": "0.25.5",
|
||||
"@esbuild/win32-arm64": "0.25.5",
|
||||
"@esbuild/win32-ia32": "0.25.5",
|
||||
"@esbuild/win32-x64": "0.25.5"
|
||||
}
|
||||
},
|
||||
"node_modules/flatbuffers": {
|
||||
"version": "1.12.0",
|
||||
"resolved": "https://registry.npmjs.org/flatbuffers/-/flatbuffers-1.12.0.tgz",
|
||||
"integrity": "sha512-c7CZADjRcl6j0PlvFy0ZqXQ67qSEZfrVPynmnL+2zPc+NtMvrF8Y0QceMo7QqnSPc7+uWjUIAbvCQ5WIKlMVdQ==",
|
||||
"license": "SEE LICENSE IN LICENSE.txt"
|
||||
},
|
||||
"node_modules/guid-typescript": {
|
||||
"version": "1.0.9",
|
||||
"resolved": "https://registry.npmjs.org/guid-typescript/-/guid-typescript-1.0.9.tgz",
|
||||
"integrity": "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ==",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/long": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
|
||||
"integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==",
|
||||
"license": "Apache-2.0"
|
||||
},
|
||||
"node_modules/onnxruntime-common": {
|
||||
"version": "1.20.1",
|
||||
"resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.20.1.tgz",
|
||||
"integrity": "sha512-YiU0s0IzYYC+gWvqD1HzLc46Du1sXpSiwzKb63PACIJr6LfL27VsXSXQvt68EzD3V0D5Bc0vyJTjmMxp0ylQiw==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/onnxruntime-web": {
|
||||
"version": "1.20.1",
|
||||
"resolved": "https://registry.npmjs.org/onnxruntime-web/-/onnxruntime-web-1.20.1.tgz",
|
||||
"integrity": "sha512-TePF6XVpLL1rWVMIl5Y9ACBQcyCNFThZON/jgElNd9Txb73CIEGlklhYR3UEr1cp5r0rbGI6nDwwrs79g7WjoA==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"flatbuffers": "^1.12.0",
|
||||
"guid-typescript": "^1.0.9",
|
||||
"long": "^5.2.3",
|
||||
"onnxruntime-common": "1.20.1",
|
||||
"platform": "^1.3.6",
|
||||
"protobufjs": "^7.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/platform": {
|
||||
"version": "1.3.6",
|
||||
"resolved": "https://registry.npmjs.org/platform/-/platform-1.3.6.tgz",
|
||||
"integrity": "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/protobufjs": {
|
||||
"version": "7.6.6",
|
||||
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.6.6.tgz",
|
||||
"integrity": "sha512-dYDWdjSl5RNb7SgPxGQcRU+GtvP7s2fpkrY0r432PcOIaZ0/rBcxEZnQN67iJhFuQiVw754JDoPruPCNdGsbjg==",
|
||||
"hasInstallScript": true,
|
||||
"license": "BSD-3-Clause",
|
||||
"dependencies": {
|
||||
"@protobufjs/aspromise": "^1.1.2",
|
||||
"@protobufjs/base64": "^1.1.2",
|
||||
"@protobufjs/codegen": "^2.0.5",
|
||||
"@protobufjs/eventemitter": "^1.1.1",
|
||||
"@protobufjs/fetch": "^1.1.1",
|
||||
"@protobufjs/float": "^1.0.2",
|
||||
"@protobufjs/path": "^1.1.2",
|
||||
"@protobufjs/pool": "^1.1.0",
|
||||
"@protobufjs/utf8": "^1.1.1",
|
||||
"@types/node": ">=13.7.0",
|
||||
"long": "^5.3.2"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=12.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/undici-types": {
|
||||
"version": "8.9.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.9.0.tgz",
|
||||
"integrity": "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg==",
|
||||
"license": "MIT"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "ai-spel-static-assets",
|
||||
"private": true,
|
||||
"description": "Pinned Piper and ONNX static assets for AI-spel",
|
||||
"scripts": {"build:assets": "node scripts/build-assets.mjs"},
|
||||
"dependencies": {
|
||||
"@diffusionstudio/piper-wasm": "1.0.0",
|
||||
"@mintplex-labs/piper-tts-web": "1.0.3",
|
||||
"esbuild": "0.25.5",
|
||||
"onnxruntime-web": "1.20.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
export const assetPrefix = 'piper-wasm-v1';
|
||||
export const voiceRevision = 'c10ece1aade47bb51c153c893d14e5bf8e5b7117';
|
||||
export const voiceRepository = 'https://huggingface.co/rhasspy/piper-voices/resolve';
|
||||
|
||||
export const voiceFiles = [
|
||||
{path: 'sv/sv_SE/alma/medium/sv_SE-alma-medium.onnx', size: 63434611, sha256: 'f75455eb6afeb1db9e2905d5bd7e4115afcd3710e0d399b6a40981766d32fedc'},
|
||||
{path: 'sv/sv_SE/alma/medium/sv_SE-alma-medium.onnx.json', size: 6059, gitBlobSha1: '7eaf3ef20af4a74e9cee9d29b98de3d9fcec0bdc'},
|
||||
{path: 'sv/sv_SE/lisa/medium/sv_SE-lisa-medium.onnx', size: 63511038, sha256: '94cae912b31d6e9140d3f5160f1815951588600c7a9e43d539ba1e81a110d131'},
|
||||
{path: 'sv/sv_SE/lisa/medium/sv_SE-lisa-medium.onnx.json', size: 7239, gitBlobSha1: 'b2b5fb0f8af2aae4ddfb81eaa7709c6b8faa412b'},
|
||||
{path: 'sv/sv_SE/nst/medium/sv_SE-nst-medium.onnx', size: 63104526, sha256: 'df011f56825a59dd1efc080c38a65a1ef70407e60f63050e9246f43a3d7e471e'},
|
||||
{path: 'sv/sv_SE/nst/medium/sv_SE-nst-medium.onnx.json', size: 4157, gitBlobSha1: '9766b9769436046a610a505fda925032e12a66b3'},
|
||||
];
|
||||
|
||||
export const requiredOnnxFiles = [
|
||||
'ort-wasm-simd-threaded.wasm',
|
||||
'ort-wasm-simd-threaded.mjs',
|
||||
];
|
||||
@@ -0,0 +1,67 @@
|
||||
import {createHash} from 'node:crypto';
|
||||
import {createWriteStream} from 'node:fs';
|
||||
import {cp, mkdir, readdir, rm, stat, rename, unlink} from 'node:fs/promises';
|
||||
import {join} from 'node:path';
|
||||
import {Readable, Transform} from 'node:stream';
|
||||
import {pipeline} from 'node:stream/promises';
|
||||
import {assetPrefix, requiredOnnxFiles, voiceFiles, voiceRepository, voiceRevision} from './assets.mjs';
|
||||
|
||||
const root = process.cwd();
|
||||
const output = join(root, 'public', 'static', assetPrefix);
|
||||
const onnxSource = join(root, 'node_modules', 'onnxruntime-web', 'dist');
|
||||
const piperSource = join(root, 'node_modules', '@diffusionstudio', 'piper-wasm', 'build');
|
||||
|
||||
async function downloadVoiceFile(file) {
|
||||
const destination = join(output, 'voices', file.path);
|
||||
const temporary = `${destination}.part`;
|
||||
await mkdir(join(destination, '..'), {recursive: true});
|
||||
const response = await fetch(`${voiceRepository}/${voiceRevision}/${file.path}`);
|
||||
if (!response.ok || !response.body) throw new Error(`Unable to download ${file.path}: HTTP ${response.status}`);
|
||||
const sha256 = createHash('sha256');
|
||||
const sha1 = createHash('sha1').update(Buffer.from(`blob ${file.size}\0`));
|
||||
let size = 0;
|
||||
const verify = new Transform({
|
||||
transform(chunk, _encoding, callback) {
|
||||
size += chunk.length;
|
||||
sha256.update(chunk);
|
||||
sha1.update(chunk);
|
||||
callback(null, chunk);
|
||||
},
|
||||
});
|
||||
try {
|
||||
await pipeline(Readable.fromWeb(response.body), verify, createWriteStream(temporary));
|
||||
if (size !== file.size || (file.sha256 && sha256.digest('hex') !== file.sha256) ||
|
||||
(file.gitBlobSha1 && sha1.digest('hex') !== file.gitBlobSha1)) {
|
||||
throw new Error(`Integrity check failed for ${file.path} (${size} bytes)`);
|
||||
}
|
||||
await rename(temporary, destination);
|
||||
} catch (error) {
|
||||
await unlink(temporary).catch(() => {});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
await rm(join(root, 'public', 'static'), {recursive: true, force: true});
|
||||
await mkdir(join(output, 'onnx'), {recursive: true});
|
||||
await mkdir(join(output, 'piper'), {recursive: true});
|
||||
for (const file of voiceFiles) await downloadVoiceFile(file);
|
||||
await cp(join(root, 'PIPER_VOICE_NOTICES.txt'), join(output, 'voices', 'ATTRIBUTION.txt'));
|
||||
|
||||
const onnxFiles = (await readdir(onnxSource)).filter(name => name.endsWith('.wasm') || name.endsWith('.mjs'));
|
||||
for (const file of requiredOnnxFiles) {
|
||||
if (!onnxFiles.includes(file)) throw new Error(`Missing ONNX Runtime asset: ${file}`);
|
||||
}
|
||||
for (const file of onnxFiles) await cp(join(onnxSource, file), join(output, 'onnx', file));
|
||||
|
||||
for (const file of ['piper_phonemize.wasm', 'piper_phonemize.data']) {
|
||||
const source = join(piperSource, file);
|
||||
const fileStat = await stat(source).catch(() => null);
|
||||
if (!fileStat?.isFile() || fileStat.size === 0) throw new Error(`Missing Piper runtime file: ${file}`);
|
||||
await cp(source, join(output, 'piper', file));
|
||||
}
|
||||
|
||||
for (const file of voiceFiles) {
|
||||
const downloaded = join(output, 'voices', file.path);
|
||||
if ((await stat(downloaded)).size !== file.size) throw new Error(`Missing verified voice file: ${file.path}`);
|
||||
}
|
||||
console.log(`Static Piper assets prepared at ${output}`);
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Update and validate the repository's single release version source."""
|
||||
|
||||
import argparse
|
||||
from pathlib import Path
|
||||
import re
|
||||
|
||||
VERSION_FILE = Path("VERSION")
|
||||
CHART_FILE = Path("deploy/helm/ai-spel-static/Chart.yaml")
|
||||
SEMVER = re.compile(r"^(\d+)\.(\d+)\.(\d+)$")
|
||||
|
||||
|
||||
def read_version() -> str:
|
||||
version = VERSION_FILE.read_text().strip()
|
||||
if not SEMVER.fullmatch(version):
|
||||
raise SystemExit(f"Invalid semantic version in {VERSION_FILE}: {version}")
|
||||
return version
|
||||
|
||||
|
||||
def check(version: str) -> None:
|
||||
chart = CHART_FILE.read_text()
|
||||
chart_version = re.findall(r"(?m)^version:\s*(\S+)\s*$", chart)
|
||||
app_version = re.findall(r'(?m)^appVersion:\s*"(\S+)"\s*$', chart)
|
||||
if chart_version != [version] or app_version != [version]:
|
||||
raise SystemExit(
|
||||
f"Version drift: VERSION={version}, chart version={chart_version}, "
|
||||
f"appVersion={app_version}"
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description=__doc__)
|
||||
parser.add_argument("--check", action="store_true")
|
||||
args = parser.parse_args()
|
||||
current = read_version()
|
||||
check(current)
|
||||
if args.check:
|
||||
print(current)
|
||||
return
|
||||
major, minor, patch = map(int, current.split("."))
|
||||
next_version = f"{major}.{minor}.{patch + 1}"
|
||||
chart = CHART_FILE.read_text()
|
||||
chart = re.sub(r"(?m)^version:\s*" + re.escape(current) + r"\s*$", f"version: {next_version}", chart, count=1)
|
||||
chart = re.sub(r'(?m)^appVersion:\s*"' + re.escape(current) + r'"\s*$', f'appVersion: "{next_version}"', chart, count=1)
|
||||
VERSION_FILE.write_text(f"{next_version}\n")
|
||||
CHART_FILE.write_text(chart)
|
||||
print(next_version)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
grep -q 'runAsNonRoot: true' deploy/helm/ai-spel-static/templates/deployment.yaml
|
||||
grep -q 'readOnlyRootFilesystem: true' deploy/helm/ai-spel-static/templates/deployment.yaml
|
||||
grep -q 'maxUnavailable: 0' deploy/helm/ai-spel-static/values.yaml
|
||||
grep -q 'maxSurge: 1' deploy/helm/ai-spel-static/values.yaml
|
||||
grep -q 'path: /static' deploy/helm/ai-spel-static/templates/ingress.yaml
|
||||
grep -q 'max-age=31536000, immutable' nginx/default.conf
|
||||
grep -q 'application/javascript js mjs;' nginx/default.conf
|
||||
grep -q 'application/wasm wasm;' nginx/default.conf
|
||||
grep -Fq 'try_files $uri =404;' nginx/default.conf
|
||||
node --test tests/*.test.cjs
|
||||
echo 'Static asset source checks passed.'
|
||||
@@ -0,0 +1,11 @@
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
|
||||
test('Piper asset manifest pins every Swedish voice and required ONNX loader files', async () => {
|
||||
const {assetPrefix, voiceFiles, requiredOnnxFiles} = await import('../scripts/assets.mjs');
|
||||
assert.equal(assetPrefix, 'piper-wasm-v1');
|
||||
assert.deepEqual([...new Set(voiceFiles.map(file => file.path.match(/sv_SE\/(\w+)\//)?.[1]))].sort(), ['alma', 'lisa', 'nst']);
|
||||
assert.ok(voiceFiles.every(file => Number.isInteger(file.size) && (file.sha256 || file.gitBlobSha1)));
|
||||
assert.ok(requiredOnnxFiles.includes('ort-wasm-simd-threaded.mjs'));
|
||||
assert.ok(requiredOnnxFiles.includes('ort-wasm-simd-threaded.wasm'));
|
||||
});
|
||||
Reference in New Issue
Block a user