feat: publish HoloLake model-native living system source

This commit is contained in:
冰朔 2026-08-03 10:04:41 +08:00
commit c395dd3a99
2467 changed files with 615073 additions and 0 deletions

View file

@ -0,0 +1,329 @@
import { spawnSync } from 'node:child_process'
import { error as logError, log } from 'node:console'
import { existsSync } from 'node:fs'
import {
chmod,
mkdir,
mkdtemp,
readFile,
rename,
rm,
writeFile,
} from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { dirname, join, resolve } from 'node:path'
import process from 'node:process'
import { fileURLToPath } from 'node:url'
export const BROKEN_LINUXDEPLOY_APPRUN_DIR_LINE =
'this_dir="$(readlink -f "$(dirname "$0")")"'
export const FIXED_LINUXDEPLOY_APPRUN_DIR_LINE =
'this_dir="$(dirname "$(readlink -f "$0")")"'
export const APPIMAGE_PLUGIN_WRAPPER_NAME = 'linuxdeploy-plugin-appimage.AppImage'
export const REAL_APPIMAGE_PLUGIN_NAME =
'tolaria-real-linuxdeploy-plugin-appimage/linuxdeploy-plugin-appimage.AppImage'
export const APPIMAGE_FCITX_GTK3_IM_MODULE_PATH =
'usr/lib/x86_64-linux-gnu/gtk-3.0/3.0.0/immodules/im-fcitx5.so'
export const APPIMAGE_FCITX_GCLIENT_LIBRARY_PATH =
'usr/lib/x86_64-linux-gnu/libFcitx5GClient.so.2'
export const DEFAULT_APPIMAGE_PLUGIN_URL =
'https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-x86_64.AppImage'
const WRAPPER_MARKER = 'Tolaria AppImage symlink launcher shim'
const REQUIRED_APPIMAGE_PATHS = [
'AppRun',
APPIMAGE_FCITX_GTK3_IM_MODULE_PATH,
APPIMAGE_FCITX_GCLIENT_LIBRARY_PATH,
]
export function tauriToolsCacheDir(env = process.env) {
if (env.TOLARIA_TAURI_TOOLS_DIR) {
return resolve(env.TOLARIA_TAURI_TOOLS_DIR)
}
if (env.XDG_CACHE_HOME) {
return resolve(env.XDG_CACHE_HOME, 'tauri')
}
if (!env.HOME) {
throw new Error('HOME or XDG_CACHE_HOME is required to locate the Tauri tools cache')
}
return resolve(env.HOME, '.cache', 'tauri')
}
export function patchAppRunText(text) {
if (text.includes(BROKEN_LINUXDEPLOY_APPRUN_DIR_LINE)) {
return {
changed: true,
text: text.replaceAll(
BROKEN_LINUXDEPLOY_APPRUN_DIR_LINE,
FIXED_LINUXDEPLOY_APPRUN_DIR_LINE,
),
}
}
return { changed: false, text }
}
export function assertSymlinkSafeAppRunText(text, label = 'AppRun') {
if (text.includes(BROKEN_LINUXDEPLOY_APPRUN_DIR_LINE)) {
throw new Error(`${label} still resolves dirname before following AppRun symlinks`)
}
if (!text.includes(FIXED_LINUXDEPLOY_APPRUN_DIR_LINE)) {
throw new Error(`${label} is missing the symlink-safe AppRun directory resolver`)
}
}
export function appImagePluginWrapperSource({
pluginUrl = DEFAULT_APPIMAGE_PLUGIN_URL,
} = {}) {
return `#!/usr/bin/env bash
set -euo pipefail
# ${WRAPPER_MARKER}
PLUGIN_URL="\${TOLARIA_APPIMAGE_PLUGIN_URL:-${pluginUrl}}"
SCRIPT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)"
REAL_PLUGIN="\${TOLARIA_APPIMAGE_REAL_PLUGIN:-"$SCRIPT_DIR/${REAL_APPIMAGE_PLUGIN_NAME}"}"
FCITX_GTK3_IM_MODULE="\${TOLARIA_FCITX_GTK3_IM_MODULE:-/usr/lib/x86_64-linux-gnu/gtk-3.0/3.0.0/immodules/im-fcitx5.so}"
FCITX_LIBRARY_DIR="\${TOLARIA_FCITX_LIBRARY_DIR:-/usr/lib/x86_64-linux-gnu}"
appdir_from_args() {
local previous=""
for arg in "$@"; do
if [ "$previous" = "--appdir" ]; then
printf '%s\\n' "$arg"
return 0
fi
case "$arg" in
--appdir=*)
printf '%s\\n' "\${arg#--appdir=}"
return 0
;;
esac
previous="$arg"
done
}
download_real_plugin() {
if [ -x "$REAL_PLUGIN" ]; then
return 0
fi
local tmp_plugin="$REAL_PLUGIN.tmp.$$"
rm -f "$tmp_plugin"
mkdir -p "$(dirname -- "$REAL_PLUGIN")"
if command -v curl >/dev/null 2>&1; then
curl -fsSL -o "$tmp_plugin" "$PLUGIN_URL"
elif command -v wget >/dev/null 2>&1; then
wget -q -O "$tmp_plugin" "$PLUGIN_URL"
else
echo "curl or wget is required to fetch the real linuxdeploy AppImage output plugin" >&2
return 1
fi
chmod +x "$tmp_plugin"
mv "$tmp_plugin" "$REAL_PLUGIN"
}
patch_apprun() {
local appdir="\${APPDIR:-}"
if [ -z "$appdir" ]; then
appdir="$(appdir_from_args "$@" || true)"
fi
if [ -z "$appdir" ] || [ ! -f "$appdir/AppRun" ]; then
return 0
fi
python3 - "$appdir/AppRun" <<'PY'
from pathlib import Path
import sys
path = Path(sys.argv[1])
broken = 'this_dir="$(readlink -f "$(dirname "$0")")"'
fixed = 'this_dir="$(dirname "$(readlink -f "$0")")"'
text = path.read_text(encoding="utf-8")
if broken in text:
path.write_text(text.replace(broken, fixed), encoding="utf-8")
print(f"Patched linuxdeploy AppRun symlink resolution in {path}", file=sys.stderr)
elif fixed in text:
pass
elif "autogenerated by linuxdeploy" in text and "AppRun.wrapped" in text:
raise SystemExit(f"{path} is a linuxdeploy wrapper but does not contain the expected AppRun resolver")
PY
}
bundle_fcitx_gtk3_module() {
local appdir="\${APPDIR:-}"
if [ -z "$appdir" ]; then
appdir="$(appdir_from_args "$@" || true)"
fi
if [ -z "$appdir" ] || [ ! -d "$appdir" ] || [ ! -f "$FCITX_GTK3_IM_MODULE" ]; then
return 0
fi
local module_dest="$appdir/${APPIMAGE_FCITX_GTK3_IM_MODULE_PATH}"
local library_dest_dir="$appdir/usr/lib/x86_64-linux-gnu"
mkdir -p "$(dirname -- "$module_dest")" "$library_dest_dir"
cp -a "$FCITX_GTK3_IM_MODULE" "$module_dest"
local copied_library=0
shopt -s nullglob
for lib in "$FCITX_LIBRARY_DIR"/libFcitx5GClient.so* "$FCITX_LIBRARY_DIR"/libFcitx5Utils.so*; do
cp -a "$lib" "$library_dest_dir/"
copied_library=1
done
shopt -u nullglob
if [ "$copied_library" -eq 0 ]; then
echo "No fcitx GTK client libraries found in $FCITX_LIBRARY_DIR" >&2
fi
}
download_real_plugin
patch_apprun "$@"
bundle_fcitx_gtk3_module "$@"
exec "$REAL_PLUGIN" "$@"
`
}
export async function preparePluginWrapper({
env = process.env,
toolsDir = tauriToolsCacheDir(env),
} = {}) {
await mkdir(toolsDir, { recursive: true })
const wrapperPath = join(toolsDir, APPIMAGE_PLUGIN_WRAPPER_NAME)
const realPluginPath = join(toolsDir, REAL_APPIMAGE_PLUGIN_NAME)
if (existsSync(wrapperPath) && !existsSync(realPluginPath)) {
const existing = await readFile(wrapperPath, 'utf8').catch(() => '')
if (!existing.includes(WRAPPER_MARKER)) {
await mkdir(dirname(realPluginPath), { recursive: true })
await rename(wrapperPath, realPluginPath)
}
}
await writeFile(wrapperPath, appImagePluginWrapperSource(), 'utf8')
await chmod(wrapperPath, 0o755)
return { realPluginPath, wrapperPath }
}
export async function validateAppRunFile(path) {
const text = await readFile(path, 'utf8')
assertSymlinkSafeAppRunText(text, path)
}
function extractAppImagePath(appImage, requiredPath, tempDir) {
const result = spawnSync(appImage, ['--appimage-extract', requiredPath], {
cwd: tempDir,
encoding: 'utf8',
})
if (result.status === 0) {
return
}
throw new Error(
[
`Failed to extract ${requiredPath} from ${appImage}`,
result.stdout.trim(),
result.stderr.trim(),
]
.filter(Boolean)
.join('\n'),
)
}
function assertAppImagePathsExtracted(appImage, tempDir, requiredPaths) {
for (const requiredPath of requiredPaths) {
const extractedPath = join(tempDir, 'squashfs-root', requiredPath)
if (!existsSync(extractedPath)) {
throw new Error(`${appImage} is missing ${requiredPath}`)
}
}
}
async function validateExtractedAppImage(appImage, tempDir) {
for (const requiredPath of REQUIRED_APPIMAGE_PATHS) {
extractAppImagePath(appImage, requiredPath, tempDir)
}
await validateAppRunFile(join(tempDir, 'squashfs-root', 'AppRun'))
assertAppImagePathsExtracted(appImage, tempDir, REQUIRED_APPIMAGE_PATHS.slice(1))
}
export async function validateAppImages(paths) {
if (paths.length === 0) {
throw new Error('At least one AppImage path is required for launcher validation')
}
for (const appImage of paths.map((path) => resolve(path))) {
const tempDir = await mkdtemp(join(tmpdir(), 'tolaria-appimage-'))
try {
await validateExtractedAppImage(appImage, tempDir)
} finally {
await rm(tempDir, { recursive: true, force: true })
}
}
}
async function preparePluginCommand() {
const { wrapperPath, realPluginPath } = await preparePluginWrapper()
log(`Prepared ${wrapperPath}`)
log(`Real plugin cache: ${realPluginPath}`)
}
async function validateAppRunFilesCommand(paths) {
for (const path of paths) {
await validateAppRunFile(path)
log(`Validated ${path}`)
}
}
async function validateAppImagesCommand(paths) {
await validateAppImages(paths)
for (const path of paths) {
log(`Validated AppImage launcher in ${path}`)
}
}
const COMMANDS = new Map([
['prepare-plugin', preparePluginCommand],
['validate-apprun-file', validateAppRunFilesCommand],
['validate-appimages', validateAppImagesCommand],
])
function usage() {
return 'Usage: node scripts/appimage-launcher-tools.mjs prepare-plugin | validate-apprun-file <AppRun...> | validate-appimages <AppImage...>'
}
async function main() {
const [command, ...args] = process.argv.slice(2)
const handler = COMMANDS.get(command)
if (!handler) {
throw new Error(usage())
}
await handler(args)
}
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
main().catch((error) => {
logError(error.message)
process.exit(1)
})
}

View file

