d3ro-voice/scripts/ci/lib/runtime-index-builder.mjs
Yun Chan ba9ef9741e fix: red-team round 3 hardening across desktop, mobile, core and server
Batch of red-team r3 fixes that were in the working tree before the
2026-09-28 design overhaul, committed as one unit with their tests.

- desktop main: STT timeouts and sidecar, voice recording store, sync
  (credentials, audio, knowledge reindex, push gates), runtime
  provisioner, update policy, AltGr keybindings, voice-command policy,
  dictionary file codec/limits, meeting transcript condensing and a
  local recording ledger so interrupted-session recovery only closes
  meetings this device recorded (a phone's live meeting is left alone).
- mobile: login CSRF via implicit token callbacks rejected, account
  deletion/retention, durable queue retention, knowledge realtime
  without unfiltered DELETE, meeting re-record failure paths, cloud STT
  client, preferences store/resync.
- core: text chunking splits long unbroken transcripts to fit, template
  field policy, dictionary limits, meeting markdown inline handling.
- server: payple webhook policy and cancellation order scope, meeting
  document generation quota, team RPC null-role guard, unified LLM
  quota in-flight accounting, knowledge chunk vector index, meeting
  re-record failure paths (migrations 20260929*).
- ci: portable/runtime feed gates, update-policy schema, Forgejo file
  delete and alias planning.

Four older tests are updated to the new contracts rather than the old
behavior: token-pair auth callbacks are rejected, knowledge realtime no
longer subscribes to DELETE, long transcript lines are split, and
meeting recovery requires the local recording ledger for empty rows.
2026-09-28 20:45:52 +09:00

52 lines
1.9 KiB
JavaScript

// scripts/ci/lib/runtime-index-builder.mjs
// 로컬 AI 런타임 인덱스(runtime.json)를 만드는 순수 함수.
//
// 왜 분리하나: build-portable.mjs 가 인덱스를 즉석 객체로 만들면서 platform/arch 를
// 빠뜨렸다. 사이드카(PyInstaller)와 ffmpeg 는 빌드 호스트의 네이티브 실행 파일인데,
// 인덱스가 그 사실을 밝히지 않아 macOS 앱이 Windows 엔진(sidecar.exe)을 받아 풀고
// 검증에서 떨어진 뒤 매번 ~160MB 를 다시 받았다. 인덱스 모양은 여기 한 곳에서 정하고,
// 클라이언트(apps/desktop/src/main/services/runtime/runtime-index.ts)는 platform/arch 가
// 다르면 부품을 받기 전에 거부한다.
export const RUNTIME_INDEX_SCHEMA_VERSION = 1;
/**
* @typedef {{ name: string, size: number, sha256: string }} RuntimePackedPart
* @typedef {{ archive: string, sha256: string, totalSize: number, parts: RuntimePackedPart[] }} RuntimePackedComponent
*/
/**
* @param {{
* version: string,
* generatedAt: string,
* platform: string,
* arch: string,
* partBaseUrl: string,
* components: Record<string, RuntimePackedComponent>,
* }} input
*/
export function buildRuntimeIndex({ version, generatedAt, platform, arch, partBaseUrl, components }) {
if (typeof platform !== "string" || platform.length === 0) {
throw new Error("runtime index requires a build platform (process.platform)");
}
if (typeof arch !== "string" || arch.length === 0) {
throw new Error("runtime index requires a build arch (process.arch)");
}
const base = String(partBaseUrl).replace(/\/+$/, "");
return {
schemaVersion: RUNTIME_INDEX_SCHEMA_VERSION,
version,
platform,
arch,
generatedAt,
components: Object.fromEntries(
Object.entries(components).map(([name, entry]) => [
name,
{
...entry,
parts: entry.parts.map((part) => ({ ...part, url: `${base}/${part.name}` })),
},
]),
),
};
}