@ -0,0 +1,182 @@
import assert from 'node:assert/strict'
import { spawnSync } from 'node:child_process'
import { existsSync, realpathSync } from 'node:fs'
import {
chmod,
mkdir,
mkdtemp,
readFile,
symlink,
writeFile,
} from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { basename, dirname, join } from 'node:path'
import process from 'node:process'
import test from 'node:test'
import {
BROKEN_LINUXDEPLOY_APPRUN_DIR_LINE,
FIXED_LINUXDEPLOY_APPRUN_DIR_LINE,
REAL_APPIMAGE_PLUGIN_NAME,
appImagePluginWrapperSource,
patchAppRunText,
preparePluginWrapper,
} from './appimage-launcher-tools.mjs'
function brokenResolverDir(invokedPath) {
return realpathSync(dirname(invokedPath))
}
function fixedResolverDir(invokedPath) {
return dirname(realpathSync(invokedPath))
}
test('patches linuxdeploy AppRun wrapper to resolve the invoked path before dirname', () => {
const original = [
'#! /usr/bin/env bash',
'# autogenerated by linuxdeploy',
BROKEN_LINUXDEPLOY_APPRUN_DIR_LINE,
'exec "$this_dir"/AppRun.wrapped "$@"',
].join('\n')
const patched = patchAppRunText(original)
assert.equal(patched.changed, true)
assert.equal(patched.text.includes(BROKEN_LINUXDEPLOY_APPRUN_DIR_LINE), false)
assert.equal(patched.text.includes(FIXED_LINUXDEPLOY_APPRUN_DIR_LINE), true)
})
test('fixed resolver follows absolute and relative symlinks before choosing AppDir', async () => {
const root = await mkdtemp(join(tmpdir(), 'tolaria-apprun-resolver-'))
const appDir = join(root, 'Tolaria.AppDir')
const binDir = join(root, 'bin')
const relativeDir = join(root, 'relative-bin')
const appRun = join(appDir, 'AppRun')
await mkdir(appDir)
await mkdir(binDir)
await mkdir(relativeDir)
await writeFile(appRun, '#! /usr/bin/env bash\n', 'utf8')
const absoluteSymlink = join(binDir, 'tolaria')
const relativeSymlink = join(relativeDir, 'tolaria')
await symlink(appRun, absoluteSymlink)
await symlink(`../${basename(appDir)}/AppRun`, relativeSymlink)
assert.equal(brokenResolverDir(absoluteSymlink), realpathSync(binDir))
assert.equal(fixedResolverDir(absoluteSymlink), realpathSync(appDir))
assert.equal(brokenResolverDir(relativeSymlink), realpathSync(relativeDir))
assert.equal(fixedResolverDir(relativeSymlink), realpathSync(appDir))
})
test('plugin wrapper patches AppRun before delegating to the real output plugin', async () => {
const root = await mkdtemp(join(tmpdir(), 'tolaria-appimage-plugin-'))
const appDir = join(root, 'Tolaria.AppDir')
const appRun = join(appDir, 'AppRun')
const wrapper = join(root, 'linuxdeploy-plugin-appimage.AppImage')
const realPlugin = join(root, 'linuxdeploy-plugin-appimage.real.AppImage')
const pluginMarker = join(root, 'plugin-ran')
await mkdir(appDir)
await writeFile(
appRun,
[
'#! /usr/bin/env bash',
'# autogenerated by linuxdeploy',
BROKEN_LINUXDEPLOY_APPRUN_DIR_LINE,
'exec "$this_dir"/AppRun.wrapped "$@"',
].join('\n'),
'utf8',
)
await writeFile(wrapper, appImagePluginWrapperSource(), 'utf8')
await chmod(wrapper, 0o755)
await writeFile(
realPlugin,
`#!/usr/bin/env bash\nset -euo pipefail\ntouch "${pluginMarker}"\n`,
'utf8',
)
await chmod(realPlugin, 0o755)
const result = spawnSync(wrapper, [], {
encoding: 'utf8',
env: {
...process.env,
APPDIR: appDir,
TOLARIA_APPIMAGE_REAL_PLUGIN: realPlugin,
},
})
assert.equal(result.status, 0, result.stderr)
assert.equal(existsSync(pluginMarker), true)
const patched = await readFile(appRun, 'utf8')
assert.equal(patched.includes(BROKEN_LINUXDEPLOY_APPRUN_DIR_LINE), false)
assert.equal(patched.includes(FIXED_LINUXDEPLOY_APPRUN_DIR_LINE), true)
})
test('plugin wrapper keeps the delegated appimage plugin basename canonical', async () => {
const root = await mkdtemp(join(tmpdir(), 'tolaria-appimage-tools-'))
const { realPluginPath, wrapperPath } = await preparePluginWrapper({
toolsDir: root,
})
assert.equal(basename(wrapperPath), 'linuxdeploy-plugin-appimage.AppImage')
assert.equal(
realPluginPath,
join(root, 'tolaria-real-linuxdeploy-plugin-appimage', 'linuxdeploy-plugin-appimage.AppImage'),
)
assert.equal(REAL_APPIMAGE_PLUGIN_NAME.endsWith('/linuxdeploy-plugin-appimage.AppImage'), true)
const wrapper = await readFile(wrapperPath, 'utf8')
assert.equal(wrapper.includes('linuxdeploy-plugin-appimage.real.AppImage'), false)
})
test('plugin wrapper bundles fcitx GTK3 input module before sealing AppImage', async () => {
const root = await mkdtemp(join(tmpdir(), 'tolaria-appimage-fcitx-'))
const appDir = join(root, 'Tolaria.AppDir')
const wrapper = join(root, 'linuxdeploy-plugin-appimage.AppImage')
const realPlugin = join(root, 'linuxdeploy-plugin-appimage.real.AppImage')
const pluginMarker = join(root, 'plugin-ran')
const hostModule = join(root, 'host', 'im-fcitx5.so')
const hostLibraryDir = join(root, 'host-lib')
const hostLibrary = join(hostLibraryDir, 'libFcitx5GClient.so.2')
await mkdir(appDir)
await mkdir(dirname(hostModule), { recursive: true })
await mkdir(hostLibraryDir)
await writeFile(hostModule, 'fake fcitx gtk module', 'utf8')
await writeFile(hostLibrary, 'fake fcitx client library', 'utf8')
await writeFile(wrapper, appImagePluginWrapperSource(), 'utf8')
await chmod(wrapper, 0o755)
await writeFile(
realPlugin,
`#!/usr/bin/env bash\nset -euo pipefail\ntouch "${pluginMarker}"\n`,
'utf8',
)
await chmod(realPlugin, 0o755)
const result = spawnSync(wrapper, [], {
encoding: 'utf8',
env: {
...process.env,
APPDIR: appDir,
TOLARIA_APPIMAGE_REAL_PLUGIN: realPlugin,
TOLARIA_FCITX_GTK3_IM_MODULE: hostModule,
TOLARIA_FCITX_LIBRARY_DIR: hostLibraryDir,
},
})
assert.equal(result.status, 0, result.stderr)
assert.equal(existsSync(pluginMarker), true)
assert.equal(
existsSync(
join(
appDir,
'usr/lib/x86_64-linux-gnu/gtk-3.0/3.0.0/immodules/im-fcitx5.so',
),
),
true,
)
assert.equal(existsSync(join(appDir, 'usr/lib/x86_64-linux-gnu/libFcitx5GClient.so.2')), true)
})

View file

@ -0,0 +1,197 @@
import { mkdir, readdir, readFile, rm, writeFile } from 'node:fs/promises'
import path from 'node:path'
import { pathToFileURL } from 'node:url'
const repoRoot = path.resolve(import.meta.dirname, '..')
const siteRoot = path.join(repoRoot, 'site')
const outputRoot = path.join(repoRoot, 'src-tauri', 'resources', 'agent-docs')
const sectionOrder = ['start', 'concepts', 'guides', 'templates', 'reference', 'troubleshooting', 'download', 'releases']
const ignoredDirs = new Set(['.vitepress', 'public', 'node_modules', '.DS_Store'])
function titleFromSlug(slug) {
return slug
.replace(/[-_]+/g, ' ')
.replace(/\b\w/g, (letter) => letter.toUpperCase())
}
function stripFrontmatter(markdown) {
return markdown.replace(/^---\n[\s\S]*?\n---\n/, '')
}
function firstHeading(markdown, fallback) {
const match = markdown.match(/^#\s+(.+)$/m)
return match?.[1]?.trim() || fallback
}
export function normalizeDocPath(relativePath) {
return relativePath.replaceAll(path.win32.sep, '/')
}
export function sectionForFile(relativePath) {
const [firstPart] = relativePath.split('/')
if (firstPart === 'index.md') return 'home'
return firstPart.replace(/\.md$/, '')
}
async function listMarkdownFiles(dir, base = dir) {
const entries = await readdir(dir, { withFileTypes: true })
const files = []
for (const entry of entries) {
if (ignoredDirs.has(entry.name)) continue
const fullPath = path.join(dir, entry.name)
if (entry.isDirectory()) {
files.push(...await listMarkdownFiles(fullPath, base))
} else if (entry.isFile() && entry.name.endsWith('.md')) {
files.push(normalizeDocPath(path.relative(base, fullPath)))
}
}
return files
}
function sortDocs(files) {
return files.sort((a, b) => {
const sectionDiff = sectionOrder.indexOf(sectionForFile(a)) - sectionOrder.indexOf(sectionForFile(b))
if (sectionDiff !== 0) return sectionDiff
return a.localeCompare(b)
})
}
function docUrl(relativePath) {
const withoutExt = relativePath.replace(/(^|\/)index\.md$/, '$1').replace(/\.md$/, '')
return `/${withoutExt}`.replace(/\/$/, '/') || '/'
}
function formatDoc(doc) {
return `# ${doc.title}\n\nSource: ${doc.path}\nURL: ${doc.url}\n\n${doc.content}`
}
function groupDocsBySection(docs) {
const bySection = new Map()
for (const doc of docs) {
const docsInSection = bySection.get(doc.section) ?? []
docsInSection.push(doc)
bySection.set(doc.section, docsInSection)
}
return bySection
}
function buildIndex(docs) {
const bySection = groupDocsBySection(docs)
const lines = [
'# HoloLake Era Agent Docs',
'',
'These docs are generated from the public HoloLake Era documentation for local AI agent lookup.',
'',
'Start here, then use `rg` over this folder for specific HoloLake Era concepts and workflows.',
'',
'When users ask how to improve a knowledge base, structure notes, choose types, model relationships, or make a vault easier for humans and agents to use, treat Portent as HoloLake Era\'s default best-practice model. Read `pages/templates/portent.md` and combine it with the HoloLake Era concepts for types, relationships, properties, Inbox, archive, and custom views.',
'',
]
for (const section of ['home', ...sectionOrder]) {
const docsInSection = bySection.get(section)
if (!docsInSection?.length) continue
lines.push(`## ${titleFromSlug(section)}`, '')
for (const doc of docsInSection) {
lines.push(`- [${doc.title}](pages/${doc.path})`)
}
lines.push('')
}
lines.push('## Generated Files', '')
lines.push('- `all.md`: all public docs concatenated for fast full-context reads.')
lines.push('- `search-index.json`: title, heading, section, path, and URL metadata for quick routing.')
lines.push('- `<section>.md`: one compact bundle per docs section.')
lines.push('- `pages/`: one generated Markdown file per public docs page.')
lines.push('')
return lines.join('\n')
}
function buildAgentInstructions() {
return `# AGENTS.md - HoloLake Era Docs Bundle
This folder contains local, generated HoloLake Era product docs for AI agents.
Use these docs when a user asks how HoloLake Era works, when you need product behavior, or before making HoloLake Era-specific assumptions.
Portent is the default best-practice model for structuring HoloLake Era knowledge bases. When a user asks how to improve a knowledge base, make it better organized, choose better types, model relationships, or make the vault easier for humans and agents to use, read \`pages/templates/portent.md\` and combine it with HoloLake Era's docs for types, relationships, properties, Inbox, archive, and custom views.
Recommended lookup flow:
1. Read the active vault's AGENTS.md for vault-specific conventions.
2. Read this folder's index.md for the docs map.
3. Use \`rg\` over this folder for advanced concepts, workflows, shortcuts, Git, AutoGit, AI, Portent, types, properties, relationships, and troubleshooting.
Vault-specific AGENTS.md wins for local conventions. These bundled docs win for HoloLake Era product behavior.
`
}
function searchIndexFor(doc) {
const headings = [...doc.content.matchAll(/^#{2,3}\s+(.+)$/gm)].map((match) => match[1].trim())
return {
title: doc.title,
path: `pages/${doc.path}`,
url: doc.url,
section: doc.section,
headings,
}
}
async function main() {
const files = sortDocs(await listMarkdownFiles(siteRoot))
const docs = []
for (const relativePath of files) {
const raw = await readFile(path.join(siteRoot, relativePath), 'utf8')
const content = stripFrontmatter(raw).trim()
const fallbackTitle = titleFromSlug(path.basename(relativePath, '.md'))
docs.push({
content,
path: relativePath,
section: sectionForFile(relativePath),
title: firstHeading(content, fallbackTitle),
url: docUrl(relativePath),
})
}
await rm(outputRoot, { force: true, recursive: true })
await mkdir(outputRoot, { recursive: true })
await writeFile(path.join(outputRoot, 'AGENTS.md'), buildAgentInstructions())
await writeFile(path.join(outputRoot, 'index.md'), buildIndex(docs))
await writeFile(path.join(outputRoot, 'all.md'), docs.map(formatDoc).join('\n\n---\n\n'))
await writeFile(path.join(outputRoot, 'search-index.json'), `${JSON.stringify(docs.map(searchIndexFor), null, 2)}\n`)
for (const doc of docs) {
const outputPath = path.join(outputRoot, 'pages', doc.path)
await mkdir(path.dirname(outputPath), { recursive: true })
await writeFile(outputPath, formatDoc(doc))
}
const bySection = groupDocsBySection(docs)
for (const [section, docsInSection] of bySection) {
await writeFile(
path.join(outputRoot, `${section}.md`),
docsInSection.map(formatDoc).join('\n\n---\n\n'),
)
}
console.log(`Generated ${docs.length} agent docs in ${path.relative(repoRoot, outputRoot)}`)
}
const entrypointUrl = process.argv[1] ? pathToFileURL(process.argv[1]).href : ''
if (import.meta.url === entrypointUrl) {
main().catch((error) => {
console.error(error)
process.exit(1)
})
}

View file

@ -0,0 +1,11 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import { normalizeDocPath, sectionForFile } from './build-agent-docs.mjs'
test('normalizes Windows doc paths before section grouping', () => {
const docPath = normalizeDocPath('concepts\\ai.md')
assert.equal(docPath, 'concepts/ai.md')
assert.equal(sectionForFile(docPath), 'concepts')
})

View file

@ -0,0 +1,18 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
platform="${1:-}"
case "$platform" in
macos)
exec "$repo_root/scripts/build-macos-internal.sh"
;;
windows)
exec "$repo_root/scripts/build-windows-jd-cross.sh"
;;
*)
echo "Usage: ./scripts/build-internal-release.sh <macos|windows>" >&2
exit 2
;;
esac

View file

@ -0,0 +1,49 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$repo_root"
[[ "$(uname -s)" == "Darwin" ]] || { echo "macOS internal packages must be built on macOS." >&2; exit 2; }
target="${HOLOLAKE_MAC_TARGET:-aarch64-apple-darwin}"
version="$(node scripts/internal-release-version.mjs src-tauri/tauri.conf.json "${HOLOLAKE_VERSION:-}")"
product_name="$(node -e "const fs=require('node:fs'); const value=JSON.parse(fs.readFileSync('src-tauri/tauri.conf.json','utf8')).productName; if(!value) process.exit(1); process.stdout.write(value)")"
output_dir="${HOLOLAKE_OUTPUT_DIR:-$repo_root/artifacts/internal/$version}"
target_root="${CARGO_TARGET_DIR:-$repo_root/src-tauri/target}"
bundle_dir="$target_root/$target/release/bundle/macos"
app="$bundle_dir/${HOLOLAKE_APP_NAME:-$product_name}.app"
installer="$output_dir/HoloLake-Era-$version-Mac-internal-aarch64.dmg"
tauri_config="${HOLOLAKE_TAURI_CONFIG:-}"
if [[ -n "${HOLOLAKE_INSTALLER_BASENAME:-}" ]]; then
installer_basename="${HOLOLAKE_INSTALLER_BASENAME//\{version\}/$version}"
installer="$output_dir/${installer_basename}.dmg"
fi
if command -v pnpm >/dev/null 2>&1; then
pnpm_cmd=(pnpm)
elif command -v corepack >/dev/null 2>&1; then
pnpm_cmd=(corepack pnpm)
else
echo "pnpm or corepack is required." >&2
exit 2
fi
command -v hdiutil >/dev/null || { echo "hdiutil is required." >&2; exit 2; }
mkdir -p "$output_dir"
if [[ "${HOLOLAKE_SKIP_INSTALL:-0}" != "1" ]]; then
"${pnpm_cmd[@]}" install --frozen-lockfile
fi
[[ -x "$repo_root/node_modules/.bin/tauri" ]] || { echo "Local Tauri CLI is missing; install dependencies first." >&2; exit 2; }
tauri_args=(build --target "$target" --bundles app)
if [[ -n "$tauri_config" ]]; then tauri_args+=(--config "$tauri_config"); fi
"$repo_root/node_modules/.bin/tauri" "${tauri_args[@]}"
[[ -d "$app" ]] || { echo "No macOS application bundle was produced: $app" >&2; exit 1; }
node scripts/verify-internal-package-content.mjs "$app"
codesign --force --deep --sign - "$app"
codesign --verify --deep --strict "$app"
hdiutil create -volname "HoloLake Era $version Internal" -srcfolder "$app" -ov -format UDZO "$installer"
hdiutil verify "$installer"
shasum -a 256 "$installer" | sed "s# .*# $(basename "$installer")#" > "$installer.sha256"
echo "$installer"

View file

@ -0,0 +1,39 @@
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'
import { dirname, resolve } from 'node:path'
import {
buildStableDownloadRedirectPage,
resolveStableDownloadTargets,
} from '../src/utils/releaseDownloadPage'
function getArg(flag: string): string {
const index = process.argv.indexOf(flag)
const value = index >= 0 ? process.argv[index + 1] : null
if (!value) {
throw new Error(`Missing required argument: ${flag}`)
}
return value
}
function readLatestReleasePayload(filePath: string): unknown {
try {
return JSON.parse(readFileSync(filePath, 'utf8'))
} catch {
return {}
}
}
const latestJsonPath = resolve(getArg('--latest-json'))
const releasesJsonPath = resolve(getArg('--releases-json'))
const outputFilePath = resolve(getArg('--output-file'))
const latestPayload = readLatestReleasePayload(latestJsonPath)
const releasesPayload = readLatestReleasePayload(releasesJsonPath)
const downloads = resolveStableDownloadTargets(latestPayload, releasesPayload)
const html = buildStableDownloadRedirectPage(downloads)
mkdirSync(dirname(outputFilePath), { recursive: true })
writeFileSync(outputFilePath, html)
console.log(`Stable download page written to ${outputFilePath}`)

View file

@ -0,0 +1,33 @@
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs'
import { dirname, resolve } from 'node:path'
import { buildReleaseHistoryPage } from '../src/utils/releaseHistoryPage'
function getArg(flag: string): string {
const index = process.argv.indexOf(flag)
const value = index >= 0 ? process.argv[index + 1] : null
if (!value) {
throw new Error(`Missing required argument: ${flag}`)
}
return value
}
function readReleasePayload(filePath: string): unknown {
try {
return JSON.parse(readFileSync(filePath, 'utf8'))
} catch {
return []
}
}
const releasesJsonPath = resolve(getArg('--releases-json'))
const outputFilePath = resolve(getArg('--output-file'))
const releasesPayload = readReleasePayload(releasesJsonPath)
const html = buildReleaseHistoryPage(releasesPayload)
mkdirSync(dirname(outputFilePath), { recursive: true })
writeFileSync(outputFilePath, html)
console.log(`Release history page written to ${outputFilePath}`)

View file

@ -0,0 +1,82 @@
#!/usr/bin/env bash
set -euo pipefail
target="x86_64-pc-windows-msvc"
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
cd "$repo_root"
if [[ "$(uname -s)" != "Linux" ]]; then
echo "This cross-build entrypoint is intended for the JD Linux build node." >&2
exit 2
fi
if command -v apt-get >/dev/null 2>&1; then
apt_prefix=()
if [[ "$(id -u)" -ne 0 ]]; then apt_prefix=(sudo); fi
"${apt_prefix[@]}" apt-get update
"${apt_prefix[@]}" apt-get install -y clang curl lld llvm nsis build-essential pkg-config libssl-dev
fi
command -v node >/dev/null || { echo "Node.js is required." >&2; exit 2; }
version="$(node scripts/internal-release-version.mjs src-tauri/tauri.conf.json "${HOLOLAKE_VERSION:-}")"
tauri_config="${HOLOLAKE_TAURI_CONFIG:-}"
export PATH="$HOME/.cargo/bin:$PATH"
if ! command -v cargo >/dev/null 2>&1; then
command -v curl >/dev/null || { echo "curl is required to install Rust." >&2; exit 2; }
export RUSTUP_DIST_SERVER="${RUSTUP_DIST_SERVER:-https://rsproxy.cn}"
export RUSTUP_UPDATE_ROOT="${RUSTUP_UPDATE_ROOT:-https://rsproxy.cn/rustup}"
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal
fi
export CARGO_HOME="${CARGO_HOME:-$HOME/.cargo-jd}"
mkdir -p "$CARGO_HOME"
cp scripts/cargo-config-jd.toml "$CARGO_HOME/config.toml"
export PATH="$CARGO_HOME/bin:$HOME/.cargo/bin:$PATH"
if ! command -v corepack >/dev/null 2>&1; then
npm install --global corepack
fi
corepack enable 2>/dev/null || true
corepack prepare pnpm@10.25.0 --activate
rustup target add "$target"
command -v cargo-xwin >/dev/null || cargo install cargo-xwin --locked
pnpm install --frozen-lockfile
# Tauri's Linux CLI intentionally only exposes Linux bundle targets. Compile the
# Windows application with cargo-xwin, then hand the resulting payload to NSIS.
tauri_args=(build --runner cargo-xwin --target "$target" --no-bundle)
if [[ -n "$tauri_config" ]]; then tauri_args+=(--config "$tauri_config"); fi
pnpm tauri "${tauri_args[@]}"
target_root="${CARGO_TARGET_DIR:-$repo_root/src-tauri/target}"
release_dir="$target_root/$target/release"
app_exe="$release_dir/tolaria.exe"
[[ -f "$app_exe" ]] || { echo "No Windows executable was produced: $app_exe" >&2; exit 1; }
bundle_dir="$release_dir/bundle/nsis"
stage_dir="$bundle_dir/stage"
installer_basename="${HOLOLAKE_INSTALLER_BASENAME:-HoloLake-Era_${version}_x64-internal-setup}"
installer_basename="${installer_basename//\{version\}/$version}"
installer="$bundle_dir/${installer_basename}.exe"
rm -rf "$stage_dir"
mkdir -p "$stage_dir/resources" "$bundle_dir"
cp "$app_exe" "$stage_dir/HoloLake Era.exe"
cp src-tauri/icons/icon.ico "$stage_dir/icon.ico"
cp -R src-tauri/resources/mcp-server "$stage_dir/resources/"
cp -R src-tauri/resources/agent-docs "$stage_dir/resources/"
cp -R src-tauri/resources/public-architecture "$stage_dir/resources/"
makensis \
-DAPP_VERSION="$version" \
-DSTAGE_DIR="$stage_dir" \
-DOUTPUT_FILE="$installer" \
scripts/windows-installer.nsi
[[ -f "$installer" ]] || { echo "No NSIS installer was produced." >&2; exit 1; }
[[ "$(basename "$installer")" == *"$version"* ]] || { echo "Installer filename does not contain version $version: $installer" >&2; exit 1; }
node scripts/verify-internal-package-content.mjs dist
node scripts/verify-internal-package-content.mjs "$stage_dir"
sha256sum "$installer" > "$installer.sha256"
echo "$installer"

View file

@ -0,0 +1,45 @@
/**
* Bundle the mcp-server Node.js files into self-contained CJS bundles
* that can be shipped as Tauri resources inside the .app bundle.
*
* Output: src-tauri/resources/mcp-server/{index.js,ws-bridge.js}
*/
import { build } from 'esbuild'
import { fileURLToPath } from 'url'
import { dirname, join } from 'path'
import { mkdirSync, writeFileSync } from 'fs'
const __dirname = dirname(fileURLToPath(import.meta.url))
const ROOT = join(__dirname, '..')
const SRC = join(ROOT, 'mcp-server')
const OUT = join(ROOT, 'src-tauri', 'resources', 'mcp-server')
mkdirSync(OUT, { recursive: true })
// Tell Node.js that this directory contains CJS bundles, even if the
// root package.json declares "type": "module".
writeFileSync(join(OUT, 'package.json'), JSON.stringify({ type: 'commonjs' }))
const shared = {
platform: 'node',
bundle: true,
format: 'cjs',
target: 'node18',
// Mark optional native bindings as external — ws works fine without them
external: ['bufferutil', 'utf-8-validate'],
logLevel: 'warning',
}
await build({
...shared,
entryPoints: [join(SRC, 'index.js')],
outfile: join(OUT, 'index.js'),
})
await build({
...shared,
entryPoints: [join(SRC, 'ws-bridge.js')],
outfile: join(OUT, 'ws-bridge.js'),
})
console.log('mcp-server bundled → src-tauri/resources/mcp-server/')

View file

@ -0,0 +1,8 @@
[source.crates-io]
replace-with = "rsproxy-sparse"
[source.rsproxy-sparse]
registry = "sparse+https://rsproxy.cn/index/"
[net]
git-fetch-with-cli = true

View file

@ -0,0 +1,55 @@
#!/usr/bin/env node
import { readFile } from 'node:fs/promises'
export function validateDeploymentSource(policy, context) {
const route = policy.routes?.find(candidate => candidate.distribution === context.distribution)
if (!route || route.deployment_enabled !== true) {
return { ok: false, reason: 'deployment_source_route_inactive' }
}
if (route.repository_id !== context.repositoryId || route.channel_id !== context.channelId) {
return { ok: false, reason: 'deployment_source_binding_mismatch' }
}
if (route.source_owner_id !== context.sourceOwnerId) {
return { ok: false, reason: 'deployment_source_owner_mismatch' }
}
if (!route.allowed_authorizers?.includes(context.authorizerId)) {
return { ok: false, reason: 'deployment_authorizer_not_allowed' }
}
if (!route.allowed_personas?.includes(context.personaId)) {
return { ok: false, reason: 'deployment_persona_not_allowed' }
}
if (!route.allowed_execution_runtimes?.includes(context.executionRuntimeId)) {
return { ok: false, reason: 'deployment_execution_runtime_not_allowed' }
}
if (!route.allowed_targets?.includes(context.target)) {
return { ok: false, reason: 'deployment_target_not_allowed' }
}
return { ok: true }
}
async function run() {
const policyFile = process.env.HOLOLAKE_SOURCE_POLICY || 'research/source-route-policy.json'
const policy = JSON.parse(await readFile(policyFile, 'utf8'))
const context = {
distribution: process.env.HOLOLAKE_DISTRIBUTION || '',
repositoryId: process.env.HOLOLAKE_SOURCE_REPOSITORY_ID || '',
channelId: process.env.HOLOLAKE_SOURCE_CHANNEL_ID || '',
sourceOwnerId: process.env.HOLOLAKE_SOURCE_OWNER_ID || '',
authorizerId: process.env.HOLOLAKE_HUMAN_AUTHORIZER_ID || '',
personaId: process.env.HOLOLAKE_PERSONA_ID || '',
executionRuntimeId: process.env.HOLOLAKE_EXECUTION_RUNTIME_ID || '',
target: process.env.HOLOLAKE_DEPLOY_TARGET || '',
}
const result = validateDeploymentSource(policy, context)
if (!result.ok) {
console.error(`HoloLake deployment source rejected: ${result.reason}`)
process.exitCode = 77
return
}
console.log(`HoloLake deployment source verified: ${context.distribution} · ${context.channelId} · ${context.target}`)
}
if (import.meta.url === `file://${process.argv[1]}`) {
await run()
}

View file

@ -0,0 +1,104 @@
import assert from 'node:assert/strict'
import test from 'node:test'
import { validateDeploymentSource } from './deployment-source-guard.mjs'
const policy = {
schema: 'hololake.source-route-policy/v1',
routes: [
{
distribution: 'personal',
repository_id: 'REPO-008',
channel_id: 'HLP-CHANNEL-0001',
source_node: 'JD-FD-PRIMARY',
source_owner_id: 'ICE-GL∞',
allowed_authorizers: ['ICE-GL∞'],
allowed_personas: ['ICE-GL-ZY001'],
allowed_execution_runtimes: ['SYS-GLW-ZY-EXEC-0001'],
allowed_targets: ['JD-FD-PRIMARY'],
deployment_enabled: true,
},
{
distribution: 'team',
repository_id: null,
channel_id: null,
source_node: 'AW-GZ-001',
allowed_personas: [],
allowed_targets: ['AW-GZ-001'],
deployment_enabled: false,
},
{
distribution: 'public-module',
repository_id: null,
channel_id: null,
source_node: null,
allowed_personas: [],
allowed_targets: [],
deployment_enabled: false,
},
],
}
test('allows Ice Shuo authorization plus Zhuyuan main control to package the personal distribution', () => {
assert.deepEqual(validateDeploymentSource(policy, {
distribution: 'personal',
repositoryId: 'REPO-008',
channelId: 'HLP-CHANNEL-0001',
sourceOwnerId: 'ICE-GL∞',
authorizerId: 'ICE-GL∞',
personaId: 'ICE-GL-ZY001',
executionRuntimeId: 'SYS-GLW-ZY-EXEC-0001',
target: 'JD-FD-PRIMARY',
}), { ok: true })
})
test('rejects a team persona using Ice Shuo personal source', () => {
assert.deepEqual(validateDeploymentSource(policy, {
distribution: 'personal',
repositoryId: 'REPO-008',
channelId: 'HLP-CHANNEL-0001',
sourceOwnerId: 'ICE-GL∞',
authorizerId: 'ICE-GL∞',
personaId: 'AGE-TEAM-001',
executionRuntimeId: 'SYS-GLW-ZY-EXEC-0001',
target: 'JD-FD-PRIMARY',
}), { ok: false, reason: 'deployment_persona_not_allowed' })
})
test('blocks team deployment until its enterprise repository route is registered', () => {
assert.deepEqual(validateDeploymentSource(policy, {
distribution: 'team',
repositoryId: 'REPO-008',
channelId: 'HLP-CHANNEL-0001',
sourceOwnerId: 'ICE-GL∞',
authorizerId: 'ICE-GL∞',
personaId: 'AGE-TEAM-001',
executionRuntimeId: 'SYS-GLW-ZY-EXEC-0001',
target: 'AW-GZ-001',
}), { ok: false, reason: 'deployment_source_route_inactive' })
})
test('public modules are reusable source and never an application deployment target', () => {
assert.deepEqual(validateDeploymentSource(policy, {
distribution: 'public-module',
repositoryId: 'REPO-008',
channelId: 'HLP-CHANNEL-0001',
sourceOwnerId: 'ICE-GL∞',
authorizerId: 'ICE-GL∞',
personaId: 'ICE-GL-ZY001',
executionRuntimeId: 'SYS-GLW-ZY-EXEC-0001',
target: 'JD-FD-PRIMARY',
}), { ok: false, reason: 'deployment_source_route_inactive' })
})
test('rejects a human identity presented as the executing persona', () => {
assert.deepEqual(validateDeploymentSource(policy, {
distribution: 'personal',
repositoryId: 'REPO-008',
channelId: 'HLP-CHANNEL-0001',
sourceOwnerId: 'ICE-GL∞',
authorizerId: 'ICE-GL∞',
personaId: 'ICE-GL∞',
executionRuntimeId: 'SYS-GLW-ZY-EXEC-0001',
target: 'JD-FD-PRIMARY',
}), { ok: false, reason: 'deployment_persona_not_allowed' })
})

View file

@ -0,0 +1,526 @@
#!/usr/bin/env node
/* global document, fetch, HTMLElement, performance, requestAnimationFrame, Response, setTimeout, URL, window */
import { spawn } from 'node:child_process'
import console from 'node:console'
import { rm } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import process from 'node:process'
import { resolve } from 'node:path'
import { chromium } from '@playwright/test'
import {
printSummary,
printThresholdFailures,
readThresholds,
thresholdFailures,
updateThresholds,
writeThresholds,
} from './editor-performance-thresholds.mjs'
const rootDir = process.cwd()
const defaultThresholdsPath = resolve(rootDir, '.editor-performance-thresholds.json')
const defaultPort = '41742'
const scenarios = {
small: { sectionCount: 5, title: 'Perf Small Note' },
large: { sectionCount: 460, title: 'Perf Large Note' },
}
const defaultScenarioNames = Object.keys(scenarios)
const metricLabels = {
blockApplyMs: 'block apply',
blockResolveMs: 'block resolve',
editFrameMs: 'edit frame',
editorVisibleMs: 'editor visible',
firstContentMs: 'first content rendered',
fullAppliedMs: 'full note applied',
noteOpenEditorSwapMs: 'note open editor swap',
noteOpenTotalMs: 'note open total',
}
function defaultOptions() {
return {
baseUrl: process.env.BASE_URL ?? '',
headful: false,
iterations: positiveInteger(process.env.EDITOR_PERF_ITERATIONS ?? '5', 'EDITOR_PERF_ITERATIONS'),
port: process.env.EDITOR_PERF_PORT ?? defaultPort,
scenarioNames: defaultScenarioNames,
thresholdsPath: defaultThresholdsPath,
update: false,
}
}
const flagOptions = {
'--headful': parsed => {
parsed.headful = true
},
'--update': parsed => {
parsed.update = true
},
}
const valueOptions = {
'--base-url': (parsed, value) => {
parsed.baseUrl = value
},
'--iterations': (parsed, value) => {
parsed.iterations = positiveInteger(value, '--iterations')
},
'--port': (parsed, value) => {
parsed.port = value
},
'--scenario': (parsed, value) => {
parsed.scenarioNames = value.split(',').filter(Boolean)
},
'--thresholds': (parsed, value) => {
parsed.thresholdsPath = value
},
}
function parseArgs(args) {
const parsed = defaultOptions()
for (let index = 0; index < args.length; index += 1) {
index = parseArg(parsed, args, index)
}
validateScenarioNames(parsed.scenarioNames)
return parsed
}
function parseArg(parsed, args, index) {
const arg = args[index]
if (arg === '--') return index
if (arg === '--help' || arg === '-h') exitWithHelp(0)
if (flagOptions[arg]) {
flagOptions[arg](parsed)
return index
}
if (valueOptions[arg]) {
valueOptions[arg](parsed, requiredValue(args, index, arg))
return index + 1
}
console.error(`Unknown argument: ${arg}`)
exitWithHelp(2)
return index
}
function validateScenarioNames(scenarioNames) {
for (const scenarioName of scenarioNames) {
if (!(scenarioName in scenarios)) {
console.error(`Unknown scenario: ${scenarioName}`)
process.exit(2)
}
}
}
function exitWithHelp(code) {
printHelp()
process.exit(code)
}
const options = parseArgs(process.argv.slice(2))
const thresholdsPath = resolve(rootDir, options.thresholdsPath)
let devServer = null
let stoppingDevServer = false
function requiredValue(args, index, name) {
const value = args[index + 1]
if (!value || value.startsWith('--')) {
console.error(`${name} requires a value`)
process.exit(2)
}
return value
}
function positiveInteger(value, name) {
if (/^[1-9][0-9]*$/.test(String(value))) return Number(value)
console.error(`${name} must be a positive integer`)
process.exit(2)
}
function printHelp() {
console.log(`Usage: pnpm perf:editor [options]
Options:
--base-url <url> Reuse an existing dev server instead of starting Vite.
--iterations <count> Runs per scenario. Default: 5.
--scenario <names> Comma-separated scenarios: small,large. Default: both.
--thresholds <path> Threshold JSON path. Default: .editor-performance-thresholds.json.
--update Ratchet stored baselines and thresholds from the current run.
--headful Run Chromium headed for debugging.
`)
}
function median(values) {
const numeric = values.filter(value => typeof value === 'number' && Number.isFinite(value))
if (numeric.length === 0) return null
const sorted = [...numeric].sort((a, b) => a - b)
const middle = Math.floor(sorted.length / 2)
return sorted.length % 2 === 0
? (sorted[middle - 1] + sorted[middle]) / 2
: sorted[middle]
}
function round(value) {
return value === null ? null : Math.round(value * 10) / 10
}
function largeMarkdown(sectionCount, title) {
const paragraphs = Array.from({ length: sectionCount }, (_, index) => {
const ordinal = index + 1
return [
`## Section ${ordinal}`,
'',
`Paragraph ${ordinal} keeps the large editor path realistic with **bold text**, *italic text*, `,
`a wikilink to [[Build Laputa App]], and a [reference link](https://example.com/${ordinal}). `,
'The text is intentionally long enough to push the source past the worker-backed parser threshold.',
].join('')
})
return [
'---',
`title: ${title}`,
'type: Note',
'---',
'',
`# ${title}`,
'',
...paragraphs,
].join('\n')
}
function syntheticEntry({ markdown, title }) {
return {
aliases: [],
archived: false,
belongsTo: [],
color: null,
createdAt: Math.floor(Date.now() / 1000) - 60,
favorite: false,
favoriteIndex: null,
fileSize: markdown.length,
filename: `${title.toLowerCase().replace(/\s+/g, '-')}.md`,
hasH1: true,
icon: null,
isA: 'Note',
listPropertiesDisplay: [],
modifiedAt: Math.floor(Date.now() / 1000) + 60,
order: null,
organized: false,
outgoingLinks: ['build-laputa-app'],
path: `/Users/luca/Laputa/${title.toLowerCase().replace(/\s+/g, '-')}.md`,
properties: {},
relationships: {},
relatedTo: [],
sidebarLabel: null,
snippet: 'Synthetic note for editor performance benchmarking.',
sort: null,
status: null,
template: null,
title,
view: null,
visible: null,
wordCount: 10 * Math.max(1, Math.floor(markdown.length / 280)),
}
}
async function waitForServer(url) {
const deadline = Date.now() + 30_000
while (Date.now() < deadline) {
try {
const response = await fetch(url)
if (response.ok) return
} catch (error) {
void error
}
await new Promise(resolveWait => setTimeout(resolveWait, 250))
}
throw new Error(`Timed out waiting for dev server: ${url}`)
}
async function startDevServer() {
if (options.baseUrl) return options.baseUrl
const baseUrl = `http://127.0.0.1:${options.port}`
const viteCacheDir = resolve(tmpdir(), `tolaria-editor-perf-vite-${options.port}`)
devServer = spawn(
'pnpm',
['dev', '--host', '127.0.0.1', '--port', options.port, '--strictPort'],
{
cwd: rootDir,
env: { ...process.env, TOLARIA_VITE_CACHE_DIR: viteCacheDir },
stdio: ['ignore', 'pipe', 'pipe'],
},
)
devServer.stdout?.on('data', chunk => process.stdout.write(`[perf-server] ${chunk}`))
devServer.stderr?.on('data', chunk => process.stderr.write(`[perf-server] ${chunk}`))
devServer.on('exit', (code, signal) => {
if (stoppingDevServer) return
if (signal || code === 0) return
console.error(`[perf-server] exited with status ${code}`)
})
await waitForServer(baseUrl)
return baseUrl
}
function stopDevServer() {
if (!devServer || devServer.killed) return
stoppingDevServer = true
devServer.stdout?.removeAllListeners('data')
devServer.stderr?.removeAllListeners('data')
devServer.kill('SIGTERM')
}
async function installSyntheticVault(page, entry, markdown) {
await page.addInitScript(({ syntheticEntryValue, syntheticMarkdown }) => {
const jsonResponse = value => new Response(JSON.stringify(value), {
headers: { 'Content-Type': 'application/json' },
status: 200,
})
const requestPath = (input) => {
const rawUrl = typeof input === 'string'
? input
: input && typeof input === 'object' && 'url' in input
? input.url
: String(input)
return new URL(rawUrl, window.location.href).pathname
}
const originalFetch = window.fetch.bind(window)
const syntheticResponses = {
'/api/vault/all-content': () => jsonResponse([{ content: syntheticMarkdown, path: syntheticEntryValue.path }]),
'/api/vault/content': () => jsonResponse({ content: syntheticMarkdown }),
'/api/vault/entry': () => jsonResponse(syntheticEntryValue),
'/api/vault/list': () => jsonResponse([syntheticEntryValue]),
'/api/vault/ping': () => new Response('ok', { status: 200 }),
'/api/vault/search': () => jsonResponse([syntheticEntryValue]),
}
window.fetch = async (input, init) => {
const responseFactory = syntheticResponses[requestPath(input)]
if (responseFactory) return responseFactory()
return originalFetch(input, init)
}
const withSyntheticEntry = (result) => {
const entries = Array.isArray(result) ? result : []
return [
syntheticEntryValue,
...entries.filter(candidate => candidate.path !== syntheticEntryValue.path),
]
}
const matchesSyntheticPath = args => args?.path === syntheticEntryValue.path
const handlerPatches = {
get_note_content: original => args => (
matchesSyntheticPath(args) ? syntheticMarkdown : original?.(args) ?? ''
),
list_vault: original => args => withSyntheticEntry(original?.(args)),
reload_vault: original => args => withSyntheticEntry(original?.(args)),
reload_vault_entry: original => args => (
matchesSyntheticPath(args) ? syntheticEntryValue : original?.(args)
),
validate_note_content: original => args => (
matchesSyntheticPath(args)
? args.content === syntheticMarkdown
: Boolean(original?.(args))
),
}
const patchHandlers = (handlers) => {
if (!handlers || handlers.__editorPerformancePatched) return handlers ?? null
for (const [name, createHandler] of Object.entries(handlerPatches)) {
handlers[name] = createHandler(handlers[name])
}
handlers.__editorPerformancePatched = true
return handlers
}
let handlersRef = patchHandlers(window.__mockHandlers)
Object.defineProperty(window, '__mockHandlers', {
configurable: true,
get() {
return handlersRef ?? undefined
},
set(value) {
handlersRef = patchHandlers(value)
},
})
}, { syntheticEntryValue: entry, syntheticMarkdown: markdown })
}
async function measureEditFrame(page) {
return await page.evaluate(async () => {
const root = document.querySelector('.bn-editor')
const editable = root?.querySelector('[contenteditable="true"]') ?? root
if (!root || !(editable instanceof HTMLElement)) return null
editable.focus()
const startedAt = performance.now()
document.execCommand('insertText', false, 'x')
await new Promise(resolveFrame => requestAnimationFrame(() => resolveFrame()))
return performance.now() - startedAt
})
}
function durationFromLog(logs, pattern) {
for (const line of logs) {
const match = line.match(pattern)
if (match?.[1]) return Number(match[1])
}
return null
}
function parsePerfMetrics(perfLogs) {
return {
blockApplyMs: durationFromLog(perfLogs, /editorBlockApply .* duration=([\d.]+)ms/),
blockResolveMs: durationFromLog(perfLogs, /editorBlockResolve .* duration=([\d.]+)ms/),
noteOpenEditorSwapMs: durationFromLog(perfLogs, /noteOpen .* editorSwap=([\d.]+)ms/),
noteOpenTotalMs: durationFromLog(perfLogs, /noteOpen .* total=([\d.]+)ms/),
}
}
async function runIteration({ baseUrl, browser, index, scenario, scenarioName }) {
const markdown = largeMarkdown(scenario.sectionCount, scenario.title)
const entry = syntheticEntry({ markdown, title: scenario.title })
const context = await browser.newContext()
const page = await context.newPage()
const perfLogs = []
page.on('console', (message) => {
const text = message.text()
if (text.includes('[perf]')) perfLogs.push(text)
})
await installSyntheticVault(page, entry, markdown)
await page.goto(baseUrl)
await page.waitForLoadState('domcontentloaded')
await page.getByText('Set up later', { exact: true }).click({ timeout: 12_000 }).catch(() => {})
const title = page.getByText(scenario.title, { exact: true }).first()
await title.waitFor({ state: 'visible', timeout: 30_000 })
const startedAt = await page.evaluate(() => performance.now())
await title.click()
await page.locator('.editor__blocknote-container').waitFor({ state: 'visible', timeout: 30_000 })
await page.locator('.bn-editor').waitFor({ state: 'visible', timeout: 30_000 })
const editorVisibleAt = await page.evaluate(() => performance.now())
await page.waitForFunction(() => {
const editor = document.querySelector('.bn-editor')
return editor?.textContent?.includes('Section 1') === true
}, undefined, { timeout: 30_000 })
const firstContentAt = await page.evaluate(() => performance.now())
await page.waitForFunction((expectedSectionCount) => {
const editor = document.querySelector('.bn-editor')
return editor?.textContent?.includes(`Section ${expectedSectionCount}`) === true
}, scenario.sectionCount, { timeout: 30_000 })
const fullAppliedAt = await page.evaluate(() => performance.now())
await page.locator('.bn-editor').click({ timeout: 10_000 })
const editFrameMs = []
for (let sample = 0; sample < 8; sample += 1) {
const value = await measureEditFrame(page)
if (typeof value === 'number') editFrameMs.push(value)
await page.waitForTimeout(80)
}
await context.close()
return {
...parsePerfMetrics(perfLogs),
editFrameMs,
editorVisibleMs: editorVisibleAt - startedAt,
firstContentMs: firstContentAt - startedAt,
fullAppliedMs: fullAppliedAt - startedAt,
index,
perfLogs,
scenario: scenarioName,
}
}
function summarizeScenario(scenarioName, scenario, runs) {
const editFrameSamples = runs.flatMap(run => run.editFrameMs)
const medians = {
blockApplyMs: round(median(runs.map(run => run.blockApplyMs))),
blockResolveMs: round(median(runs.map(run => run.blockResolveMs))),
editFrameMs: round(median(editFrameSamples)),
editorVisibleMs: round(median(runs.map(run => run.editorVisibleMs))),
firstContentMs: round(median(runs.map(run => run.firstContentMs))),
fullAppliedMs: round(median(runs.map(run => run.fullAppliedMs))),
noteOpenEditorSwapMs: round(median(runs.map(run => run.noteOpenEditorSwapMs))),
noteOpenTotalMs: round(median(runs.map(run => run.noteOpenTotalMs))),
}
return {
contentBytes: largeMarkdown(scenario.sectionCount, scenario.title).length,
medians,
runs: runs.map(run => ({
...run,
blockApplyMs: round(run.blockApplyMs),
blockResolveMs: round(run.blockResolveMs),
editFrameMs: run.editFrameMs.map(round),
editorVisibleMs: round(run.editorVisibleMs),
firstContentMs: round(run.firstContentMs),
fullAppliedMs: round(run.fullAppliedMs),
noteOpenEditorSwapMs: round(run.noteOpenEditorSwapMs),
noteOpenTotalMs: round(run.noteOpenTotalMs),
})),
scenario: scenarioName,
sectionCount: scenario.sectionCount,
}
}
async function runBenchmarks(baseUrl) {
const browser = await chromium.launch({
headless: !options.headful,
...(process.env.PLAYWRIGHT_EXECUTABLE_PATH
? { executablePath: process.env.PLAYWRIGHT_EXECUTABLE_PATH }
: {}),
})
const summaries = {}
try {
for (const scenarioName of options.scenarioNames) {
const scenario = scenarios[scenarioName]
console.log(`[perf] scenario=${scenarioName} sections=${scenario.sectionCount}`)
const runs = []
for (let index = 1; index <= options.iterations; index += 1) {
const run = await runIteration({ baseUrl, browser, index, scenario, scenarioName })
runs.push(run)
console.log(
`[perf] ${scenarioName} run=${index} `
+ `visible=${round(run.editorVisibleMs)}ms `
+ `first=${round(run.firstContentMs)}ms `
+ `full=${round(run.fullAppliedMs)}ms `
+ `edit=${round(median(run.editFrameMs))}ms`,
)
}
summaries[scenarioName] = summarizeScenario(scenarioName, scenario, runs)
}
} finally {
await browser.close()
}
return summaries
}
const startedBaseUrl = await startDevServer()
try {
const summaries = await runBenchmarks(startedBaseUrl)
const thresholds = await readThresholds(thresholdsPath)
const activeThresholds = options.update ? updateThresholds(thresholds, summaries) : thresholds
printSummary({ metricLabels, summaries, thresholds: activeThresholds })
if (options.update) {
await writeThresholds(thresholdsPath, activeThresholds)
console.log(`\nUpdated ${thresholdsPath}`)
}
const failures = thresholdFailures(activeThresholds, summaries)
let exitCode = 0
if (failures.length > 0) {
printThresholdFailures({ failures, metricLabels })
exitCode = 1
}
await rm(resolve(rootDir, 'test-results'), { recursive: true, force: true })
if (exitCode !== 0) process.exit(exitCode)
} finally {
stopDevServer()
}

View file

@ -0,0 +1,113 @@
import console from 'node:console'
import { existsSync } from 'node:fs'
import { readFile, writeFile } from 'node:fs/promises'
const thresholdDescription = 'Ratcheted editor performance budgets for synthetic small and large note opens. Lower is better; maxMs values should only move down unless intentionally rebaselined.'
export async function readThresholds(thresholdsPath) {
if (!existsSync(thresholdsPath)) {
return { scenarios: {}, version: 1 }
}
return JSON.parse(await readFile(thresholdsPath, 'utf8'))
}
export async function writeThresholds(thresholdsPath, thresholds) {
await writeFile(thresholdsPath, `${JSON.stringify(thresholds, null, 2)}\n`)
}
export function updateThresholds(thresholds, summaries) {
const next = {
...thresholds,
description: thresholdDescription,
scenarios: { ...thresholds.scenarios },
updatedAt: new Date().toISOString(),
version: 1,
}
for (const [scenarioName, summary] of Object.entries(summaries)) {
next.scenarios[scenarioName] = updatedScenarioThreshold(thresholds, scenarioName, summary)
}
return next
}
export function thresholdFailures(thresholds, summaries) {
return Object.entries(summaries).flatMap(([scenarioName, summary]) => (
scenarioThresholdFailures(thresholds, scenarioName, summary)
))
}
export function printSummary({ metricLabels, summaries, thresholds, writeLine = console.log }) {
for (const [scenarioName, summary] of Object.entries(summaries)) {
writeLine(`\n${scenarioName} (${summary.contentBytes} bytes, ${summary.sectionCount} sections)`)
for (const [metricName, value] of currentMetricEntries(summary)) {
writeLine(summaryMetricLine({
label: metricLabels[metricName] ?? metricName,
maxMs: thresholds.scenarios?.[scenarioName]?.metrics?.[metricName]?.maxMs,
value,
}))
}
}
}
export function printThresholdFailures({ failures, metricLabels, writeLine = console.error }) {
writeLine('\nEditor performance thresholds failed:')
for (const failure of failures) {
const label = metricLabels[failure.metricName] ?? failure.metricName
writeLine(` ${failure.scenarioName} ${label}: ${failure.value}ms > ${failure.maxMs}ms`)
}
}
function updatedScenarioThreshold(thresholds, scenarioName, summary) {
const previousScenario = thresholds.scenarios?.[scenarioName] ?? {}
return {
contentBytes: summary.contentBytes,
metrics: updatedMetricThresholds(previousScenario.metrics ?? {}, summary),
sectionCount: summary.sectionCount,
}
}
function updatedMetricThresholds(previousMetrics, summary) {
return Object.fromEntries(currentMetricEntries(summary).map(([metricName, value]) => [
metricName,
{
baselineMs: value,
maxMs: ratchetedMax(metricName, previousMetrics[metricName], value),
},
]))
}
function currentMetricEntries(summary) {
return Object.entries(summary.medians)
.filter(([, value]) => typeof value === 'number' && Number.isFinite(value))
}
function ratchetedMax(metricName, existingMetric, value) {
const observedBudget = metricName === 'editFrameMs'
? Math.ceil(Math.max(value * 2.5, value + 8, 16))
: Math.ceil(Math.max(value * 1.35, value + 25))
if (!existingMetric?.maxMs) return observedBudget
return Math.min(existingMetric.maxMs, observedBudget)
}
function scenarioThresholdFailures(thresholds, scenarioName, summary) {
return currentMetricEntries(summary)
.map(([metricName, value]) => metricFailure(thresholds, scenarioName, metricName, value))
.filter(Boolean)
}
function metricFailure(thresholds, scenarioName, metricName, value) {
const maxMs = thresholds.scenarios?.[scenarioName]?.metrics?.[metricName]?.maxMs
if (typeof maxMs !== 'number' || value <= maxMs) return null
return {
maxMs,
metricName,
scenarioName,
value,
}
}
function summaryMetricLine({ label, maxMs, value }) {
const suffix = typeof maxMs === 'number' ? ` / max ${maxMs}ms` : ''
return ` ${label.padEnd(24)} ${String(value).padStart(6)}ms${suffix}`
}

View file

@ -0,0 +1,3 @@
import { rename } from 'node:fs/promises'
await rename('dist/team-foundation.html', 'dist/index.html')

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,37 @@
#!/usr/bin/env node
import { readFile } from 'node:fs/promises'
import { pathToFileURL } from 'node:url'
const SEMVER = /^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/
export async function resolveInternalReleaseVersion(configPath, explicitVersion = '') {
const config = JSON.parse(await readFile(configPath, 'utf8'))
const configuredVersion = String(config.version ?? '').trim()
if (!SEMVER.test(configuredVersion)) {
throw new Error(`tauri.conf.json must contain a valid semantic version; received: ${configuredVersion || '<empty>'}`)
}
const requestedVersion = explicitVersion.trim()
if (requestedVersion && requestedVersion !== configuredVersion) {
throw new Error(`Requested version ${requestedVersion} does not match configured version ${configuredVersion}`)
}
return configuredVersion
}
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
const configPath = process.argv[2]
if (!configPath) {
console.error('Usage: node scripts/internal-release-version.mjs <tauri.conf.json> [expected-version]')
process.exit(2)
}
try {
console.log(await resolveInternalReleaseVersion(configPath, process.argv[3] ?? ''))
} catch (error) {
console.error(error instanceof Error ? error.message : String(error))
process.exit(1)
}
}

View file

@ -0,0 +1,41 @@
import assert from 'node:assert/strict'
import { mkdtemp, writeFile } from 'node:fs/promises'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
import test from 'node:test'
import { resolveInternalReleaseVersion } from './internal-release-version.mjs'
test('reads the version from tauri.conf.json', async () => {
const root = await mkdtemp(join(tmpdir(), 'hololake-release-version-'))
await writeFile(join(root, 'tauri.conf.json'), JSON.stringify({ version: '1.2.3' }))
assert.equal(await resolveInternalReleaseVersion(join(root, 'tauri.conf.json')), '1.2.3')
})
test('accepts an explicit matching version', async () => {
const root = await mkdtemp(join(tmpdir(), 'hololake-release-version-'))
await writeFile(join(root, 'tauri.conf.json'), JSON.stringify({ version: '1.2.3' }))
assert.equal(await resolveInternalReleaseVersion(join(root, 'tauri.conf.json'), '1.2.3'), '1.2.3')
})
test('rejects a version that differs from the application configuration', async () => {
const root = await mkdtemp(join(tmpdir(), 'hololake-release-version-'))
await writeFile(join(root, 'tauri.conf.json'), JSON.stringify({ version: '1.2.3' }))
await assert.rejects(
resolveInternalReleaseVersion(join(root, 'tauri.conf.json'), '1.2.4'),
/does not match configured version 1\.2\.3/,
)
})
test('rejects malformed configured versions', async () => {
const root = await mkdtemp(join(tmpdir(), 'hololake-release-version-'))
await writeFile(join(root, 'tauri.conf.json'), JSON.stringify({ version: 'tomorrow' }))
await assert.rejects(
resolveInternalReleaseVersion(join(root, 'tauri.conf.json')),
/valid semantic version/,
)
})

View file

@ -0,0 +1,38 @@
#!/usr/bin/env node
import { spawn } from 'node:child_process'
import { tmpdir } from 'node:os'
import { join } from 'node:path'
const port = process.argv[2] ?? process.env.PORT ?? '41741'
const viteCacheDir = process.env.TOLARIA_VITE_CACHE_DIR ?? join(tmpdir(), `tolaria-vite-smoke-${port}`)
const child = spawn(
'pnpm',
['dev', '--host', '127.0.0.1', '--port', port, '--strictPort'],
{
cwd: process.cwd(),
env: {
...process.env,
TOLARIA_VITE_CACHE_DIR: viteCacheDir,
},
stdio: ['pipe', 'inherit', 'inherit'],
},
)
function forwardSignal(signal) {
if (child.killed) return
child.kill(signal)
}
process.on('SIGINT', () => forwardSignal('SIGINT'))
process.on('SIGTERM', () => forwardSignal('SIGTERM'))
child.on('exit', (code, signal) => {
if (signal) {
process.kill(process.pid, signal)
return
}
process.exit(code ?? 1)
})

View file

@ -0,0 +1,200 @@
#!/usr/bin/env node
import { spawn } from 'node:child_process'
import console from 'node:console'
import { readFile, rm } from 'node:fs/promises'
import { createRequire } from 'node:module'
import os from 'node:os'
import process from 'node:process'
import { resolve } from 'node:path'
const rootDir = process.cwd()
const forwardedArgs = process.argv.slice(2)
const totalShards = positiveInteger(process.env.FRONTEND_COVERAGE_SHARDS ?? '2', 'FRONTEND_COVERAGE_SHARDS')
const concurrency = positiveInteger(
process.env.FRONTEND_COVERAGE_CONCURRENCY ?? String(totalShards),
'FRONTEND_COVERAGE_CONCURRENCY',
)
const runId = `${Date.now()}-${process.pid}`
const shardRoot = resolve(os.tmpdir(), 'tolaria-vitest-coverage-shards', runId)
const finalCoverageDir = resolve(rootDir, 'coverage')
const coverageRequire = createCoverageRequire()
const { createCoverageMap } = coverageRequire('istanbul-lib-coverage')
const libReport = coverageRequire('istanbul-lib-report')
const reports = coverageRequire('istanbul-reports')
const thresholdPercent = Number(process.env.VITEST_COVERAGE_THRESHOLD ?? '70')
const thresholds = {
lines: metricThreshold('LINES'),
functions: metricThreshold('FUNCTIONS'),
branches: metricThreshold('BRANCHES'),
statements: metricThreshold('STATEMENTS'),
}
function positiveInteger(value, name) {
if (/^[1-9][0-9]*$/.test(value)) {
return Number(value)
}
console.error(`${name} must be a positive integer`)
process.exit(2)
}
function metricThreshold(metricName) {
const value = process.env[`VITEST_COVERAGE_${metricName}_THRESHOLD`]
return value === undefined ? thresholdPercent : Number(value)
}
function createCoverageRequire() {
const require = createRequire(import.meta.url)
const coveragePackagePath = require.resolve('@vitest/coverage-v8/package.json')
return createRequire(coveragePackagePath)
}
function shardLabel(shardIndex) {
return `${shardIndex}/${totalShards}`
}
function shardCoverageDir(shardIndex) {
return resolve(shardRoot, `shard-${shardIndex}`)
}
async function clearVitestCache() {
const exitCode = await spawnCommand('clear-cache', 'pnpm', ['exec', 'vitest', '--clearCache'], process.env)
if (exitCode !== 0) {
throw new Error(`Vitest cache clear failed with exit code ${exitCode}`)
}
}
function runShard(shardIndex) {
const env = {
...process.env,
VITEST_COVERAGE_FINAL_DIR: shardCoverageDir(shardIndex),
VITEST_COVERAGE_SHARD: shardLabel(shardIndex),
VITEST_COVERAGE_SKIP_CLEAR_CACHE: '1',
VITEST_COVERAGE_SKIP_THRESHOLDS: '1',
}
return spawnCommand(
`coverage-${shardIndex}`,
process.execPath,
['scripts/run-vitest-coverage.mjs', '--coverage.reporter=json', ...forwardedArgs],
env,
)
}
function spawnCommand(name, command, args, env) {
return new Promise((resolveExit, rejectExit) => {
console.log(`[${name}] started`)
const child = spawn(command, args, {
cwd: rootDir,
env,
stdio: ['inherit', 'pipe', 'pipe'],
})
child.stdout?.on('data', (chunk) => process.stdout.write(`[${name}] ${chunk}`))
child.stderr?.on('data', (chunk) => process.stderr.write(`[${name}] ${chunk}`))
child.on('error', rejectExit)
child.on('exit', (code, signal) => {
if (signal) {
rejectExit(new Error(`${name} exited via signal: ${signal}`))
return
}
const exitCode = code ?? 1
console.log(`[${name}] exited with status ${exitCode}`)
resolveExit(exitCode)
})
})
}
async function runShardBatch(firstShard) {
const shardIndexes = []
for (
let shardIndex = firstShard;
shardIndex <= totalShards && shardIndexes.length < concurrency;
shardIndex += 1
) {
shardIndexes.push(shardIndex)
}
const results = await Promise.all(shardIndexes.map((shardIndex) => runShard(shardIndex)))
return results.every((exitCode) => exitCode === 0)
}
async function runShards() {
let firstShard = 1
while (firstShard <= totalShards) {
if (!(await runShardBatch(firstShard))) {
console.error(`Coverage shard artifacts preserved at ${shardRoot}`)
process.exit(1)
}
firstShard += concurrency
}
}
async function readShardCoverage(shardIndex) {
const coveragePath = resolve(shardCoverageDir(shardIndex), 'coverage-final.json')
return JSON.parse(await readFile(coveragePath, 'utf8'))
}
async function mergeCoverage() {
const coverageMap = createCoverageMap({})
for (let shardIndex = 1; shardIndex <= totalShards; shardIndex += 1) {
coverageMap.merge(await readShardCoverage(shardIndex))
}
return coverageMap
}
async function writeCoverageReports(coverageMap) {
await rm(finalCoverageDir, { recursive: true, force: true })
const context = libReport.createContext({
coverageMap,
dir: finalCoverageDir,
})
for (const reportName of ['text', 'json', 'html', 'lcov']) {
reports.create(reportName).execute(context)
}
}
function printCoverageSummary(summary) {
for (const metric of ['lines', 'functions', 'branches', 'statements']) {
const item = summary[metric]
console.log(
`${metric.padEnd(10)} ${String(item.pct).padStart(6)}% `
+ `(${item.covered}/${item.total}, threshold ${thresholds[metric]}%)`,
)
}
}
function checkCoverageThresholds(coverageMap) {
const summary = coverageMap.getCoverageSummary().toJSON()
printCoverageSummary(summary)
const failures = Object.entries(thresholds)
.filter(([metric, threshold]) => summary[metric].pct < threshold)
if (failures.length === 0) {
return
}
for (const [metric, threshold] of failures) {
console.error(
`Coverage for ${metric} (${summary[metric].pct}%) does not meet threshold ${threshold}%`,
)
}
process.exit(1)
}
await clearVitestCache()
await runShards()
const coverageMap = await mergeCoverage()
await writeCoverageReports(coverageMap)
checkCoverageThresholds(coverageMap)
await rm(shardRoot, { recursive: true, force: true })

View file

@ -0,0 +1,203 @@
#!/usr/bin/env node
import { cp, mkdir, rm } from 'node:fs/promises'
import console from 'node:console'
import os from 'node:os'
import process from 'node:process'
import { resolve } from 'node:path'
import { spawn } from 'node:child_process'
const rootDir = process.cwd()
const finalCoverageDir = resolve(rootDir, process.env.VITEST_COVERAGE_FINAL_DIR ?? 'coverage')
const coverageRunRoot = resolve(os.tmpdir(), 'tolaria-vitest-coverage-runs')
const forwardedArgs = process.argv.slice(2)
const hasFileParallelismOverride = forwardedArgs.some((arg) =>
arg === '--fileParallelism' || arg === '--no-file-parallelism'
)
const hasMaxWorkersOverride = forwardedArgs.some((arg) =>
arg === '--maxWorkers' || arg.startsWith('--maxWorkers=')
)
const hasShardOverride = forwardedArgs.some((arg) =>
arg === '--shard' || arg.startsWith('--shard=')
)
const defaultMaxWorkers = resolveDefaultMaxWorkers()
const coverageShard = process.env.VITEST_COVERAGE_SHARD?.trim() ?? ''
const skipCoverageThresholds = process.env.VITEST_COVERAGE_SKIP_THRESHOLDS === '1'
const skipClearCache = process.env.VITEST_COVERAGE_SKIP_CLEAR_CACHE === '1'
const maxAttempts = 2
// Standalone pnpm installs ship a native binary, so npm_execpath points at
// a Mach-O/ELF executable. Only reuse process.execPath (node) when the path
// is something node can actually load as a module.
const packageManagerExec = process.env.npm_execpath
const isJsExecpath = packageManagerExec && /\.[mc]?js$/i.test(packageManagerExec)
const command = isJsExecpath ? process.execPath : 'pnpm'
const baseCommandArgs = isJsExecpath
? [packageManagerExec, 'exec', 'vitest', 'run', '--coverage']
: ['exec', 'vitest', 'run', '--coverage']
const clearCacheCommandArgs = isJsExecpath
? [packageManagerExec, 'exec', 'vitest', '--clearCache']
: ['exec', 'vitest', '--clearCache']
function isKnownVitestInternalStateFlake(output) {
return output.includes('Vitest failed to access its internal state.')
&& /Test Files\s+\d+\s+passed\s+\(\d+\)/.test(output)
&& /Tests\s+\d+\s+passed\s+\(\d+\)/.test(output)
}
function appendCapturedOutput(output, chunk) {
const nextOutput = output + chunk
return nextOutput.length > 200_000 ? nextOutput.slice(-200_000) : nextOutput
}
function resolveDefaultMaxWorkers() {
const value = process.env.VITEST_COVERAGE_MAX_WORKERS?.trim()
if (!value) {
return '4'
}
if (/^[1-9][0-9]*%?$/.test(value)) {
return value
}
console.warn(`Ignoring invalid VITEST_COVERAGE_MAX_WORKERS=${JSON.stringify(value)}; using 4`)
return '4'
}
function isValidCoverageShard(value) {
return /^[1-9][0-9]*\/[1-9][0-9]*$/.test(value)
}
function coverageThresholdOverrideArgs() {
return [
'--coverage.thresholds.lines=0',
'--coverage.thresholds.functions=0',
'--coverage.thresholds.branches=0',
'--coverage.thresholds.statements=0',
]
}
async function runCoverageAttempt(attempt) {
const runId = `${Date.now()}-${process.pid}-${attempt}`
const runCoverageDir = resolve(coverageRunRoot, runId)
const runCoverageTempDir = resolve(runCoverageDir, '.tmp')
await mkdir(runCoverageDir, { recursive: true })
// Vitest writes per-worker coverage shards under reportsDirectory/.tmp.
await mkdir(runCoverageTempDir, { recursive: true })
if (!skipClearCache) {
await clearVitestCache()
}
const commandArgs = [
...baseCommandArgs,
// Keep coverage fast enough for CI while avoiding the unbounded worker
// contention that makes a few DOM-heavy suites time out under full
// file parallelism. Callers can still opt into serial or wider runs.
...(hasFileParallelismOverride ? [] : ['--fileParallelism']),
...(hasMaxWorkersOverride ? [] : [`--maxWorkers=${defaultMaxWorkers}`]),
...(coverageShard && !hasShardOverride ? [`--shard=${coverageShard}`] : []),
...(skipCoverageThresholds ? coverageThresholdOverrideArgs() : []),
`--coverage.reportsDirectory=${runCoverageDir}`,
...forwardedArgs,
]
let output = ''
const exitCode = await new Promise((resolveExit, rejectExit) => {
const child = spawn(command, commandArgs, {
cwd: rootDir,
env: {
...process.env,
VITEST_COVERAGE_DIR: runCoverageDir,
},
stdio: ['inherit', 'pipe', 'pipe'],
})
const handleOutput = (stream, target) => {
if (!stream) return
stream.setEncoding('utf8')
stream.on('data', (chunk) => {
target.write(chunk)
output = appendCapturedOutput(output, chunk)
})
}
handleOutput(child.stdout, process.stdout)
handleOutput(child.stderr, process.stderr)
child.on('error', rejectExit)
child.on('exit', (code, signal) => {
if (signal) {
rejectExit(new Error(`Vitest coverage exited via signal: ${signal}`))
return
}
resolveExit(code ?? 1)
})
})
return {
exitCode,
output,
runCoverageDir,
}
}
async function clearVitestCache() {
const exitCode = await new Promise((resolveExit, rejectExit) => {
const child = spawn(command, clearCacheCommandArgs, {
cwd: rootDir,
env: process.env,
stdio: 'inherit',
})
child.on('error', rejectExit)
child.on('exit', (code, signal) => {
if (signal) {
rejectExit(new Error(`Vitest cache clear exited via signal: ${signal}`))
return
}
resolveExit(code ?? 1)
})
})
if (exitCode !== 0) {
throw new Error(`Vitest cache clear failed with exit code ${exitCode}`)
}
}
if (coverageShard && !isValidCoverageShard(coverageShard)) {
console.error(`Invalid VITEST_COVERAGE_SHARD=${JSON.stringify(coverageShard)}; expected index/total`)
process.exit(2)
}
let finalRun = null
for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
const run = await runCoverageAttempt(attempt)
finalRun = run
if (run.exitCode === 0) {
await rm(finalCoverageDir, { recursive: true, force: true })
await cp(run.runCoverageDir, finalCoverageDir, {
force: true,
recursive: true,
})
await rm(run.runCoverageDir, { recursive: true, force: true })
process.exit(0)
}
// Retry once when Vitest itself flakes after a fully passing suite.
if (attempt < maxAttempts && isKnownVitestInternalStateFlake(run.output)) {
console.error(`Vitest hit a known internal-state teardown flake on attempt ${attempt}; retrying once...`)
await rm(run.runCoverageDir, { recursive: true, force: true })
continue
}
break
}
console.error(`Vitest coverage artifacts preserved at ${finalRun.runCoverageDir}`)
process.exit(finalRun.exitCode)

View file

@ -0,0 +1,284 @@
#!/usr/bin/env node
/**
* Production static server for Laputa App demo.
* Serves dist/ + handles /api/vault/* routes for browser testing.
*/
import http from 'http'
import { log } from 'console'
import {
closeSync,
createReadStream,
fstatSync,
openSync,
opendirSync,
readFileSync,
} from 'fs'
import path from 'path'
import { fileURLToPath, URL } from 'url'
import matter from 'gray-matter'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
const DIST_DIR = path.join(__dirname, '..', 'dist')
const REPO_DIR = path.resolve(__dirname, '..')
const PORT = 5173
const DEDICATED_FRONTMATTER_KEYS = new Set([
'aliases',
'Is A',
'Belongs to',
'Related to',
'Status',
'Owner',
'Cadence',
'Created at',
])
function isAllowedPath(p) {
return isInsideRelativePath(path.relative(REPO_DIR, p))
}
function isInsideRelativePath(relative) {
return relative === '' || (!relative.startsWith('..') && !path.isAbsolute(relative))
}
function resolveInside(root, target) {
const normalizedTarget = path.normalize(target)
if (path.isAbsolute(normalizedTarget)) return null
const candidate = path.normalize(`${root}${path.sep}${normalizedTarget}`)
return isInsideRelativePath(path.relative(root, candidate)) ? candidate : null
}
function readUtf8File(filePath) {
const fd = openSync(filePath, 'r')
try {
return readFileSync(fd, 'utf-8')
} finally {
closeSync(fd)
}
}
function pathStats(filePath) {
const fd = openSync(filePath, 'r')
try {
return fstatSync(fd)
} finally {
closeSync(fd)
}
}
function pathExists(filePath) {
try {
pathStats(filePath)
return true
} catch {
return false
}
}
function directoryEntries(dir) {
const directory = opendirSync(dir)
try {
const entries = []
let entry = directory.readSync()
while (entry) {
entries.push(entry)
entry = directory.readSync()
}
return entries
} finally {
directory.closeSync()
}
}
function streamFile(filePath) {
const fd = openSync(filePath, 'r')
return createReadStream(null, { fd, autoClose: true })
}
function staticAssetPath(url) {
const pathname = new URL(url, 'http://localhost').pathname
const requested = pathname === '/' ? 'index.html' : decodeURIComponent(pathname).replace(/^\/+/, '')
return resolveInside(DIST_DIR, requested) ?? path.normalize(`${DIST_DIR}${path.sep}index.html`)
}
const MIME = {
'.html': 'text/html',
'.js': 'application/javascript',
'.css': 'text/css',
'.svg': 'image/svg+xml',
'.png': 'image/png',
'.ico': 'image/x-icon',
'.woff2':'font/woff2',
'.json': 'application/json',
}
function findMarkdownFiles(dir) {
const results = []
try {
for (const entry of directoryEntries(dir)) {
const full = resolveInside(dir, entry.name)
if (!full) continue
if (entry.isDirectory()) results.push(...findMarkdownFiles(full))
else if (entry.name.endsWith('.md')) results.push(full)
}
} catch {}
return results
}
function extractWikiLinks(value) {
if (!value) return []
const str = Array.isArray(value) ? value.join(' ') : String(value)
return [...str.matchAll(/\[\[([^\]]+)\]\]/g)].map(m => `[[${m[1]}]]`)
}
function frontmatterRelationships(frontmatter) {
const relationships = {}
for (const [key, value] of Object.entries(frontmatter)) {
if (DEDICATED_FRONTMATTER_KEYS.has(key)) continue
const links = extractWikiLinks(value)
if (links.length) relationships[key] = links
}
return relationships
}
function aliasesFrom(frontmatter) {
if (Array.isArray(frontmatter.aliases)) return frontmatter.aliases
return frontmatter.aliases ? [frontmatter.aliases] : []
}
function markdownBodyText(content) {
return content.replace(/---[\s\S]*?---/, '').trim()
}
function markdownTitle(bodyText, aliases, filePath) {
const h1 = bodyText.match(/^#\s+(.+)/m)?.[1]
return h1 || aliases[0] || path.basename(filePath, '.md')
}
function createdAtMillis(frontmatter) {
return frontmatter['Created at'] ? new Date(frontmatter['Created at']).getTime() : null
}
function snippetFrom(bodyText) {
return bodyText
.replace(/^#+\s+.+/gm, '')
.replace(/\n+/g, ' ')
.trim()
.slice(0, 200)
}
function parseMarkdownFile(filePath) {
try {
const raw = readUtf8File(filePath)
const { data: fm, content } = matter(raw)
const stat = pathStats(filePath)
const bodyText = markdownBodyText(content)
const aliases = aliasesFrom(fm)
return {
path: filePath,
filename: path.basename(filePath),
title: markdownTitle(bodyText, aliases, filePath),
isA: fm['Is A'] ?? null,
aliases,
belongsTo: extractWikiLinks(fm['Belongs to']),
relatedTo: extractWikiLinks(fm['Related to']),
status: fm['Status'] ?? null,
owner: fm['Owner'] ?? null,
cadence: fm['Cadence'] ?? null,
modifiedAt: stat.mtimeMs,
createdAt: createdAtMillis(fm),
fileSize: stat.size,
snippet: snippetFrom(bodyText),
relationships: frontmatterRelationships(fm),
}
} catch { return null }
}
function sendJson(res, statusCode, payload) {
res.writeHead(statusCode, { 'Content-Type': 'application/json' })
res.end(JSON.stringify(payload))
}
function badPath(res) {
sendJson(res, 400, { error: 'bad path' })
}
function existingAllowedPath(params) {
const requestedPath = params.searchParams.get('path')
return requestedPath && isAllowedPath(requestedPath) && pathExists(requestedPath)
? requestedPath
: null
}
function handleVaultPing(_params, res) {
sendJson(res, 200, { ok: true })
}
function handleVaultList(params, res) {
const dir = existingAllowedPath(params)
if (!dir) return badPath(res)
const entries = findMarkdownFiles(dir).map(parseMarkdownFile).filter(Boolean)
sendJson(res, 200, entries)
}
function handleVaultContent(params, res) {
const file = existingAllowedPath(params)
if (!file) return badPath(res)
sendJson(res, 200, { content: readUtf8File(file) })
}
function allVaultContent(dir) {
const map = {}
for (const filePath of findMarkdownFiles(dir)) {
try { map[filePath] = readUtf8File(filePath) } catch {}
}
return map
}
function handleVaultAllContent(params, res) {
const dir = existingAllowedPath(params)
if (!dir) return badPath(res)
sendJson(res, 200, allVaultContent(dir))
}
const VAULT_API_ROUTES = new Map([
['/api/vault/ping', handleVaultPing],
['/api/vault/list', handleVaultList],
['/api/vault/content', handleVaultContent],
['/api/vault/all-content', handleVaultAllContent],
])
function serveVaultApi(url, res) {
const params = new URL(url, 'http://localhost')
const handler = VAULT_API_ROUTES.get(params.pathname)
if (!handler) return false
handler(params, res)
return true
}
const server = http.createServer((req, res) => {
const url = req.url ?? '/'
// API routes
if (url.startsWith('/api/vault/')) {
if (!serveVaultApi(url, res)) {
res.writeHead(404); res.end()
}
return
}
// Static files
let filePath = staticAssetPath(url)
if (!pathExists(filePath) || pathStats(filePath).isDirectory()) {
filePath = path.normalize(`${DIST_DIR}${path.sep}index.html`) // SPA fallback
}
const ext = path.extname(filePath)
res.writeHead(200, { 'Content-Type': MIME[ext] ?? 'application/octet-stream' })
streamFile(filePath).pipe(res)
})
server.listen(PORT, '0.0.0.0', () => {
log(`✅ Laputa demo server running on http://0.0.0.0:${PORT}`)
log(` Tailscale: https://mac-mini.tail7cbc15.ts.net`)
})

View file

@ -0,0 +1,149 @@
import {
closeSync, fstatSync, openSync, opendirSync, readFileSync,
} from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
const localesDir = path.join(root, 'src/lib/locales')
const sourcePath = path.join(localesDir, 'en.json')
function readCatalog(filePath) {
return JSON.parse(readUtf8File(filePath))
}
function readUtf8File(filePath) {
const fd = openSync(filePath, 'r')
try {
return readFileSync(fd, 'utf8')
} finally {
closeSync(fd)
}
}
function directoryFiles(dirPath) {
const dir = opendirSync(dirPath)
try {
const files = []
let entry = dir.readSync()
while (entry) {
if (entry.isFile()) files.push(entry.name)
entry = dir.readSync()
}
return files
} finally {
dir.closeSync()
}
}
function ensureDirectory(dirPath) {
const fd = openSync(dirPath, 'r')
try {
if (!fstatSync(fd).isDirectory()) {
throw new Error(`${dirPath} is not a directory`)
}
} finally {
closeSync(fd)
}
}
function isFlatObject(value) {
if (!value) return false
if (typeof value !== 'object') return false
return !Array.isArray(value)
}
function assertFlatStringCatalog(locale, catalog) {
if (!isFlatObject(catalog)) {
throw new Error(`${locale}: expected a flat object of translation keys`)
}
for (const [key, value] of Object.entries(catalog)) {
if (typeof value !== 'string') {
throw new Error(`${locale}: key "${key}" must map to a string`)
}
}
}
function missingKeys(sourceKeys, localeKeys) {
const localeKeySet = new Set(localeKeys)
return sourceKeys.filter((key) => !localeKeySet.has(key))
}
function extraKeys(sourceKeys, localeKeys) {
const sourceKeySet = new Set(sourceKeys)
return localeKeys.filter((key) => !sourceKeySet.has(key))
}
function placeholders(value) {
return Array.from(value.matchAll(/\{(\w+)\}/g), (match) => match[1]).sort()
}
function sameValues(left, right) {
if (left.length !== right.length) return false
return left.every((value, index) => value === right[index])
}
function formatValues(values) {
return values.length === 0 ? 'none' : values.join(', ')
}
function placeholderIssues(locale, sourceCatalog, catalog) {
const issues = []
for (const [key, sourceValue] of Object.entries(sourceCatalog)) {
if (!(key in catalog)) continue
const sourcePlaceholders = placeholders(sourceValue)
const localePlaceholders = placeholders(catalog[key])
if (sameValues(sourcePlaceholders, localePlaceholders)) continue
issues.push(
`${locale}: key "${key}" placeholders differ ` +
`(expected ${formatValues(sourcePlaceholders)}, found ${formatValues(localePlaceholders)})`,
)
}
return issues
}
const sourceCatalog = readCatalog(sourcePath)
assertFlatStringCatalog('en', sourceCatalog)
const sourceKeys = Object.keys(sourceCatalog).sort()
ensureDirectory(localesDir)
const localeFiles = directoryFiles(localesDir).filter((file) => file.endsWith('.json'))
const issues = []
for (const file of localeFiles) {
const locale = file.replace(/\.json$/, '')
const filePath = path.join(localesDir, file)
const catalog = readCatalog(filePath)
assertFlatStringCatalog(locale, catalog)
if (locale === 'en') continue
const keys = Object.keys(catalog).sort()
const missing = missingKeys(sourceKeys, keys)
const extra = extraKeys(sourceKeys, keys)
if (missing.length > 0) {
issues.push(`${locale}: missing ${missing.length} key(s)`)
}
if (extra.length > 0) {
issues.push(`${locale}: extra ${extra.length} key(s)`)
}
issues.push(...placeholderIssues(locale, sourceCatalog, catalog))
}
if (issues.length > 0) {
console.error('Locale validation failed:')
for (const issue of issues) {
console.error(`- ${issue}`)
}
process.exit(1)
}
console.log(`Validated ${localeFiles.length} locale catalog(s) against ${sourceKeys.length} English keys.`)

View file

@ -0,0 +1,54 @@
#!/usr/bin/env node
import { readFile, stat } from 'node:fs/promises'
import { basename } from 'node:path'
const FORBIDDEN = [
'guanghubingshuo.com',
]
const REQUIRED = [
'guanghulab.com/fifth-domain/bingshuo/hololake-platform',
'GLS-SYS-ARCH-001',
]
const targets = process.argv.slice(2)
if (targets.length === 0) {
console.error('Usage: pnpm verify:internal-package <unpacked-app-file-or-directory> [...]')
process.exit(2)
}
async function collectFiles(target) {
const info = await stat(target)
if (info.isFile()) return [target]
const { readdir } = await import('node:fs/promises')
const entries = await readdir(target, { withFileTypes: true })
return (await Promise.all(entries.map((entry) =>
collectFiles(`${target}/${entry.name}`),
))).flat()
}
const matches = new Map(REQUIRED.map((needle) => [needle, false]))
for (const target of targets) {
for (const file of await collectFiles(target)) {
const contents = await readFile(file)
const text = contents.toString('latin1')
for (const forbidden of FORBIDDEN) {
if (text.includes(forbidden)) {
console.error(`Forbidden private route found in ${file}: ${forbidden}`)
process.exit(1)
}
}
for (const required of REQUIRED) {
if (text.includes(required)) matches.set(required, true)
}
}
}
const missing = [...matches].filter(([, found]) => !found).map(([needle]) => needle)
if (missing.length > 0) {
console.error(`Required public architecture markers missing from ${targets.map((target) => basename(target)).join(', ')}: ${missing.join(', ')}`)
process.exit(1)
}
console.log(`Internal package content verified: ${targets.map((target) => basename(target)).join(', ')}`)

View file

@ -0,0 +1,68 @@
Unicode true
RequestExecutionLevel user
SetCompressor /SOLID lzma
!ifndef APP_VERSION
!error "APP_VERSION is required"
!endif
!ifndef STAGE_DIR
!error "STAGE_DIR is required"
!endif
!ifndef OUTPUT_FILE
!error "OUTPUT_FILE is required"
!endif
Name "HoloLake Era"
OutFile "${OUTPUT_FILE}"
InstallDir "$LOCALAPPDATA\Programs\HoloLake Era"
InstallDirRegKey HKCU "Software\Guanghu\HoloLake Era" "InstallDir"
Icon "${STAGE_DIR}/icon.ico"
UninstallIcon "${STAGE_DIR}/icon.ico"
VIProductVersion "${APP_VERSION}.0"
VIAddVersionKey "ProductName" "HoloLake Era"
VIAddVersionKey "ProductVersion" "${APP_VERSION}"
VIAddVersionKey "FileVersion" "${APP_VERSION}"
VIAddVersionKey "CompanyName" "Guanghu Language World"
VIAddVersionKey "FileDescription" "HoloLake Era internal installer"
VIAddVersionKey "LegalCopyright" "Guanghu Language World"
Page directory
Page instfiles
UninstPage uninstConfirm
UninstPage instfiles
Section "HoloLake Era" SEC_MAIN
SetOutPath "$INSTDIR"
File "${STAGE_DIR}/HoloLake Era.exe"
File "${STAGE_DIR}/icon.ico"
File /r "${STAGE_DIR}/resources"
WriteUninstaller "$INSTDIR\Uninstall.exe"
WriteRegStr HKCU "Software\Guanghu\HoloLake Era" "InstallDir" "$INSTDIR"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\HoloLakeEra" "DisplayName" "HoloLake Era"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\HoloLakeEra" "DisplayVersion" "${APP_VERSION}"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\HoloLakeEra" "DisplayIcon" "$INSTDIR\icon.ico"
WriteRegStr HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\HoloLakeEra" "UninstallString" '"$INSTDIR\Uninstall.exe"'
CreateDirectory "$SMPROGRAMS\HoloLake Era"
CreateShortcut "$SMPROGRAMS\HoloLake Era\HoloLake Era.lnk" "$INSTDIR\HoloLake Era.exe" "" "$INSTDIR\icon.ico"
CreateShortcut "$DESKTOP\HoloLake Era.lnk" "$INSTDIR\HoloLake Era.exe" "" "$INSTDIR\icon.ico"
WriteRegStr HKCU "Software\Classes\tolaria" "" "URL:Tolaria Protocol"
WriteRegStr HKCU "Software\Classes\tolaria" "URL Protocol" ""
WriteRegStr HKCU "Software\Classes\tolaria\shell\open\command" "" '"$INSTDIR\HoloLake Era.exe" "%1"'
WriteRegStr HKCU "Software\Classes\guanghu" "" "URL:Guanghu Protocol"
WriteRegStr HKCU "Software\Classes\guanghu" "URL Protocol" ""
WriteRegStr HKCU "Software\Classes\guanghu\shell\open\command" "" '"$INSTDIR\HoloLake Era.exe" "%1"'
SectionEnd
Section "Uninstall"
Delete "$DESKTOP\HoloLake Era.lnk"
RMDir /r "$SMPROGRAMS\HoloLake Era"
DeleteRegKey HKCU "Software\Classes\tolaria"
DeleteRegKey HKCU "Software\Classes\guanghu"
DeleteRegKey HKCU "Software\Microsoft\Windows\CurrentVersion\Uninstall\HoloLakeEra"
DeleteRegKey HKCU "Software\Guanghu\HoloLake Era"
RMDir /r "$INSTDIR"
SectionEnd