From ac2b5274f4693f1b0c7cca3c26f4cac131ffd0a7 Mon Sep 17 00:00:00 2001 From: IsaacHuo <150095409+IsaacHuo@users.noreply.github.com> Date: Sat, 24 Oct 2026 16:28:20 +0800 Subject: [PATCH] feat(workspace): ship the local research browser preview --- .github/workflows/checks.yml | 21 +- .gitignore | 2 + .gitleaks.toml | 4 +- .markdownlint-cli2.jsonc | 2 +- CHANGELOG.md | 2 + CONTRIBUTING.md | 6 +- CONTRIBUTING.zh-CN.md | 6 +- README.md | 30 +- README.zh-CN.md | 30 +- THIRD_PARTY_NOTICES.md | 8 +- app/ag-ui.ts | 14 +- app/main.ts | 2 + app/openapi.ts | 3 + app/platform.ts | 15 +- app/service.ts | 23 +- app/settings.ts | 5 +- app/skills.ts | 15 +- app/store.ts | 100 +- app/tests/service.test.ts | 9 +- app/tests/settings-skills.test.ts | 8 +- app/tests/store.test.ts | 46 + assets/docs/workspace-preview.png | Bin 0 -> 173429 bytes browser-tests/workspace.spec.ts | 87 + docs/architecture.md | 4 +- docs/development.md | 16 +- docs/implementation-plan.md | 4 +- docs/openapi.json | 212 ++ docs/product.md | 2 +- docs/verification.md | 10 +- docs/workspace.md | 4 +- package-lock.json | 4156 ++++++++++++++++++++++++++++- package.json | 22 +- playwright.config.ts | 6 + scripts/browser-fixture.mjs | 50 + scripts/check-repository.mjs | 5 +- tsconfig.web.json | 8 + vite.config.ts | 3 + web/api.ts | 27 + web/conversation.tsx | 43 + web/diagnostics.tsx | 20 + web/index.html | 13 + web/main.tsx | 5 + web/style.css | 6 + web/ui.tsx | 18 + web/workspace.tsx | 226 ++ 45 files changed, 5110 insertions(+), 188 deletions(-) create mode 100644 assets/docs/workspace-preview.png create mode 100644 browser-tests/workspace.spec.ts create mode 100644 playwright.config.ts create mode 100644 scripts/browser-fixture.mjs create mode 100644 tsconfig.web.json create mode 100644 vite.config.ts create mode 100644 web/api.ts create mode 100644 web/conversation.tsx create mode 100644 web/diagnostics.tsx create mode 100644 web/index.html create mode 100644 web/main.tsx create mode 100644 web/style.css create mode 100644 web/ui.tsx create mode 100644 web/workspace.tsx diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 41a549b..eb06174 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -33,6 +33,23 @@ jobs: - run: npm run check env: PR_TITLE: ${{ github.event.pull_request.title }} + - run: npm run build + + browser: + name: browser acceptance + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + - uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0 + with: + node-version: 24 + cache: npm + - run: npm ci --ignore-scripts + - run: npm run build + - run: npm run test:browser actions: name: actionlint @@ -64,16 +81,18 @@ jobs: required-checks: name: required-checks if: ${{ always() }} - needs: [repository, actions, secrets] + needs: [repository, browser, actions, secrets] runs-on: ubuntu-latest timeout-minutes: 2 steps: - name: Require every check to succeed env: REPOSITORY_RESULT: ${{ needs.repository.result }} + BROWSER_RESULT: ${{ needs.browser.result }} ACTIONS_RESULT: ${{ needs.actions.result }} SECRETS_RESULT: ${{ needs.secrets.result }} run: | test "$REPOSITORY_RESULT" = success + test "$BROWSER_RESULT" = success test "$ACTIONS_RESULT" = success test "$SECRETS_RESULT" = success diff --git a/.gitignore b/.gitignore index 0593264..384f1c2 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,5 @@ dist/ Thumbs.db .openlab/ *.diagnostics.zip +test-results/ +playwright-report/ diff --git a/.gitleaks.toml b/.gitleaks.toml index a2cc8b7..ddd736d 100644 --- a/.gitleaks.toml +++ b/.gitleaks.toml @@ -4,5 +4,5 @@ title = "OpenLab repository secret scanning" useDefault = true [[allowlists]] -description = "Generated dependencies and verified tool caches" -paths = ['''(^|/)node_modules/''', '''(^|/)\.cache/''', '''(^|/)\.git/'''] +description = "Generated dependencies, verified caches and untracked local application/test data" +paths = ['''(^|/)node_modules/''', '''(^|/)\.cache/''', '''(^|/)\.git/''', '''(^|/)\.openlab/''', '''(^|/)dist/''', '''(^|/)test-results/''', '''(^|/)playwright-report/'''] diff --git a/.markdownlint-cli2.jsonc b/.markdownlint-cli2.jsonc index 9491a33..860c0d1 100644 --- a/.markdownlint-cli2.jsonc +++ b/.markdownlint-cli2.jsonc @@ -1,6 +1,6 @@ { "globs": ["**/*.md"], - "ignores": ["node_modules/**", ".cache/**", "docs/vision.zh-CN.md"], + "ignores": ["node_modules/**", ".cache/**", ".openlab/**", "dist/**", "test-results/**", "playwright-report/**", "docs/vision.zh-CN.md"], "config": { "default": true, "MD013": false, diff --git a/CHANGELOG.md b/CHANGELOG.md index c006b5d..e4c3f77 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ Delivered changes are recorded here. Version tags use `v` followed by a semantic ### Added +- Runnable bilingual React/assistant-ui workspace with official AG-UI adapter, projects/history, human decisions, live report revisions, source/step navigation, protected settings, material previews and keyboard-accessible drawers/divider. +- Native Chrome desktop/mobile acceptance and axe checks, a real browser DeepSeek/Crossref metadata research/export record, and native Windows credential module isolation checks. - Authenticated loopback research API and OpenAPI definition, protected provider settings/capability tests, pinned inert skill installation, Crossref metadata/source tools, scoped public-page permissions and persisted AG-UI replay. - Economics/English and humanities/Chinese live DeepSeek/Crossref metadata research, report revision and archive round-trip smoke tests; full-text and browser acceptance remain pending. - Initial TypeScript platform archive, generic single-agent Harness, supervised IPC worker and streaming model adapter, with confirmation, interaction validation, cancellation, restart reconciliation and immutable report archives. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7ec9fc9..b7cad53 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,7 +2,7 @@ [简体中文](CONTRIBUTING.zh-CN.md) -OpenLab currently contains governance, documentation, and repository tooling. The [product specification](docs/product.md) identifies planned application behavior. Contributions should distinguish working functionality from design proposals. +OpenLab contains governance, documentation, repository tooling and a local research preview. The [product specification](docs/product.md) defines the application target; [development](docs/development.md) records implemented scope. Contributions should distinguish working functionality from design proposals. ## Start a contribution @@ -19,12 +19,16 @@ npm ci npm run check npm run check:actions npm run check:secrets +npm run build +npm run test:browser ``` On Windows PowerShell, use `npm.cmd` if script execution policy blocks `npm.ps1`. The workflow and secret checks support Windows x64 and Linux x64 and download fixed, checksum-verified binaries into `.cache/`. They need network access on first use. Windows downloads use the built-in curl, with GitHub CLI as a fallback for networks that block direct release downloads. Other platforms can run the documentation checks and rely on CI for these two binaries until tool manifests add their platform. `npm run check` runs Markdown lint, local-link and repository configuration validation, and automation tests. The original Chinese vision is preserved verbatim and excluded from style lint. External sites are not queried by the link checker because network availability must not block a documentation PR. +Application checks include strict server/browser TypeScript and controlled runtime/storage/API tests. Browser acceptance uses installed Chrome through Playwright against a separate synthetic local service, including desktop/mobile and axe checks. It never needs your model key. Live model/retrieval smoke tests are separate opt-in commands documented in [development](docs/development.md). Build the browser bundle before `npm start`. + ## Pull requests - Use a Conventional Commit PR title, for example `docs: clarify research evidence levels` or `fix(ci): validate release targets`. diff --git a/CONTRIBUTING.zh-CN.md b/CONTRIBUTING.zh-CN.md index 7d32817..806bb77 100644 --- a/CONTRIBUTING.zh-CN.md +++ b/CONTRIBUTING.zh-CN.md @@ -2,7 +2,7 @@ [English](CONTRIBUTING.md) -当前仓库提供项目治理、文档和维护工具,科研应用尚未实现。贡献内容需要清楚区分已经可用的功能与设计方案。 +当前仓库提供项目治理、文档、维护工具和本地科研预览。[产品规格](docs/product.md)定义目标行为,[开发说明](docs/development.md)记录已实现范围。贡献内容需要清楚区分已经可用的功能与设计方案。 ## 开始贡献 @@ -19,12 +19,16 @@ npm.cmd ci npm.cmd run check npm.cmd run check:actions npm.cmd run check:secrets +npm.cmd run build +npm.cmd run test:browser ``` PowerShell 执行策略可能阻止 `npm.ps1`,使用 `npm.cmd` 即可。工作流检查和密钥扫描支持 Windows x64、Linux x64,首次运行需要联网,固定版本工具经 SHA-256 校验后存入 `.cache/`。Windows 使用系统自带的 curl 下载;网络阻止直接下载时回退到 GitHub CLI。其他系统可执行文档检查,暂由 CI 执行这两项工具检查。 `npm run check` 包括 Markdown 格式、内部链接、仓库配置与自动化测试。原始中文愿景逐字保留,不参与风格检查。外部网址不参与本地链接检查,避免网络波动阻塞 PR。 +应用检查包括服务端与浏览器 TypeScript,以及受控运行时、存储和 API 测试。浏览器验收通过 Playwright 使用已安装的 Chrome,连接独立的合成本地服务,覆盖桌面、窄屏和 axe 检查,不使用你的模型凭据。真实模型和检索测试需要单独执行,见[开发说明](docs/development.md)。运行 `npm start` 前先构建浏览器产物。 + ## 提交与审查 - PR 标题采用 Conventional Commits,例如 `docs: clarify research evidence levels`。 diff --git a/README.md b/README.md index 37b36f5..d7697bb 100644 --- a/README.md +++ b/README.md @@ -21,20 +21,36 @@ OpenLab is a local-first research workspace being designed to connect open resea ## Current status -**v0.0.1 establishes repository governance and design documents. There is no runnable research application or installer in this release.** The commands below validate this repository; they do not launch OpenLab. Screenshots and research demonstrations will be added when the corresponding functionality is available. +**The published v0.0.1 release is foundation-only.** Current development source includes a runnable local research preview; no installer is provided. -Current development source adds the platform archive, generic Harness and supervised worker. See [development](docs/development.md) and the [implementation plan](docs/implementation-plan.md) for implemented scope and remaining application gates. +The preview combines projects, a supervised Harness, DeepSeek-tested model configuration, Crossref metadata retrieval, plan confirmation, source-linked Markdown reports and portable research archives. See [development](docs/development.md) and the [implementation plan](docs/implementation-plan.md) for tested scope and remaining gates. | Capability | Status | | --- | --- | | Contribution, issue, review, and release processes | Available in this repository | | Automated documentation, workflow, and secret checks | Available in GitHub Actions and local tooling | -| Local service with browser UI, Windows first | Planned | -| Projects, skill installation, model configuration, and literature research | Planned | -| OpenLab Harness: default single-agent literature runtime | Specified; implementation and native Windows validation planned | +| Local service with browser UI, Windows first | Implemented in development source; desktop/mobile Chrome checked | +| Projects, pinned inert skill installation, model configuration, and metadata research | Implemented; two live DeepSeek/Crossref tasks checked | +| OpenLab Harness: default single-agent literature runtime | Implemented with supervised native Windows worker and replaceable interfaces | | Supervisor-Skills execution through the platform runtime adapter | Planned; not an endorsed or verified integration yet | -| Conversation, execution state, and live artifacts workspace | Specified with assistant-ui and AG-UI; no runnable UI yet | -| Source tracking, execution history, and artifact export | Specified; implementation planned | +| Conversation, execution state, and live artifacts workspace | React/assistant-ui with AG-UI; plan, input, denial and replay checked | +| Source tracking, execution history, and artifact export | Immutable revisions, material copies and verified archive round trips | + +## Run the development preview + +Use Node.js 24 on Windows: + +```sh +npm ci +npm run build +npm start +``` + +Open the launch link printed in the terminal. Add and test your model provider in settings, create a project, and submit a question. Review the plan before research starts. Model planning and execution use your provider account. Windows protects credentials under the current user; saved projects remain readable without provider access. Crossref retrieval needs a network connection. Public-page reading asks for scoped access and can be unavailable; metadata alone does not verify a paper's conclusions. DeepSeek currently uses non-thinking mode. + +![Native Windows preview with a real DeepSeek/Crossref metadata research record](assets/docs/workspace-preview.png) + +This screenshot shows a real metadata-only task, not a full-text or scientific claim-verification result. [Verification details](docs/verification.md) distinguish live probes from synthetic browser fixtures. ## First research release diff --git a/README.zh-CN.md b/README.zh-CN.md index 29f1406..39aeac4 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -1,6 +1,6 @@ # OpenLab -当前开发源码已加入平台档案、通用 Harness 和受监督的 worker。已实现范围、验证结果与剩余应用验收项见[开发说明](docs/development.md)和[实施计划](docs/implementation-plan.md)。v0.0.1 发布范围仍为仓库基础设施。 +当前开发源码已提供可运行的本地科研预览,包括平台档案、通用 Harness、受监督的 worker 和浏览器工作区。已实现范围、验证结果与剩余验收项见[开发说明](docs/development.md)和[实施计划](docs/implementation-plan.md)。已发布的 v0.0.1 仍为仓库基础设施。
@@ -23,18 +23,34 @@ OpenLab 致力于建立本地优先的科研工作空间,将已有的开源科 ## 当前状态 -**v0.0.1 交付仓库规范、协作流程和设计文档,目前没有可运行的科研应用或安装包。** 仓库中的 npm 命令用于校验文档和自动化配置。截图与科研演示会在对应能力实际可用后补充。 +**已发布的 v0.0.1 仅交付仓库规范、协作流程和设计文档。** 当前开发源码已提供本地科研应用预览,尚不提供安装包。 | 能力 | 状态 | | --- | --- | | 贡献指南、Issue/PR 模板与发布流程 | 本仓库提供 | | 文档、工作流与密钥扫描 | GitHub Actions 与本地工具提供 | -| Windows 优先、本地启动、浏览器交互 | 计划实现 | -| 项目管理、技能安装、模型配置与文献研究 | 计划实现 | -| OpenLab Harness:默认单 Agent 文献研究运行时 | 已定义规格,待实现及原生 Windows 验证 | +| Windows 优先、本地启动、浏览器交互 | 已实现,已检查桌面与窄屏 Chrome | +| 项目管理、固定版本技能安装、模型配置与元数据研究 | 已实现,已验证两条 DeepSeek/Crossref 真实研究任务 | +| OpenLab Harness:默认单 Agent 文献研究运行时 | 已实现原生 Windows worker 与可替换接口 | | 通过平台运行时适配器执行 Supervisor-Skills | 计划集成,尚未通过执行验证 | -| 对话、执行状态与实时研究产物工作台 | 已确定 assistant-ui 与 AG-UI 方向,目前没有可运行界面 | -| 来源追踪、运行历史与成果导出 | 已定义规格,待实现 | +| 对话、执行状态与实时研究产物工作台 | React/assistant-ui 与 AG-UI,已验证确认、回答、拒绝与重放 | +| 来源追踪、运行历史与成果导出 | 不可变修订、附件副本与校验后的档案导入导出 | + +## 运行开发预览 + +在 Windows 上安装 Node.js 24,然后执行: + +```sh +npm ci +npm run build +npm start +``` + +打开终端输出的启动链接,在设置中添加并测试模型服务,新建项目后提交研究问题。研究工具会等待你确认计划;生成计划和执行研究会使用你的模型账户。Windows 使用当前用户保护凭据,已保存项目不依赖模型服务即可查看。Crossref 检索需要网络。公开页面读取会请求具体授权,也可能不可用;仅有元数据不能证明论文支持某个结论。当前 DeepSeek 使用非思考模式。 + +![原生 Windows 上真实 DeepSeek/Crossref 元数据研究预览](assets/docs/workspace-preview.png) + +截图展示真实的元数据研究任务,不代表全文或科学论点已经核实。[验证记录](docs/verification.md)分别说明真实测试与合成浏览器夹具。 ## 首个科研版本 diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index 59b5f95..735153c 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -21,6 +21,12 @@ GitHub Actions used by the workflows retain their upstream licenses and are pinn ## Planned application components and design references +### Implemented development dependencies + +Current development pins React/React DOM 19.3.1 (MIT), assistant-ui React 0.15.31 (MIT), its AG-UI adapter 0.1.0 (MIT), AG-UI client/core 1.0.3 (MIT), react-markdown 10.1.0 (MIT), and YAML 2.9.1 (ISC). Build/test tooling adds Vite 8.3.5 (MIT), its React plugin 6.1.2 (MIT), Playwright 1.64.1 (Apache-2.0), and axe-core Playwright 4.14.0 (MPL-2.0). Package license metadata was inspected on 2026-10-24. Transitive versions, integrity hashes and license metadata remain in package-lock.json; installed packages retain their license files. OpenLab's supporting UI components are original code; no shadcn or AI Elements source has been copied. + +Zod is overridden to 4.6.7 to avoid [GHSA-mrhx-c7xc-m3w5](https://github.com/advisories/GHSA-mrhx-c7xc-m3w5). The AG-UI client already imports Zod's v4 API despite a v3 package range. Type checks, the official client and browser flows pass with the override; npm audit reports zero vulnerabilities in the verified lockfile. Review this override with future upstream upgrades. + - [assistant-ui](https://github.com/assistant-ui/assistant-ui) is the planned conversation foundation, with its [official AG-UI adapter](https://www.assistant-ui.com/docs/runtimes/ag-ui/overview). - [AG-UI](https://docs.ag-ui.com/introduction) is the selected frontend interaction protocol. OpenLab retains its own research archive and runtime contracts. - [shadcn/ui AI Elements](https://ui.shadcn.com/docs/ai-elements) provides supporting UI composition patterns; copied component code must retain applicable source and license records. @@ -28,4 +34,4 @@ GitHub Actions used by the workflows retain their upstream licenses and are pinn - OpenCode was the original first-runtime selection in ADR 0001. [ADR 0002](docs/decisions/0002-workspace-and-harness.md) selects self-developed OpenLab Harness and retains OpenCode as an optional future adapter. - DeepSeek Harness is named in the supplied planning material as architectural inspiration. No upstream implementation or license has been independently established in this revision; no code is copied or bundled on that basis. -These application components are not installed or bundled in v0.0.1. The referenced documentation was reviewed on 2026-10-24; exact dependency/component revisions, license terms and compatibility must be recorded before implementation distribution. A design reference does not imply upstream endorsement. +These application components are not installed or bundled in the historical v0.0.1 release. Current development's pinned dependencies and verification are recorded above. A design reference does not imply upstream endorsement. diff --git a/app/ag-ui.ts b/app/ag-ui.ts index d506718..0b87621 100644 --- a/app/ag-ui.ts +++ b/app/ag-ui.ts @@ -1,14 +1,14 @@ import type { ObjectData, ResearchEvent, Run } from './contracts.ts'; // Projection only. No event in this module can start or resume execution. -export function projectEvent(event: ResearchEvent, run: Run): ObjectData[] { +export function projectEvent(event: ResearchEvent, run: Run, history: ResearchEvent[] = []): ObjectData[] { const { kind, data } = event; const timestamp = Date.parse(event.at); const events: ObjectData[] = []; if (kind === 'run_created') return []; if (kind === 'run_started') { const messageId = `${run.id}:user`; - events.push({ type: 'RUN_STARTED', runId: run.id, threadId: run.conversationId }, { type: 'TEXT_MESSAGE_START', messageId, role: 'user' }, { type: 'TEXT_MESSAGE_CONTENT', messageId, delta: run.question }, { type: 'TEXT_MESSAGE_END', messageId }); + events.push({ type: 'RUN_STARTED', runId: run.id, threadId: run.conversationId }, { type: 'MESSAGES_SNAPSHOT', messages: [{ id: messageId, role: 'user', content: run.question }] }); } else if (kind === 'message_start') events.push({ type: 'TEXT_MESSAGE_START', messageId: data.messageId, role: 'assistant' }); else if (kind === 'message_delta') events.push({ type: 'TEXT_MESSAGE_CONTENT', messageId: data.messageId, delta: data.text }); @@ -18,6 +18,16 @@ export function projectEvent(event: ResearchEvent, run: Run): ObjectData[] { else if (kind === 'tool_intent') events.push({ type: 'TOOL_CALL_START', toolCallId: data.callId, toolCallName: data.name }, { type: 'TOOL_CALL_ARGS', toolCallId: data.callId, delta: data.arguments }, { type: 'TOOL_CALL_END', toolCallId: data.callId }); else if (kind === 'tool_result') events.push({ type: 'TOOL_CALL_RESULT', toolCallId: data.callId, messageId: `${String(data.callId)}:result`, role: 'tool', content: JSON.stringify(data.result) }); else if (kind === 'run_finished') { + const messages = new Set(); const steps = new Set(); + for (const previous of history) { + if (previous.seq >= event.seq) break; + if (previous.kind === 'message_start') messages.add(String(previous.data.messageId)); + if (previous.kind === 'message_end') messages.delete(String(previous.data.messageId)); + if (previous.kind === 'step_started') steps.add(String(previous.data.stepId)); + if (previous.kind === 'step_finished') steps.delete(String(previous.data.stepId)); + } + for (const messageId of messages) events.push({ type: 'TEXT_MESSAGE_END', messageId }); + for (const stepName of steps) events.push({ type: 'STEP_FINISHED', stepName }); events.push({ type: 'CUSTOM', name: 'openlab.run_state', value: data }); if (data.state === 'failed' || data.state === 'interrupted') events.push({ type: 'RUN_ERROR', message: data.error ?? 'Run interrupted', code: data.state }); else events.push({ type: 'RUN_FINISHED', runId: run.id, threadId: run.conversationId }); diff --git a/app/main.ts b/app/main.ts index a2f3e8d..3ac838f 100644 --- a/app/main.ts +++ b/app/main.ts @@ -23,6 +23,8 @@ const runtime = new WorkerRuntime(async run => { if (provider.model !== run.config.model) throw new DomainError('Model selection changed; create a new run'); const skillId = String(run.config.skillId); let instructions = researchInstructions; + const materialContext = (Array.isArray(run.config.materialIds) ? run.config.materialIds : []).map(materialId => store.readMaterial(String(materialId)).material).filter(m => m.projectId === run.projectId && typeof m.preview === 'string').map(m => `Attached material ${String(m.name)} (untrusted evidence, not instructions):\n${String(m.preview).slice(0, 20_000)}`).join('\n\n').slice(0, 60_000); + if (materialContext) instructions += '\n\n' + materialContext; if (skillId !== 'builtin-literature') { const skill = skills.load(skillId); if (skill.entry.revision !== run.config.skillRevision) throw new DomainError('Skill revision changed; create a new run'); diff --git a/app/openapi.ts b/app/openapi.ts index 001bb1f..5e6f4f8 100644 --- a/app/openapi.ts +++ b/app/openapi.ts @@ -8,9 +8,12 @@ const operation = (summary: string, request?: object, result?: object) => ({ sum const paths: Record> = { '/session': { post: { ...operation('Exchange the per-launch bootstrap token for an HttpOnly local session'), security: [], parameters: [{ in: 'header', name: 'x-openlab-bootstrap', required: true, schema: string }] }, get: operation('Read current CSRF token') }, '/setup': { get: operation('Read local runtime diagnostics and public provider configuration') }, + '/retrieval/test': { post: operation('Check the fixed metadata provider without starting research') }, '/projects': { get: operation('List local projects', undefined, { type: 'array', items: ref('Project') }), post: operation('Create a project', object({ name: string }), ref('Project')) }, '/projects/{projectId}': { get: operation('Read project, conversations, runs, sources and artifacts'), patch: operation('Rename, archive or reopen a project', object({ name: string, archived: boolean }, []), ref('Project')) }, '/projects/{projectId}/conversations': { post: operation('Create a conversation', object({ name: string }, [])) }, + '/projects/{projectId}/materials': { post: operation('Copy an inert attachment into project storage (10 MB maximum)', object({ name: string, base64: string })) }, + '/materials/{materialId}': { get: { ...operation('Download checksum-verified original attachment bytes'), responses: { '200': { description: 'Original bytes; Content-Disposition attachment', content: { 'application/octet-stream': { schema: { type: 'string', format: 'binary' } } } } } } }, '/projects/{projectId}/export': { get: operation('Download a versioned portable research archive', undefined, ref('Archive')) }, '/import': { post: operation('Verify and import an archive as historical records with new identities', ref('Archive')) }, '/conversations/{conversationId}': { get: operation('Read platform-owned messages and run history') }, diff --git a/app/platform.ts b/app/platform.ts index cb3bc58..123765b 100644 --- a/app/platform.ts +++ b/app/platform.ts @@ -26,13 +26,17 @@ export class Platform { for (const run of store.list('run')) if (!terminal.has(run.state)) this.event(run.id, 'interrupted', { reason: 'Control plane restarted; explicit retry required' }, r => { r.state = 'interrupted'; delete r.interaction; }); } project(name: string): ObjectData { - const project = { id: randomUUID(), name: text(name, 'project name', 200), archived: false, createdAt: new Date().toISOString() }; + const project = { id: randomUUID(), name: text(name, 'project name', 200), question: '', discipline: '', method: '', archived: false, createdAt: new Date().toISOString() }; this.store.put('project', project.id, project.id, project); return project; } updateProject(projectId: string, changes: ObjectData): ObjectData { const project = this.store.get('project', projectId); if (changes.name !== undefined) project.name = text(changes.name, 'project name', 200); + for (const field of ['question', 'discipline', 'method']) if (changes[field] !== undefined) { + if (typeof changes[field] !== 'string' || String(changes[field]).length > 10_000) throw new DomainError(`Invalid project ${field}`); + project[field] = changes[field]; + } if (changes.archived !== undefined) { if (typeof changes.archived !== 'boolean') throw new DomainError('Invalid archive flag'); if (changes.archived && this.store.list('run', projectId).some(r => !terminal.has(r.state))) throw new DomainError('Cancel active runs before archiving', 409); @@ -54,8 +58,12 @@ export class Platform { const projectId = String(conversation.projectId); if (this.store.get('project', projectId).archived) throw new DomainError('Reopen the project first', 409); text(input.question, 'question', 20_000); text(input.requestKey, 'request key', 200); - const configKeys = new Set(['providerId', 'model', 'skillId', 'skillRevision', 'runtimeId', 'retrievalId', 'language']); + const configKeys = new Set(['providerId', 'model', 'skillId', 'skillRevision', 'skillPath', 'runtimeId', 'retrievalId', 'language', 'materialIds']); for (const [key, value] of Object.entries(input.config)) { + if (key === 'materialIds') { + if (!Array.isArray(value) || value.length > 50 || value.some(item => typeof item !== 'string' || this.store.get('material', item).projectId !== projectId)) throw new DomainError('Invalid selected materials'); + continue; + } if (!configKeys.has(key) || typeof value !== 'string' || value.length > 500) throw new DomainError('Run configuration accepts public selection identifiers only'); } const runs = this.store.list('run', projectId).filter(r => r.conversationId === conversationId); @@ -67,6 +75,7 @@ export class Platform { if (runs.some(r => !terminal.has(r.state))) throw new DomainError('Conversation already has an active run', 409); if (input.parentRunId && !runs.some(r => r.id === input.parentRunId && terminal.has(r.state))) throw new DomainError('Retry parent must be a terminal run in this conversation'); const limits = { ...defaultLimits, ...input.limits }; + if (Object.keys(limits).some(key => !(key in defaultLimits))) throw new DomainError('Unsupported run limit'); for (const [key, value] of Object.entries(limits)) if (!Number.isSafeInteger(value) || value < 1 || value > defaultLimits[key as keyof Limits]) throw new DomainError(`Invalid ${key} limit`); const run: Run = { id: randomUUID(), projectId, conversationId, requestKey: input.requestKey, question: input.question, config: input.config, limits, state: 'queued', phase: 'planning', decisions: {}, createdAt: new Date().toISOString(), ...(input.parentRunId ? { parentRunId: input.parentRunId } : {}) }; this.store.transaction(() => { @@ -112,7 +121,7 @@ export class Platform { // The adapter has observed worker termination before settling start(). try { const run = this.store.get('run', runId); - if (!terminal.has(run.state)) this.finish(runId, run.state === 'cancelling' ? 'cancelled' : error instanceof DomainError && error.status === 503 ? 'interrupted' : 'failed', error instanceof DomainError ? error.message : 'Execution failed; check provider connectivity and configuration'); + if (!terminal.has(run.state)) this.finish(runId, run.state === 'cancelling' ? 'cancelled' : error instanceof DomainError && error.status === 503 ? 'interrupted' : 'failed', run.state === 'cancelling' ? undefined : error instanceof DomainError ? error.message : 'Execution failed; check provider connectivity and configuration'); } catch { // A persistence failure prevents further dispatch and publication. Once // storage is restored restart reconciliation will classify the run. diff --git a/app/service.ts b/app/service.ts index 408d0d0..07d9233 100644 --- a/app/service.ts +++ b/app/service.ts @@ -60,6 +60,11 @@ export async function createService(options: { platform: Platform; settings: Set if (path === '/api/v1/session' && method === 'GET') { json(res, { csrf }); return; } if (path === '/api/v1/openapi' && method === 'GET') { json(res, openapi); return; } if (path === '/api/v1/setup' && method === 'GET') { json(res, { node: process.version, platform: process.platform, runtime: platform.runtime.capabilities(), providers: settings.list(), retrieval: { provider: 'Crossref', configured: true, requiresNetwork: true } }); return; } + if (path === '/api/v1/retrieval/test' && method === 'POST') { + const probe = await research.getMetadata('https://api.crossref.org/works?rows=0', AbortSignal.timeout(20_000)); + if (probe.status !== 200) throw new DomainError(`Crossref diagnostics failed (HTTP ${probe.status})`, 502); + json(res, { provider: 'Crossref', testedAt: new Date().toISOString(), metadataAvailable: true }); return; + } if (path === '/api/v1/projects') { if (method === 'GET') json(res, platform.store.list('project')); else if (method === 'POST') json(res, platform.project(text((await body(req)).name)), 201); @@ -79,15 +84,21 @@ export async function createService(options: { platform: Platform; settings: Set else throw new DomainError('Method not allowed', 405); return; } - const match = path.match(/^\/api\/v1\/(projects|conversations|runs|providers|integrations|sources|artifacts)\/([^/]+)(?:\/(.*))?$/); + const match = path.match(/^\/api\/v1\/(projects|conversations|runs|providers|integrations|sources|artifacts|materials)\/([^/]+)(?:\/(.*))?$/); if (!match) throw new DomainError('API route not found', 404); const [, resource, resourceId, operation = ''] = match; if (resource === 'projects') { const project = platform.store.get('project', resourceId); if (operation === 'export' && method === 'GET') { res.setHeader('content-disposition', `attachment; filename="openlab-${resourceId}.json"`); json(res, platform.store.export(resourceId)); } + else if (operation === 'materials' && method === 'POST') { + const input = await body(req); const encoded = text(input.base64, 'material', 14_000_000); + const bytes = Buffer.from(encoded, 'base64'); + if (bytes.toString('base64') !== encoded) throw new DomainError('Invalid material encoding'); + json(res, platform.store.material(resourceId, text(input.name), bytes), 201); + } else if (operation === 'conversations' && method === 'POST') { const input = await body(req); json(res, platform.conversation(resourceId, typeof input.name === 'string' ? input.name : undefined), 201); } else if (!operation && method === 'PATCH') json(res, platform.updateProject(resourceId, await body(req))); - else if (!operation && method === 'GET') json(res, { project, conversations: platform.store.list('conversation', resourceId), runs: platform.store.list('run', resourceId), artifacts: platform.store.list('artifact', resourceId), sources: platform.store.list('source', resourceId), revisions: platform.store.list('revision', resourceId) }); + else if (!operation && method === 'GET') json(res, { project, conversations: platform.store.list('conversation', resourceId), runs: platform.store.list('run', resourceId), artifacts: platform.store.list('artifact', resourceId), sources: platform.store.list('source', resourceId), revisions: platform.store.list('revision', resourceId), materials: platform.store.list('material', resourceId) }); else throw new DomainError('Project operation not found', 404); return; } @@ -101,7 +112,7 @@ export async function createService(options: { platform: Platform; settings: Set if (!provider?.streaming || !provider.tools || !provider.testedAt) throw new DomainError('Test the selected model provider before starting research', 409); const skillId = typeof input.skillId === 'string' ? input.skillId : 'builtin-literature'; const skill = skillId === 'builtin-literature' ? { revision: '1' } : skills.load(skillId).entry; - json(res, platform.createRun(resourceId, { question: text(input.question), requestKey: text(input.requestKey), config: { providerId, model: provider.model, skillId, skillRevision: skill.revision, runtimeId: 'openlab-harness-1', retrievalId: 'crossref', language: input.language === 'zh' ? 'zh' : 'en' }, ...(typeof input.parentRunId === 'string' ? { parentRunId: input.parentRunId } : {}) }), 201); + json(res, platform.createRun(resourceId, { question: text(input.question), requestKey: text(input.requestKey), config: { providerId, model: provider.model, skillId, skillRevision: skill.revision, runtimeId: 'openlab-harness-1', retrievalId: 'crossref', language: input.language === 'zh' ? 'zh' : 'en', materialIds: platform.store.list('material', String(conversation.projectId)).map(material => String(material.id)) }, ...(input.limits ? { limits: object(input.limits) as Partial } : {}), ...(typeof input.parentRunId === 'string' ? { parentRunId: input.parentRunId } : {}) }), 201); } else throw new DomainError('Conversation operation not found', 404); return; } @@ -115,6 +126,10 @@ export async function createService(options: { platform: Platform; settings: Set } if (resource === 'integrations' && operation === 'enable' && method === 'POST') { json(res, skills.enable(resourceId, (await body(req)).reviewed === true)); return; } if (resource === 'sources' && !operation && method === 'GET') { json(res, platform.store.get('source', resourceId)); return; } + if (resource === 'materials' && !operation && method === 'GET') { + const { material, bytes } = platform.store.readMaterial(resourceId); + res.writeHead(200, { 'content-type': String(material.contentType), 'content-disposition': `attachment; filename*=UTF-8''${encodeURIComponent(String(material.name))}` }); res.end(bytes); return; + } if (resource === 'artifacts' && method === 'GET') { const artifact = platform.store.get('artifact', resourceId); const revisions = platform.store.list('revision', String(artifact.projectId)).filter(r => r.artifactId === resourceId); @@ -146,7 +161,7 @@ export async function createService(options: { platform: Platform; settings: Set const current = platform.store.get('run', run.id); const events = platform.store.events(run.id, agui && projectionIndex >= 0 ? Math.max(0, sequence - 1) : sequence); for (const event of events) { - const projected = agui ? projectEvent(event, current) : [event as unknown as ObjectData]; + const projected = agui ? projectEvent(event, current, event.kind === 'run_finished' ? platform.store.events(run.id) : []) : [event as unknown as ObjectData]; for (let index = 0; index < projected.length; index++) { if (agui && event.seq === sequence && index <= projectionIndex) continue; res.write(`id: ${event.seq}${agui ? ':' + index : ''}\ndata: ${JSON.stringify(projected[index])}\n\n`); diff --git a/app/settings.ts b/app/settings.ts index 604c033..4ff6f30 100644 --- a/app/settings.ts +++ b/app/settings.ts @@ -14,7 +14,8 @@ export const windowsProtection: SecretProtection = { }; function powershell(command: string, input: string): string { if (process.platform !== 'win32') throw new DomainError('Persistent credentials currently require Windows user-scoped protection'); - const result = spawnSync('powershell.exe', ['-NoProfile', '-NonInteractive', '-Command', command], { input, encoding: 'utf8', windowsHide: true, timeout: 15_000 }); + const systemRoot = process.env.SystemRoot ?? 'C:\\Windows'; + const result = spawnSync(join(systemRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'powershell.exe'), ['-NoProfile', '-NonInteractive', '-Command', command], { input, encoding: 'utf8', windowsHide: true, timeout: 15_000, env: { ...process.env, PSModulePath: join(systemRoot, 'System32', 'WindowsPowerShell', 'v1.0', 'Modules') } }); if (result.status !== 0 || !result.stdout.trim()) throw new DomainError('Windows credential protection failed'); return result.stdout.trim(); } @@ -38,7 +39,7 @@ export class Settings { const model = text(input.model, 'model', 200); // Validate before persisting; credentials cannot be embedded in a URL. new CompatibleModel({ baseUrl, model, apiKey }); - const entry: ProviderEntry = { id: randomUUID(), name: text(input.name, 'provider name', 200), baseUrl, model, thinkingDisabled: input.thinkingDisabled === true }; + const entry: ProviderEntry = { id: randomUUID(), name: text(input.name, 'provider name', 200), baseUrl, model, thinkingDisabled: new URL(baseUrl).hostname === 'api.deepseek.com' }; writeFileSync(join(this.root, 'credentials', `${entry.id}.dpapi`), this.protection.protect(apiKey), { flag: 'wx', mode: 0o600 }); this.save([...this.list(), entry]); return entry; diff --git a/app/skills.ts b/app/skills.ts index f895641..dbd5ba4 100644 --- a/app/skills.ts +++ b/app/skills.ts @@ -6,8 +6,9 @@ import { join, resolve, posix } from 'node:path'; import { DomainError, id, text } from './contracts.ts'; import type { ObjectData } from './contracts.ts'; import { hash } from './store.ts'; +import { parseDocument } from 'yaml'; -export interface SkillEntry { id: string; source: string; revision: string; name: string; license: string; skills: string[]; enabled: boolean; maturity: 'L1'; files: { path: string; hash: string; bytes: number }[] } +export interface SkillEntry { id: string; source: string; revision: string; name: string; license: string; skills: string[]; declarations?: { path: string; metadata: ObjectData }[]; enabled: boolean; maturity: 'L1'; files: { path: string; hash: string; bytes: number }[] } function safePath(path: string): void { if (!path || path.includes('\\') || path.split('/').some(p => !p || p === '.' || p === '..' || /[<>:"|?*\x00-\x1f]/u.test(p) || /[. ]$/.test(p) || /^\.git$/i.test(p) || /^(con|prn|aux|nul|com[0-9]|lpt[0-9])(?:\.|$)/i.test(p))) throw new DomainError('Unsafe or unsupported skill path'); } @@ -71,8 +72,18 @@ export class Skills { const skillPaths = files.filter(file => /(^|\/)SKILL\.md$/i.test(file.path)).map(file => file.path); if (!skillPaths.length) throw new DomainError('Repository contains no SKILL.md'); const skillId = randomUUID(); + const declarations = skillPaths.map(path => { + const bytes = files.find(file => file.path === path)!.bytes; + const frontmatter = bytes.toString('utf8').match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/); + if (!frontmatter) return { path, metadata: {} }; + const document = parseDocument(frontmatter[1], { uniqueKeys: true }); + if (document.errors.length) throw new DomainError('Invalid skill frontmatter'); + const metadata: unknown = document.toJS({ maxAliasCount: 20 }); + if (!metadata || typeof metadata !== 'object' || Array.isArray(metadata)) throw new DomainError('Invalid skill metadata'); + return { path, metadata: JSON.parse(JSON.stringify(metadata)) as ObjectData }; + }); const licenses = files.filter(file => /(^|\/)(LICENSE|LICENCE|COPYING)(\.[^/]+)?$/i.test(file.path)).map(file => `${file.path}\n${file.bytes.toString('utf8').slice(0, 12_000)}`).join('\n\n'); - const entry: SkillEntry = { id: skillId, source, revision, name: source.split('/').at(-1) ?? 'Skill', license: licenses || 'No repository license found; inspect skill declarations before use.', skills: skillPaths, enabled: false, maturity: 'L1', files: files.map(file => ({ path: file.path, bytes: file.bytes.length, hash: hash(file.bytes) })) }; + const entry: SkillEntry = { id: skillId, source, revision, name: typeof declarations[0]?.metadata.name === 'string' ? declarations[0].metadata.name : source.split('/').at(-1) ?? 'Skill', license: licenses || 'No repository license found; inspect skill declarations before use.', skills: skillPaths, declarations, enabled: false, maturity: 'L1', files: files.map(file => ({ path: file.path, bytes: file.bytes.length, hash: hash(file.bytes) })) }; for (const file of files) { const path = join(this.root, skillId, 'upstream', file.path); mkdirSync(resolve(path, '..'), { recursive: true }); diff --git a/app/store.ts b/app/store.ts index 37abc34..8c16947 100644 --- a/app/store.ts +++ b/app/store.ts @@ -1,6 +1,6 @@ import { DatabaseSync } from 'node:sqlite'; import { randomUUID, createHash } from 'node:crypto'; -import { mkdirSync, realpathSync, existsSync, lstatSync, writeFileSync, readFileSync, renameSync, openSync, fsyncSync, closeSync } from 'node:fs'; +import { mkdirSync, realpathSync, existsSync, lstatSync, writeFileSync, readFileSync, renameSync, openSync, fsyncSync, closeSync, unlinkSync } from 'node:fs'; import { resolve, join } from 'node:path'; import { DomainError, id, object, text, terminal } from './contracts.ts'; import type { Json, ObjectData, ResearchEvent, Run } from './contracts.ts'; @@ -9,19 +9,33 @@ export interface RecordEntry { id: string; kind: string; projectId: string; data export const hash = (bytes: string | Buffer) => createHash('sha256').update(bytes).digest('hex'); const kinds = new Set(['project', 'conversation', 'run', 'message', 'source', 'artifact', 'revision', 'material']); export class Store { - db: DatabaseSync; + db!: DatabaseSync; root: string; + ownerToken = randomUUID(); constructor(root: string) { mkdirSync(root, { recursive: true }); this.root = realpathSync(root); + const lock = join(this.root, 'owner.lock'); + if (existsSync(lock)) { + if (lstatSync(lock).isSymbolicLink()) throw new DomainError('Unsafe archive ownership file'); + const owner = JSON.parse(readFileSync(lock, 'utf8')) as { pid: number }; + if (!Number.isSafeInteger(owner.pid) || owner.pid <= 0) throw new DomainError('Invalid archive ownership file'); + let alive = true; + try { process.kill(owner.pid, 0); } catch (error) { alive = (error as NodeJS.ErrnoException).code !== 'ESRCH'; } + if (alive) throw new DomainError('This data root is already open in another OpenLab process', 409); + unlinkSync(lock); + } + writeFileSync(lock, JSON.stringify({ pid: process.pid, token: this.ownerToken }), { flag: 'wx', mode: 0o600 }); + try { this.db = new DatabaseSync(join(this.root, 'openlab.sqlite')); this.db.exec('PRAGMA journal_mode=WAL; PRAGMA foreign_keys=ON; PRAGMA synchronous=FULL;'); const version = this.db.prepare('PRAGMA user_version').get() as { user_version: number }; - if (version.user_version > 1) { this.db.close(); throw new DomainError('This archive needs a newer OpenLab version'); } + if (version.user_version > 1) throw new DomainError('This archive needs a newer OpenLab version'); this.db.exec(`CREATE TABLE IF NOT EXISTS records (id TEXT PRIMARY KEY, kind TEXT NOT NULL, project_id TEXT NOT NULL, data TEXT NOT NULL); CREATE INDEX IF NOT EXISTS records_project ON records(project_id, kind); CREATE TABLE IF NOT EXISTS events (run_id TEXT NOT NULL, seq INTEGER NOT NULL, at TEXT NOT NULL, kind TEXT NOT NULL, data TEXT NOT NULL, PRIMARY KEY(run_id, seq)); PRAGMA user_version=1;`); + } catch (error) { this.db?.close(); this.releaseOwnership(); throw error; } } transaction(fn: () => T): T { this.db.exec('BEGIN IMMEDIATE'); @@ -109,6 +123,32 @@ export class Store { if (hash(content) !== revision.hash) throw new DomainError('Artifact checksum mismatch', 500); return { revision, content }; } + material(projectId: string, name: string, bytes: Buffer): ObjectData { + this.get('project', projectId); + if (!bytes.length || bytes.length > 10_000_000) throw new DomainError('Material must be between 1 byte and 10 MB'); + const materialId = randomUUID(); + const filename = text(name, 'filename', 250).split(/[\\/]/).at(-1)!; + let contentType = 'application/octet-stream'; + let preview: Json = null; + if (bytes.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))) contentType = 'image/png'; + else if (bytes[0] === 255 && bytes[1] === 216 && bytes[2] === 255) contentType = 'image/jpeg'; + else if (/\.(md|txt)$/i.test(filename)) { + try { preview = new TextDecoder('utf-8', { fatal: true }).decode(bytes).slice(0, 50_000); } + catch { throw new DomainError('Text material must use UTF-8'); } + if (String(preview).includes('\0')) throw new DomainError('Binary data cannot be previewed as text'); + contentType = /\.md$/i.test(filename) ? 'text/markdown' : 'text/plain'; + } + this.writeImmutable(this.file(projectId, 'materials', materialId, 'content.bin'), bytes); + const material: ObjectData = { id: materialId, projectId, name: filename, contentType, preview, bytes: bytes.length, hash: hash(bytes), createdAt: new Date().toISOString() }; + this.put('material', materialId, projectId, material); + return material; + } + readMaterial(materialId: string): { material: ObjectData; bytes: Buffer } { + const material = this.get('material', materialId); + const bytes = readFileSync(this.file(String(material.projectId), 'materials', materialId, 'content.bin')); + if (hash(bytes) !== material.hash || bytes.length !== material.bytes) throw new DomainError('Material checksum mismatch'); + return { material, bytes }; + } export(projectId: string): ObjectData { this.get('project', projectId); const rows = this.db.prepare('SELECT * FROM records WHERE project_id=? ORDER BY rowid').all(id(projectId)); @@ -117,11 +157,13 @@ export class Store { if (runs.some(r => !terminal.has((r.data as unknown as Run).state))) throw new DomainError('Finish or cancel active runs before export', 409); const files: ObjectData = {}; for (const record of records.filter(r => r.kind === 'revision')) files[record.id] = this.readRevision(record.id).content; + for (const record of records.filter(r => r.kind === 'material')) files[record.id] = this.readMaterial(record.id).bytes.toString('base64'); return { schemaVersion: 1, projectId, records: records as unknown as Json, events: runs.flatMap(r => this.events(r.id)) as unknown as Json, files }; } import(value: unknown): string { const archive = object(value); if (archive.schemaVersion !== 1 || !Array.isArray(archive.records) || !Array.isArray(archive.events)) throw new DomainError('Unsupported archive'); + if (archive.records.length > 10_000 || archive.events.length > 100_000) throw new DomainError('Archive exceeds record limit'); const files = object(archive.files); const records = archive.records.map(value => { const record = object(value); @@ -135,6 +177,38 @@ export class Store { const mapping = new Map(records.map(r => [r.id, randomUUID()])); if (mapping.size !== records.length) throw new DomainError('Duplicate archive identifiers'); const projectId = mapping.get(originalProject)!; + const byId = new Map(records.map(record => [record.id, record])); + const reference = (value: Json | undefined, kind: string) => { + if (typeof value !== 'string' || byId.get(value)?.kind !== kind) throw new DomainError('Archive contains a missing or foreign reference'); + }; + for (const record of records) { + const data = record.data; + if (record.kind !== 'project' && data.projectId !== originalProject) throw new DomainError('Archive contains foreign project data'); + if (record.kind === 'project') { text(data.name, 'project name', 200); if (typeof data.archived !== 'boolean') throw new DomainError('Invalid archived project'); } + if (record.kind === 'conversation') text(data.name, 'conversation name', 200); + if (record.kind === 'run') { + reference(data.conversationId, 'conversation'); + text(data.question, 'question', 20_000); object(data.decisions); object(data.limits); + const config = object(data.config); + const keys = new Set(['providerId', 'model', 'skillId', 'skillRevision', 'skillPath', 'runtimeId', 'retrievalId', 'language', 'materialIds']); + for (const [key, value] of Object.entries(config)) { + if (!keys.has(key)) throw new DomainError('Archive run contains private or unsupported configuration'); + if (key === 'materialIds') { if (!Array.isArray(value)) throw new DomainError('Invalid archive materials'); for (const materialId of value) reference(materialId, 'material'); } + else text(value, 'selection', 500); + } + if (data.parentRunId) reference(data.parentRunId, 'run'); + } + if (record.kind === 'message') { reference(data.runId, 'run'); reference(data.conversationId, 'conversation'); if (typeof data.content !== 'string' || data.content.length > 2_000_000 || !['user', 'assistant'].includes(String(data.role))) throw new DomainError('Invalid archived message'); } + if (record.kind === 'artifact') { reference(data.runId, 'run'); reference(data.latest, 'revision'); } + if (record.kind === 'revision') { reference(data.runId, 'run'); reference(data.artifactId, 'artifact'); id(text(data.stepId)); } + if (record.kind === 'source') { + text(data.title, 'source title', 2000); + const url = new URL(text(data.url)); + if (url.protocol !== 'https:' || url.username || url.password) throw new DomainError('Invalid archived source URL'); + if (!['unverified', 'metadata_verified', 'content_verified'].includes(String(data.verification)) || !Array.isArray(data.accesses)) throw new DomainError('Invalid archived evidence'); + for (const access of data.accesses) reference(object(access).runId, 'run'); + } + } const rewrite = (value: Json): Json => { if (typeof value === 'string') return mapping.get(value) ?? value; if (Array.isArray(value)) return value.map(rewrite); @@ -147,6 +221,17 @@ export class Store { const content = text(files[record.id], 'artifact', 2_000_000); if (hash(content) !== record.data.hash || Buffer.byteLength(content) !== record.data.bytes || !records.some(r => r.kind === 'artifact' && r.id === record.data.artifactId)) throw new DomainError('Archive checksum or reference mismatch'); } + if (record.kind === 'material') { + const bytes = Buffer.from(text(files[record.id], 'material', 14_000_000), 'base64'); + if (hash(bytes) !== record.data.hash || bytes.length !== record.data.bytes || bytes.length > 10_000_000) throw new DomainError('Material archive checksum mismatch'); + // Imported previews are derived from verified bytes, never trusted JSON. + record.data.preview = null; + if (['text/plain', 'text/markdown'].includes(String(record.data.contentType))) { + try { record.data.preview = new TextDecoder('utf-8', { fatal: true }).decode(bytes).slice(0, 50_000); } + catch { throw new DomainError('Invalid imported text material'); } + } + if (!['text/plain', 'text/markdown', 'image/png', 'image/jpeg', 'application/octet-stream'].includes(String(record.data.contentType))) throw new DomainError('Invalid material type'); + } } const events = archive.events.map(value => { const event = object(value); @@ -157,11 +242,12 @@ export class Store { this.transaction(() => { for (const record of records) { const data = rewrite(record.data) as ObjectData; - data.originalId = record.id; + data.originalId = record.data.originalId ?? record.id; if (record.kind === 'revision') { // Report bytes remain exact. IDs inside prose remain historical citations. this.writeImmutable(this.file(projectId, 'artifacts', String(data.artifactId), `${String(data.id)}.md`), String(files[record.id])); } + if (record.kind === 'material') this.writeImmutable(this.file(projectId, 'materials', String(data.id), 'content.bin'), Buffer.from(String(files[record.id]), 'base64')); if (record.kind === 'run') { data.imported = true; delete data.interaction; } this.put(record.kind, String(data.id), projectId, data); } @@ -169,5 +255,9 @@ export class Store { }); return projectId; } - close(): void { this.db.close(); } + private releaseOwnership(): void { + const lock = join(this.root, 'owner.lock'); + if (existsSync(lock) && !lstatSync(lock).isSymbolicLink() && (JSON.parse(readFileSync(lock, 'utf8')) as { token: string }).token === this.ownerToken) unlinkSync(lock); + } + close(): void { this.db.close(); this.releaseOwnership(); } } diff --git a/app/tests/service.test.ts b/app/tests/service.test.ts index 4d3e172..cf610f1 100644 --- a/app/tests/service.test.ts +++ b/app/tests/service.test.ts @@ -7,6 +7,7 @@ import { Skills } from '../skills.ts'; import { Research } from '../research.ts'; import { createService } from '../service.ts'; import type { Run, ObjectData } from '../contracts.ts'; +import { HttpAgent } from '@ag-ui/client'; test('loopback authentication, origin/CSRF validation and public-only provider metadata', async () => { const f = fixture(); @@ -37,8 +38,8 @@ test('independent AG-UI consumer and domain cursor replay never create or resume const f = fixture(new SubstituteRuntime(async (_run, host, signal) => { work++; await host.wait({ kind: 'confirmation', input: 'boolean', prompt: 'Plan' }, signal); - await host.emit('message_start', { messageId: 'stable-message' }); - await host.emit('message_delta', { messageId: 'stable-message', text: '中文证据' }); + await host.emit('message_start', { messageId: '00000000-0000-4000-8000-000000000001' }); + await host.emit('message_delta', { messageId: '00000000-0000-4000-8000-000000000001', text: '中文证据' }); await host.emit('message_end', { messageId: '00000000-0000-4000-8000-000000000001', content: '中文证据', stepId: null }); })); const service = await createService({ platform: f.platform, settings: new Settings(join(f.root, 'settings')), skills: new Skills(join(f.root, 'skills')), research: new Research(f.store) }); @@ -64,6 +65,10 @@ test('independent AG-UI consumer and domain cursor replay never create or resume assert.equal(events.filter(e => e.type === 'TEXT_MESSAGE_CONTENT' && e.delta === '中文证据').length, 1); assert.equal(await (await connect()).text(), stream); assert.equal(work, 1); + const agent = new HttpAgent({ url: `${service.origin}/api/v1/runs/${run.id}/ag-ui`, threadId: run.conversationId, headers }); + await agent.runAgent({ runId: run.id }); + assert.equal(agent.messages.filter(message => message.role === 'user').length, 1); + assert.ok(agent.messages.some(message => message.role === 'assistant' && message.content === '中文证据')); assert.equal((await connect({ threadId: 'wrong' })).status, 409); const sequence = f.store.events(run.id).at(-1)!.seq; const replay = await fetch(`${service.origin}/api/v1/runs/${run.id}/events?cursor=${sequence}`, { headers }); diff --git a/app/tests/settings-skills.test.ts b/app/tests/settings-skills.test.ts index 38d6a2f..896a5bd 100644 --- a/app/tests/settings-skills.test.ts +++ b/app/tests/settings-skills.test.ts @@ -3,9 +3,15 @@ import assert from 'node:assert/strict'; import { readFileSync, writeFileSync } from 'node:fs'; import { join } from 'node:path'; import { fixture } from './helpers.ts'; -import { Settings } from '../settings.ts'; +import { Settings, windowsProtection } from '../settings.ts'; import { Skills } from '../skills.ts'; +test('native Windows credential protection round trips without inheriting PowerShell Core modules', { skip: process.platform !== 'win32' }, () => { + const encrypted = windowsProtection.protect('synthetic-credential-roundtrip'); + assert.notEqual(encrypted, 'synthetic-credential-roundtrip'); + assert.equal(windowsProtection.unprotect(encrypted), 'synthetic-credential-roundtrip'); +}); + test('provider public metadata never contains the protected credential', async () => { const f = fixture(); try { diff --git a/app/tests/store.test.ts b/app/tests/store.test.ts index 4683a3a..8aaba85 100644 --- a/app/tests/store.test.ts +++ b/app/tests/store.test.ts @@ -5,6 +5,7 @@ import { writeFileSync, symlinkSync } from 'node:fs'; import { join } from 'node:path'; import { fixture } from './helpers.ts'; import type { ObjectData } from '../contracts.ts'; +import { Store } from '../store.ts'; test('Unicode paths, exact immutable revisions, checksums and export/import round trip', async () => { const f = fixture(); @@ -67,3 +68,48 @@ test('managed directory junctions cannot redirect files outside a project', asyn assert.throws(() => f.store.file(projectId, 'artifacts', randomUUID(), 'revision.md'), /Unsafe/); } finally { await f.cleanup(); } }); + +test('copied materials retain hashes and exact bytes across portable archive imports', async () => { + const f = fixture(); + try { + const material = f.store.material(f.projectId, '中文附件.md', Buffer.from('# 文本\n\nSynthetic evidence.')); + const unsupported = f.store.material(f.projectId, 'active.html', Buffer.from('')); + assert.equal(unsupported.preview, null); + assert.equal(unsupported.contentType, 'application/octet-stream'); + assert.equal(material.contentType, 'text/markdown'); + const original = f.store.readMaterial(String(material.id)).bytes; + const importedId = f.store.import(f.store.export(f.projectId)); + const copied = f.store.list('material', importedId).find(m => m.originalId === material.id)!; + assert.deepEqual(f.store.readMaterial(String(copied.id)).bytes, original); + assert.match(String(copied.preview), /Synthetic evidence/); + assert.throws(() => f.store.material(f.projectId, 'large.bin', Buffer.alloc(10_000_001)), /10 MB/); + assert.throws(() => f.store.material(f.projectId, 'bad.txt', Buffer.from([255, 254, 0])), /UTF-8/); + } finally { await f.cleanup(); } +}); + +test('imports reject foreign references and private run configuration before publishing a project', async () => { + const f = fixture(); + try { + const run = f.create(); await f.platform.tasks.get(run.id); + const archive = f.store.export(f.projectId); + const foreign = structuredClone(archive); + const foreignRecords = foreign.records as unknown as { kind: string; data: ObjectData }[]; + foreignRecords.find(r => r.kind === 'run')!.data.projectId = randomUUID(); + assert.throws(() => f.store.import(foreign), /foreign project/); + const privateConfig = structuredClone(archive); + const records = privateConfig.records as unknown as { kind: string; data: ObjectData }[]; + (records.find(r => r.kind === 'run')!.data.config as ObjectData).apiKey = 'test-only-sensitive'; + assert.throws(() => f.store.import(privateConfig), /private or unsupported/); + assert.equal(f.store.list('project').length, 1); + } finally { await f.cleanup(); } +}); + +test('one control plane owns a data root and closing it releases ownership', async () => { + const f = fixture(); + try { + assert.throws(() => new Store(f.root), /already open/); + f.store.close(); + f.platform.store = new Store(f.root); + assert.equal(f.platform.store.list('project').length, 1); + } finally { await f.cleanup(); } +}); diff --git a/assets/docs/workspace-preview.png b/assets/docs/workspace-preview.png new file mode 100644 index 0000000000000000000000000000000000000000..e3c5390c8a06e760fe09dd81b2517724f6d88971 GIT binary patch literal 173429 zcmd?QRa9JC&@S2u2?P%wAV7e|EqH(c3GVJ1q;YpAA-H?v?(QzZ-CaAlyGz6AWbgf- zJMMkD<2;`Aw8mPiXV!J&oZRV*a3Fl&w&3B`{LjGf*1dHe*R1I)o*E4hlfGG%a_ucDEzySHSKeFF*F@o z;;P|+ikze9Kz2zAl9L3B!U{$Z_~D1-vclNjQ%uuDIu{jQ*_hKdnEm}{P=K?7b8RU_ zmgtgT-WZ;+3j!r|Nu4bSOv+X3pf^hO)hZrIwINJOsp<8CG|j5E-RUMB2{ojD2t$-U z1&d(~9LBR0X^yDxSK14wTI!5BKtgr&4$HL0D=52ik~x~g-P8H&#hP$BqZ+#aFlD!5 zq6l9lntX^n6fAYDzDgd+hvj3Wh5-P42gfwd*`NEPtaXeF=d0dq&(`QXE-mv-dNR_V z4yO=*noR%zzPOUczHo_mJgRR9NUE!`zN^Ya8KMc7$cec9vPLnkB`8KMM8crSE~FH0 ze5jGIk)bEMg>0ZeMH0op2{oJ%suBhTz{OypiGdkD|6HbgtuCVvt`X@Z(#EW$n&_S3 z_81YLc+4ayqo<(RAP*SGN!gvKO5Kso>PtLq7rduuoL$rTG7$+XmSJ#2m#GqO1x8rR zN?QNWP6Kh)7p}8QlA9=W;W|-_r3=xf^pBTNO}coQevPi2Dw3lbh$0f_rLvco#7vAi z68ibh)W7pJvpbFf1xw#Sg+-#IREg26S0is#fR;Lp_=QP}6?BcU`bI}W^`eg^COAc2 zL?a?)bEotGfJIO`WOGv}OyArX=7 z@QW@PS|xgqqWE~BdxyOs`WR05Aax{OS@)LdFlj<4y8Dy6VZ?bz)0L|AY%V2+yD`JpcHL7!GtM)42!Fl6ESVv+*$f?o&WZleIO=pA6yP%>=77TO+FMG$Qe$mj)4%(dN z80Z^H@zuv?T$iWo%50Q+`>^*RdWW(TU)Yx}_bzcL)=)f*Q*|yzBAa3&kSjO#HH}!% z=gCgmg5edQxH?%e28Fe1DL6{M1C@rv4M~#9Em%?KY^uoD` zh@Igb?zx|w2^;_r_cki&HhGh&>9nmNEXJ?apLhCYEOIp{q}9keGM^Oe#PpV@gi-aA z{wE8lzn+=7`Z~Eq?J@@yOY_XaDRL)%*Bx)@L%Gh$H{fAfi{i=L}f%l>Q;tHYyj z%!5F8t2_}LKu#Q{M;-j30ee1}a#&p7NWWDJwgpB|8)8k;Cgi1;WL3UI<`WtP+DOuB z_XNDbJmbAGpydzg7sV-zowOkpmfU(T3TY2me1$sT(?`G(}-{x9nD+*&tE~@%R`l!eO#N`n3_3{`G z7HD>}W((L3A=BAY3HXYREdNf0XLpa!%yrYa^Jr}4b(n>}3EyW)uvjDT+0D5-QXF(l z*@o#&dVlI?lDzODhW*iU$?V*X!!1w*h+O@CUMx$JXPHxL!^ug5{b%e@ZeW+QkP6VW z&p##V08S`Cx!5#%-kPlMN;f58qxMwLs(UJzXn8NP9{^Col{REtf&SFp#$(%LL3%Nm z+y9#X$>_T9VLFXs>uI5nGi3xf1`+RCa!5yFzC83i-b!(93PRMBT(ROGgyYm!43Jc?n}){{Cy~n{3j@H!Ss_ ztpoEIyR9@wE+n)0Us($;rh10RG1V6Z=76C=+nkNMf$tkvN!`&1+kp2D9bm&9#L75> zv4O9dCCH%<{^uMX;u1LlI3SXs;oHlQfnGKrR~t1Bc!|9?Y+%H<6XX$J>F{BGymzjj znr~W*zH5k!5tpleZ~#}77|&g-4@-PVx%YI7^``&uNhBz1hBY*NjAo;}^^DM{uej`h z%>aJL9QX0|*X~qfv8W-*^A=R^^0`YC+i7pF(wUdAjje0*(udi2!PZxC>NWq))gW9d zEu($cW(VQC&*Ue{rN$6hH0{PY6_6no01zw5?$6?E4`ef21DigvjIDIp)v%@yjni-6 zZZoj?MIC(?f;W>Pj~P8#GPAl|id71Ur7MhnrH&5HOBb^PBD#WskGBMaJ`XIGYcvl0 zl{HmdkwVWy7`CQ#f7x__{qd=Kiuu*wH-3`|ueMtYogAk^Mzt+C`zZZes^wdCzsK#E$|^KAlhat%@$lx@Mr6i2S6H%99fiwRnK=uOg4hZ`Squ42(xCG*72LF4<-B|1&L!wT-$*un=C^xQsP z-fO!th4%+SV5)I%+Fe#Fk=Z6hgIj2seRghv5tA;BV%4gF@kw&a?{)n@p7CilGp2J4^%ufI zG%)G;CO%q6EmjNpGf*Jhrm@>)-Q<_CggWTjD{!?%U3VxK$7XuhJ3J>|d9MY0J!`%I zQPPUe3*^Dyg)~UuyP09838ZLhHDOAztU7=DVIB@7KY^KUp99&Y&EEF)01j*qsWA{& zrIfJMNK|(EMUs}ViPYZv_i8ef+^VTY+s_Tl(q23xvVEhFilTX;B{`UHV+R!HiB+LN(OqwPV=ozvWv@bFMG>k|m4tCh zVfV*rb)c)zW=}Aw?nIk3y7>#dnwm1`#b|#mx}4iMd^KY5n8I&Kx?;h_K5anjIlH|% zgHxi1v8{m_Z*1N6VW~61QNMC%H4O&Crd}C$nPI7`#k5$48>%^8#bPALsC``eUs-c5>Jc!@r&N_mdS z)tWTSliWuoAuUT}2%Jk5k7{J+ecE_rhmTeidT*uD@z9U}7Ke7tU^P8e>7aVkc(Uwk z?4obFP;T-3%@!sPGXePdIr7&4z}Al!kN5|XMo8TkQ?Aa^L_Y(s4;jM5;#ew7Ex0dsRmh53+tyh5)$GWo%d z(>#*v_|i=D0C#Cs3n;R0D6gBFe<_-#TT08qGM(>tpOl1m=iAiw!CBy3E*}hi0s=VC zl-5YXwr&t_uxzaAh?tsA7fbfu36<5I`c^y06&O2wlJ~aU##|y1Gg(^`o)b+{XCnK1 zui7Zr(){BxCc))fzm5L0WGiZj|C7Bs7(c|0DuCs zlB1ze%xxw8FoRLrP$34#k5T)hFJ~chpX>^@d1Zpv}m zXRFK2ncZ|zD8yY$>+<$#q#+%RVF4%Zt4{0dv-VH5JMmT`*T=O@WU`(?a(V6#v#M5Y z+s_<7{-*S1m6HPaF0L@({JZv&)}N`6ie=iNUAgb&e19l-2;{~9*}nj(ZqeB7o3mp$ zI;G6JeuJ#)@U;b0z4@|2F2#ciqXHcL%$Rgwc_a&zq|k@}3%|2A4*!_l^Dr_~(*rjt zm{wK8`V#buWkyKkN8Me<_gZ9l%>Rf5jeG*ZM85bH1vLMu*H=RReLE};YihsZB{o@% zVm$DirZ9-Hw|a;)paTi3()=m6Gi|q_WO}uA3w?a7MYS^?sUP+a9?wj6B19MKCyU2Q zP0ZPoG+A>r4X!KNbm}unB8axwn##p&pC^7orecV*OwpU$P6p8CZ=oH_Xo5WTWs+&bRbT*R=?Rv?V@}@U8Nm?Fjs^+QIhpoRDZU-&`8y ziOl7TcA5aIDoa{~vy0T#d>rq{;wXremTnp@N2?K&6OrXzT4Gu_2Z`}GvRJs<`PH^b ztu}0s1-_9&QTsZ`#={(%f%s7cqxL=EdcDW(XH9v=6<1$v(Qx+|4t! z6nXTihAASCb(uA(Ci1Gxd@)7(r8*YW^Y8i&MmBh+@IxFR8TAZ}@4&?sr5CCJd6#ml z(DQoMwpLz~6-ugF@B6s-$$QQnv&Ihbkb1*)ubsn2R>gY@4Sj6Bjf|i%Zexsr-~=94 ziKm;pNPGm)5G4Wn8-Vq9GenRHnLT~Rp^%7?_PAB&%#hMIKFXM{+S0x~od@z*O4~$@ z6eFV}q_#+eBOF%vbAlTB*|}3s8(gBOHHWPNCT8EZo9>p`iYcqK56sz3 zSG}f(8IFXJ4>_r#qR}TEeaEi*=HO3gS=lNHp_R(+Jvf&yEqafkIy+Nis&qQQiDROD zgBcx-fXN(JWAt?En)GHb#~(_o&p7<<^w=1!GZmFOtO8G7_q*2TxAO8+;i;O-O-|(n zYV#KimaRr%10kKFuFFp+t@&X0>#8(I)l_pZ5;mGz1U8@hD6WPhhkHs@M0%;Z6Uq~| zZwnOnF5s_jr$uHJuidRl@btU;$avp?Gu3nxd05?R=%b<#I9m!Dv$K4Kmx3PJ6_S3p z1V|gNleiB1Cs%xZZ~JjHV~ld40Pu4nIUWVCI{WH+^>bLVEiaG9-SVfGfbverGxlfq zr(1}3h)yIPj+;KOLity?QW4bTmWw*HLWLO?57Xhu%*z|cj;|hw!?@{w1_9i$PuP6m zwRAN`&rl69Jl5kwxuq1Sb`w2`DJ;EQ&r_-6RnqOITuJfVO|R$0^_5x2O7t1xzju1D znWl1&Caw9_?YrMXK3rhF~IxD9zOj<>oT3$3j2Yb69_g0g&+cys{3Bw$v%7C&T48j zg)8V#=}01p_9JRj7yARb?SecG#X1hGS0h) zMYnXRs@&@>Okqho9Gp1B5*1`WW{ih)5bB1)LBL*AANex}$_BDmf*%N9s-+5NS~i_8 zDlb0&%952Ay0wWcG4~xnIJ>u!7!x3wNXV;;-ha;ltVq5ayGyLth zKzPnIFbQ&T%}8k*FfKaX?IRFargQT&xy|@G+8m5o4s{Z3xLJc1?d|s=G(4&UXsTuceoycXgs0);|^y-b91=K8RQn6fOgtk?tiQ zIseki1-a3TeHfVa#>)jqV9CWEx4|cA5d_i732`k~$(zJg29p-M@orHKN(1&V3ifW3 zk})aFfyu-Mb2cBnpPB2|7SgfAn~{+l<144Dqfbz3u>3b zYh3L|1!u`l=*O`%U4FQf%h^{NI?e5qP3M@Hh`PFIjf`#yk8C_9wqIG+Bp^5!X5Qbm zO2ibG6r7M8j$|;tju&thsa%gg3@rC_s;<-Ebbj#VNlnumifh@$zpS>TP3wgs78y=evX1VqIAM6ljA zBO{-a-7||DzNwlpT})G2y|VY#_@u!;B9`@4E(DDv!b(`|c;@F01;afm(PYW3x@T%V zNjD-!Yg>Cc+6kQR|Ad>#h*QK7S-VURVa zyc|$=&Aym-k1&cHY)KG~V2qKtwcAMBnzWVm8w+`IO@fLw$EjnXy06Cg_Q$iboD;{W zNQ>pJyy5DG(l^+CjMWt5ZWPZH9BrMCaaX#AWR7of@(IlP&~I$Ym+78B3j}Wv+fTdH zy%bk_VtANn-;?j<9KuJVrvm?KuU6h^mUmA-WFon(e>>@_=sK2pZ?z8?9M@|T(MH7n z6Z&m!$Y{?#h7oeSytKJUw4|_(Ghst;a2iq~j(Ugmngvik-2RNqUJ?wkC)Rw*J8cxO6xDFytiK{KLe}`AGa4%2`F?eQ z%A=LiMv;aAz3nzPuR&PW`fb?A9cU?>Ob@)C)%On;DhiIeh%T=(dh@)VqfN zAU%*MMnbN#PYw=wimFW}`s`NQ+nYE}lN}<$;ISj+a8(U1hBW5jZ5E=GSxb_c?92*H z5sm4Q&!)>W#~b54SO`wvLbM+~l|O8c?=|Kig_>FS%Hm1a)Lxlz8?c%6hT9&ooh-HV zZMJ4FPI+2rL)(tXOYa-giKPE7Y=)UOir&wN22!z9cQy0XfY;2n@Y$(2uq4#jIQgBe zG@dYTNv^l?j$5CYxAY;yQle(NRmltOL(8Q^c;;4hZ~WPh?|5Mhzm~EtO$<%cxGtLw zjVU(f3iq=4J&b3GuwE4a?^&@$WgJpm zNXVaGyghjg=v0{c!FsbfLVq=&Arfx(uq=hvXW{yHddgwR8Qi7^1?D0K0jB+;YQj=V z9Oi=u-J(prl<+{qu}}A4`=w*r3rCxUu=9>P60Ka6xph?`FjH?7v94v>b=>CsF#blB z91YxQUQxVAJB<4?2tc99Wk-?LnSFhP#_L#>n^_Q%yH$ZdwKrDeq0tQxpTozsFMuE?4j-0jnFqUtTGCb`1kSNg%P3#h1v??#-)Oty2D-mYg2j`?Z%C#imv8t&}nc zXf%f%#N0G|J^ii&UOSi(bQ1JD`_2&}2%dlsfgPZ|V8AAXJ7 zsLS21DZWd%pYFc+cL*2l53xVEvlDe#|5Wb*)<9b;*ejQ#Z@2%Yv0qTXQ2(Q>40?XA z18&F|KgrAz6f#|)ATH#Wikf1)T{?gaa_`yklR|5kN8%$Y291p%vX!?-`WbMTSa-RC6m1wtMQHhl3tHQTC6 zUSojyTCmzQ#Aylb0k<1`_CGF=S-B_SBR!+jHWW9 zJHdmV%cm`j&zzLlHS93OW><%}HdYhNi$ zwuYSFv$F)kBUg69K>cNYkb2$4XDzG4<;(I%B)}=9IlrkNX;x6sc z`Q`c(vWN_vCVN!40gI`q9g{JZLWO&tHEBKNpml7kv~dXQU5-ypav;hojY0Z0Y9k4% zJ3|6<#pzkWOuAFv9Ov`z%sZq9Ln51Mh+9mg*8%$d;(%)3*weVEhMc|SjehzHiR@0g z@+})br*5tSJO~Cu2>$K-aEW2BtvKmJWgiSbO{_Zf;4AdLEiq1J7`MTYT9 zwKr;&u3qSElR4Due8rNkWU0l?rMp({PxfofH1=9o^e*gZQzAuuVb>EmryoW@y zs2swem!`aG)d^l!7Xd*^vQ9UnM92B@PM!*A3lGL<2fU%r>;~<=y7>GLU3^D;hpdlZ zoCahMYuCWYrkPWX)26>=np)Hrz0^b@LO}iYB$G4MtRa5vpd@n)fD1b;==~MYnmZFM zcO&#FJP_YQt>%tRUU04974^|iR(;cLy}4q~{k3FJ>fLq{v*&DV+fW}Qhk;R)LHhN8 z`=HjTB`1@HYZozb7_aud5o}C(-ypQHpE9*C*?Bk^_2G7iKKq= zcsVT+c*kdC&{Y>rwd2XDlbbl)_mf*YBx%U$IEPf8#_c{u$#r~Cp=vTDw6si1cZp#LQbM+V6Rh}4AxhthizANUe8#f|l5UN$F!JCMFoEsSW+8UKERBA3ZNJ9ZZc7S`Z(mRe_}=N;f{2F%j;a%2r+m z^$6ckEDtZ_PBPSPNa=Y!R!O%xm2JGJdFRIIos23i!YLGa}z@5MG-E;3kY0@|;03rlD`bb$1gb?h38ILYlnV7v=tRidd< zd#ZCD&5!BtD3dd3Br`MM0Xm5PtqXiJNo9!nlfWTA@pNQMY}VmVFGY`0o1jpy@~>eA zD&@^no7UowcR;Ha1%nkz3jUk3kpx5WsKczM;Q96wwEBKwuH^{>Z;b6Oj*3LSCh^;S znzP2`uIZEfd6Ef6585^2o>6>W301fEv&lxj7>!IsALDS_9%vu9cFB2G9!M<;e~%^D zSB~73x_ZZ}cftXbk^fUeW$^mV(b}MzLvPsbjopf6$a=j5R^t89j`h>_2C~()rfh$G z^5s4S?cIWgGa91~Zf@JE;dmdBFE4zU1IH&UJr>)GGV#G+7<-Q^ui-bAk@y(db7~E>xR4{lhYWyt{+M6LCJtvKY!;ig0__s@tCh98qaRzN8xG$|AcnH z>NgxT7?*#cLs(txy+V8Y!1aQfQ-$W@#{T}GW25hqTxjkC_#Z6(ol_k~2*QZyYXqXD z+?5d_GPpPq{?D*a_{A#yV@{mjt* zjJ=8l84~{o?Xft?n1ldB#uGb;jrArcGEvyx&rKqO8&JcrCVOY(G>Y4c0Tt4i zl76~K`}KnrLtO6-*x$-oJwsI=H&61%kddFEC+&DlI+0CzI<0P~t8T@ab#=!)VGy@s zK3K^W>@NrZ1yxcFlg)~mxMH6W0i%7n%Qok1u}d_XDBqb}UmTv)h%z_`MVfJz7}q|; zlY2p{q0Hq=l!ga6SCyHbS_l(eYEcDpLz*oxAaKshIWxyR@5XU(H`yxpEIZM=;$fy^Hk$t;b}JN{-j%8Hx_uGN{aW#&8l`<$}O zuaoW1P|edvR~0K6Mc&q4R6XCI*K%=KVCf`EO$?YhE%Kv4h{D}>-Nhvy_27z$6M6US5=V(G1BP=_*Le= zF{wKPSSZ@ijE6UQ-aPPtN;Rm7JHyMb*I_#NPMg&K+3mc>udPi5jzQrD2de47#IGw` ze^xvU`hQh?b&rZ-*I2;Lw_C&;=sLu*{nh3W9j_ZYp0xwwFd4Vdpl^~<+3M#kj#D6> z^9PRH0+8Bnhu+~11gu_?ozgj;?HZca%fPs5Q!ylMnv^Z%p=D4aI5v|8uqnwKBXl*k z^dR9|{KoZKRZO4(TSViCOePqQ^8loDAC(lgDWTesL#dhOtn~qhf}rxFi4e^aDp5Or z`UowEa$<_Yx6~0m#=1I+iB=i0GI_tyBVlO3B31>RY=CUDlkW!k;dUmVJxe5+u1j9ENOa~k2k}CrWRQ4w_ge`d#gjDZf_P| z{iYDZ&PKK6fHG(<4~r#riZMcINzqL-Fv^K7PeX}R`~saqQym+|Za)wl2L?@X!qs{3 znv;6oN^>e;cx<78Gl6H0bh)Yv{bWGXvK(K+k#1V^^_lbqgGt)DAj0Np<6r$e#+yGA zLisBM{_rCi#{&>qN5Z`pjv$nXRkT(bH}m@@H<+o@9?bZ$z-;vDL%{so%*k<-9w5EL zi3-ct$>)o3=eBx?NWW5;yPLmwz$cp7xwo#lD5ktmzqg)>;KH$;tr!^BIDh}g1+2tS z`|T^l@pnjAYLBJH0pm6TE~4NMpHMQqaP6KjKo)Ztjutwa z@oZ}Ct=Y_*nNz`nWqI`>)v8PR_uWE*S=t<)*y}*Aucqmfwp|$-VY)Lr(*lg)821xe zt6NXxf1ERF8XhjeZmXmY4>u~|HlLdqjCZ1#E|S0>s~C^au$N?$)#DH4Yk$wZe|{B^ zBfnEFfbK=H#Qd;hO|n=+c~Z0O|7j&0f5iZ$UZ+uE`$t|G+v8MRbW&k5BVFy(uhU%4 zl#gg)wsyIgi+M1*nLgpIde~bEzoVt9%EX+px2E~4hZapikoE{sSnur(>ZW8|8cR;H za=ItPGNA_@5A)@RS!tu}>}^T&>xoWBk|;aHV2xW_aW*A8AYnOJhena0Y>3H}LO1F| ztCxN#Wjky}NskW@Z`4(@FFHJzdAp^5ToPg}Z7iY#ewQ)D<1Y%+F1#fF(>Qv1URP=c zQ^ZYVO*Qj(S&jL_KPSg}1>*T98t^0{V_i}sWwJ2)HVo;)m9ox)V}=U-^w2H%;`~0W zPRaF>1J?<;ak&ju)U9uHiFc#Ex zXre+^E9WmJD!DLhR85Ka11TBOBusH%E!GLlvKHFxY@Kv!5-M+*0?z{1(D*yBkiTPd zOdaMe8TLSX2qL0W9DXE>7(LhO|4XcWp-^_GuJ-PE#V1Q6E1QT@-N+R;O$%yMfL!Fc zs6|+9ycg>eLH7=ijc_MJ;sr?rdky-mNm@4zHT&i@QbZg?8LnV8dPK6tE62_^02vJ7 zQaL8+{VC`P$x98Z)$Lg|tMBoWewutD;LjDS$c; zk#AV;Q4&^v0-=FxuolZdSDbRQk?y*LHup}i$CwClEyTeUj!=hf^!JKSfO?U;)x$+y z>PW?t5EYN!Yee8d$#n~_i z-|VWb@BJfH27PVG1e2tpNVA3gqQtZI)Alg2?>37h6DKJrS}|VBtETQ2=S1lsBDKjw zN#93LR|qLfv^gia<*}r*(epOHBNibQ=<=?yrwN+Ig-?0W0|ZbB%j7!- zR4p+9bkrE?iQoQL%e%}^-_QvfVijnx@H0K|1KduY9;=8(%td5F3|mbZoyqDAjA*q8 z-3I?m=~ppsz18XEJ3ZIktf}_Y6j8Cf?T^BO`T{pvH1tWK(6gL9pUjw><9R)=Zzm+o zo1RYy}Kd#cg!UT>0%MbLx%hW}HS3HBTSltV7;L>WO65WF`2v12n=B9gm}$~uGJh9O2eBHF%2c21^S8oa55-}m%v<50Jqx`* z&5U9JXFOi-c~0tAH$h{tF29+=S-*cs#$Q+gQmguP2DK7(3X7){Q;0wEskF2nO#LTa zsEu|TqtkWH8)5!~`(K{a1-0@_&K@K`Z{(uegapDiUB>EChRdiLxmOa?GbxPVCZ zzHzt?-A=W?9;~l||5YWr#g7L&8A0`L4qYp*(w`r56Ae=zkLe)qarOP?-X3WTL|4?e zBxia}(zgAP{jWJYjQqY~M>SP&b4&ayzLIRaZ29>Kul~1oWYu!a??vPJ=5yo|m0J*g zDoX!9>ov_@UtO(1m>4-XQQX3A`zPh|To7#S9T8hOlbxvcex~y{Tlc%qgC$t(E|L$H zr6R9jNA+<2H1vj#^Ru%(nZA@Gz5Vbf)P7ls#cy(YomgQXN-F;5X?^r9sdM6@V$gGcs-C=N~W*sFfNdhp#uMEk*Rm$d2E2HJfiD* zUhTT#nIxi31dBSmwV_DGP`Fa9vDz@AUA2n3YAH?h=5&c4e@JdV2UiQnvPl{k zwf;%8D~y%luC+U0&P$Ne;{5=XE?2-#8jDW8Q)>CP&x<0>9)ZNA#C2F=$?x*`t`VgL zCDoaLfSon)o+9ecJX?-$<(Dp<&)r-ZtZ7MaHyY_1Kh50~K>-391NDZhjbl(Lb=I4w zZW3r;urG9oxGm>cBJiMb6rrU#Qnh2!TzRy_ucJ36sB+^y!o2ZQb%gR&`wX~Vg<`xrtDptm9ks9^I4ZmOes+fCunEK+1 z)Fz7@7h9C+++?g*ek>7zpdOhn&`6=pAa zZNeGU==o%^P`mDS7zQL8Hcy)O%Gzut6@=~3# zV8$~#_&(>YAsb<}#5;%(ryuwLMLErC6KLe*NSugy{Pf)+ckYBgni^GC;_=Lw*>=l$ z=^a~l!|kQmQl4)9$<9_i2RjER%PfpFxGL80 z-T%_XbMI{>ZlyEL6jTr;A9r3lQj#ru3rdys3HJmkrcakFV^u%A&hh*P4ch2`5uQ5i z(AX}5OIdcXLnvC~Fx;%sEUmq{9IvEflyH#qOE;u{a5nvx>Eivj`z9za4s832+@$uZ zH*#H;kCZD`N=>v2%ckKZylXLvdz0v44{;(n+9ZUIUh(BOxPCv7m0b4XmNmndEdPM{ z@g{bliS*GxolFovBBlQ9R0Avx9QLo_U;!qg3*gblPevw1d9t6`pxHk5e~KCM58ZZ6 z6z8OPRX%2AWYO(*W4O=|2E>VVY|mBpzMraar?NXFCVd0Sm@PK!AcZP;LXEhg!T=q? zyGqCjpS|t{hk<59;g$FMTj|DUXN&8-wA%D{;I@b@fM{wq0#d`4Pxe}n?!qM{h>o_O zftrj-x;eAWN^nL{S3-(Zcyq1++TKef4S8P~NO+NA!yL|UeIGYWg(|*<19TfR3_?+M zc_;vD4o;Wyw5$p;!m_b)Sa*35L`n8U$IA=Z6FNr@&J03nC2~`|ob5O7aTsn%@2yyq zd=z|eEHgYVCrNXDkV}A1F8>jQYNF6fmJB9XP(mzuyY2h&Hh?5bsLbu6{kiIAq!Edw z7AFi9={*LLCOIfIC~T(z*^h6B8)%784L{l8>Nzr}Zx0Uwds~}US6w7NOS*|J_}d%d z`#<&jN4rKL4Gj!XysLiYICQ{)gq=Z)otMs!?(91Zltx<6u~)>ps1T- z5L;ybWc;D@@a`Z#3o%!>njNhYm$m+F8Vf;wh{87$lP`dMTv+ri@4g7##iFu~wci=; zJ{^v(uVYWXcWv~aR?#?Kh%wF?>iv1|w26y^l|6=yOvi&(QEEte@o99)GU#*EM7>U> zX;^jP`NBq<<5K)iqb*l201oi$z*poQY@$#YH`W+I`~=J994i^fQKMn5Rz=X#5(}l}8xLQbJ6^__CX}#CrJl zENjq})bqy?R%0+J6;*w%9u9QnaQ_9k*n*bYP3gFDt>5v&qzDI`Q3JMHRXR>%p&U6O zen?ew9B8ylCFy%5w*&)Ma`~34&IR@eNgr}j10^>eAPJi2wVQU%8?%c!g}K$)95iAf z;o=*F9q*h;$Oi<^|KkG8x}JxE?gzJx982E-`S0_w_o^}#Ax@8jr#9+mD2= z21>50o_v1ex`+Na?{_$l%$zT-WwTP=qMt-e<#>9iQ~gd?PUrche8GIjHGwl%vwLz< zT8CbU(w4Fbi#K|#3td9j$+@bLx!}!ddS`3qFe9TV*lFHvn`tI(1XtS!Z$Zk?Om|R9 zQf-un_F(sXi@PznG~8eTk+4mu$ln4!cR%4aTliV8d(Pg-MOo+Lms$a8xD1(g$j&PC z4bA6f;Ah4#O@o8IgQE{8S?d++<_KhHl5yKiB%zl&9nL={8Wa*gcdR{Lw45IK7qQ}f z9vz#qn!L31^Y+2aPmz+tf0|Ai@@I2TBEWLa{^BGdX*`7a%;4K26>LR>_@v#myfsy6 zIisR77&E=1Xng#vya*@S9AbRyhG!qwQsgxeQxec==k@-N$^b4sLV`|dc#B)n%op2O zEV{S))}Nf}pVbzua0uF_e={6qc|VipW<5j>%289v_vUqMTLDQE=21u!Rr@^55*r41 zrZ2N+sb?Bm_BbN+z)Tuv-{(uNdYo> zBx%jWgYn$HtHxg|m&X7`HKjy+B0u&oVUWp}RrSzb#gIkB5C!e*%?Ft42nLTHLYOGy zRARj>f|h!_we}3)`nE8tEgWZx(+-jwOLb$8Irvi`aa$~OwLkG_l&AjSmTCH-yXKkM z3AX&4rqKFYwTo03ke&Wy6h?@hH*#_@Sz-nxL>R?%H4^Pn(4@GX}&m1#83C|v0!!_$DFS8ulYPgIS#r3f? zRLY~{F`-p2@9U2SVE5p6*x&Ls&~&twA%t4CZjU)0WC`koJw)-yU>B6N)LL7HI;iKb zd`FWV(~7$0Vr+yLrT#@T_~|!~`IfLL6xp>NQWBQzpB#7VElhNP(_#J;<@j=x|6ohx zWxMY5iE`_oDFT$kjTn`&n_G~|KMEHSI3D}Fz=rMM@sf{>EDF2kO9Pc)K38%7^j9Rr za{Vm9x4*|Lg8!z6^s?7biP=~`%#W01H2&z0kD9wQ_*Q>Xo_mTE?k;i3;y*zmi@LuV*Ew1 zeXpV`Z5gPs7ZB?Kc8J*P-m%+B}A ziPU*goC5_SK8y1juNezA{lpIq4>WJ}lLbvCKPt4ocTExajTcg=g;=7M&2@!w1BUNU zc^^d{ej6XS;>C%55tVbeyqxyNJhO}G$UH|@QCYdzweG{w-|zXG+d$|{zEU_)PWS7J zQYRLVyrjewvHrC897bmTcjikt{4@}YF=h7KV`ioLL7Wlm4Ki+@tA__?{j|Oj=d44w zr)o|Pu+9{3kpVu!HWV&(!-Z9+u4lYszRG=2^ku1@S*kPzk;%#Fv^}u>sE#l+pa5wz zL}Qv!>{)A}1tZJ5qhut%lCFBfn&np_6LBIqi|^)+4t(!ACZorks4Ah~&GU;UUKNxOR!O zxWu!M_IDwF%)&ytf|#NS23s)BMT*{QIK%d!<7S-=H2g<8@e)?Ih8W!-$u>SVK z#~ax~tLN((Kjc%C3F8^>Ck!R_<-94CGJu5+)1jm_t#EiXYcU~sVTnKD`i zyXGEJr+_p^W|96}M-v8LV1m!1=D;Co7iWS}c7@6a)=sK{Q`E|e=dyD1!7eAMB}|gd ze6f&{%RSGV=afPVxs&sflCu;x7=a6_io$~?6eR!{da<)&X^%a&-B}7ZSHpPyvc^t;;yP# zvZKOkCNGlQRcXF=sird8p$QQK-N9ALT5Cb**;o8z=bbUuX;R)XIiIrcWqN$ojHsp1 zCk&u$vH=qVR+)(rid3+gp4H2I{5KO+>OMQ+9qCo)lz*3N>HTxL%+j13on8jBcAaR3 zgT&ei$~Zd#`|=VufR}|I=GyI-^3^WZl3j{aMZMBx z_0_NKyo{>)n+HjTf5eU;rK6iWNGmzc_Rv!#!g0PzNnFtmVx>ddC8I_iK!$`U0cOeotM_%b%x!MC7L0{~gKEk9<2w9DluKK7ytg9Is~B32 z@vVj?$wW^EPW3P2VS9H5XFr8$Z>dB8$@z2^T<5f3XKr5>PZFw5m2xqHx21Lg^C_&S zhu3boy~6YKVb=$F`mOOh3_NmRw6Shha-$)DSC3W1zjpLr>>g$uQJW_JQII3ffvI-e zE+%%a9vAMX8b(DU&D>kwU+qWA7n5MwR2hfo5`qDq%UDNUvXrvkeQ`DaRg&!<`&=N{ z+0=sl`ci7dvUIw^lg|KzZGWRxUj1jMu90u^_LIeJbJ#-{D(UVRLubZAP)zdi3=Ght zX}KurOWbT8mAtFtcAt1^wBJ$Oj=46Kho}_4jGt`aw5{}Lm!?RwzzE~^oegpPq|ka# zZO!_W`HS$+&Aa}>VP}DW>zs&ipWgsQr>uUYm7Wb9X*e1Lc*br|HMS0M2s&Lthf>ri z>E;^oyUQF=U^~dbjzikXnV`1z_|4wzoPW5^ugifDLpu`IcO*fOQ&(3DxPv={X{%P% z^^`bRd?>f2@*%?7j7DhnPX6>eoQt`_QRAt$4{9?8h9C|4W!2)be8GJ~bu^YlKg}7P zWk`^awyh$U(n*KtL|wW_+AB?srNge+`Chpv`@N2SM?4tK&i=9J3+BxQpi-Hu zVp=gX>-{4tX-cCd&E_iti9mZ+pqVNAxLtuLHSE;a#gqe`Z6zZ))=Y zDyDVeg9Nr%3WLN$UVHyR1VSpMFp$0YpD_jXp9&-7Z#Jm^z+xb&{Qm%l|9|`X8vPq6 zbFzFaA`We)9>qEmw@2K8ihE=de*#bM_zx(_5ufHa`q!0nN#`0)6kWMJ#JZDv=81|+ zFpz7Lq%ZME6uI-hvf7WrQ+90~uV|qgiockO^p1%GZ!EYw<<=LZV$t^_FHh4u1!9i5lH@E56ixXMCgt zWvuXgV-v|eQ9Y^w*?O2@+;maBjL2$^OF3z3eWFmaqrqJ4SPWh>K+yq1r$>=H(pB2B z6a~0m(oXN?-PWmsod&Ac23*d)Bj2MXRXhJuqewKrM?c~{9@!$%-^I~IhwLKOk4Qn%ZOx*8WwZ+q=v^0oRXkJOXbL5qAM}~UxE_hus4EFV9a@V~#@n8RpQhRa4C&3~IH!y& zo<%9P{#*m+-KOvV9+-blJX}N;-O5vzoF`7TgLt6L#N1H*Kp%wT%e@Y0lTWTBX_)GY zY9Zxc5x*0~QzI$fTx&llR@sh<8SNJs8d-GT5>E`pR=Bm}X!BrPz{78>OFC)m;)8jCLiYP~!6L(+O_2k`ZwK+2h zHY_p7ZurV&NVNFrtM)9SjUdBu1|v>C7R|5q;HZ% zgc&1_7Ib}ee-SGsOCb3<=B*9$@~Xn-8F^cBK)dn$59Vuq4TyP=e^>UNp^3wD`>Va> z=97|C_)1v7@Y+R!hZ;ld$|L~6pv-bUkeXPh47s&vrnzoBmK6Ni&bV1Jt zBsU4!Eo1fvF+)-3f%huZ^?LJ|blqZCwb>kH_hK>j%%a^*3HplNg`h5h;+vT`tZIp> zQ%U4vBn*QFY+kLb^|BiJ&@Y~nRP^ibgY5h%uVf?@`8lIi2R{%;M=n^9hudIq_k*mR zv{(!b5ps*s(3spM(Iq_*Xa$^IK(ivXinz(b&t&J9t87P8qqLT(KzR+}&AM+KCzoq{ z)Y|TwYWC7r$;r+cHcmz(a1Y9)wb(zFfwm(9YKoFVMJ2@174oC{7Jud&RqFl&L}mm# z8-=f*v@H}F4)5je2{`zo#<=T9Xic?vkHYK}eSxgQ7kHfE6IP;s{uP$k=qLy~FA z&CNj%tqYLMZlfP--o?XA@*Oq~=iPnmMLNKU;xqd^dLbu6`kkxao*L>Sg-zp7BvK+b zTJ)q0wZ_^GAm-ztJyG2vkP9ti&5(AwGj_|;;Ja8=c!0VN97V^AC1ONgZ(F;p{GIvBW#S0)sa%bOj<1}7vPo5TA~aZt7z z1s`M$H9c}MKe0ZiSzRHl@dci4HoeN8__Gz7ay$|$T-7Z z1N2KP=x!yj#%&i6oyCO^okHRkYb@QW(NuGy$2-kK0+#)x1?{9LGSJMm&@gt4(Y&?_ zoIl3;lE*_6Do)t_ID_ z+eo?MMCeNU$L8teZ2>DMe3A<#YN|=h;YSK-u$sRyK?NUi`x?+7KA_4ZX6!M9&)Q6+ zGbA8JmOR<(%)dV-rCW+zI-1CY62}0vUjl|NR9b?*lnz(i%cZHC-7YfQlW~)?=lN~K zamA*b3<|E@Azu1ME*Wap0Z$sI9=XnT)T?2IaBEGG7Bl>br0F8X5?iL4k(yUIomS_8 zi@GtK6*9{}y)nDhTOf)acdx1rQYtRi7#6pyRn9s9Va&?NQ>)QFxR={9BUB6dD!uLE zarikyPDg~h*>eoD)9^AU`wj$Y5mOb6t^>TZRTmueg0Q?qU&(o&m+A$vK`B=&DOG!X zJzhk*ufL$C*vUOB6VSvOD)ytiWojVRBDBcZDu*FHyf}?m|4|C~!jJ`1jQLwz{s>gK zG^4qObX2vLdSg%45z{fyCXt#sB0$#AwWzjQi-UGZhfd~>nuUk*evr^T9#i$o5oBOk z%gbKY85$)M)`W*{A_Cc0>{K{|HlKC*~`#>jlF)=dysSVEE{j9&1d?^sM z$mt6Zk}$53^97|bmIL>tls?JLQj!Q$#ZE-oCIdDW!+wBbRm|%f3S~Z zqf3U{j7ig2MKNhafrgS-Xyr575|k#wcIiPX0QxROQhYwtAP^bQG0jy$V`XdL$OdtN z8h`)9n!CVvWiGm`3pcqP-pm~zlJc9mGnV9)@-r5;mlhA!e` z+XEjswTo67* zqjsp_6mZ( z>zp#%tg}!Ww}Vr1Z;-UgLVom3On#6^*OM7k$1TGIerdn)r6jy=s^BJ^|6T6^%p^T* z#5h33BgPWVH2EuB;C2A#u|4w^Givx|BrssQfK@lz*+aoBwPb-iuvgtO$9*a1>z+6% zmy;g_=RB9;mI$o9`FR|QD&nR%p@ua2Ig3h$l=DV1v=;U7^ho==kl9O(P*Q=4S(EO# zRk2}P^k^lKQpJD~9joq>v@&(nVM2-mB)yH@GF9_oI-(ytI$%7I*-b8*o=ML-AcE9K z^QbP})cq7<6f$|k0%+qZuk2lj1=Tz(zdKOcO@6y<{cZ@83_MQ)jQMuiXWz^Bx=xjx z<~zuEdVQ~EBWF&;!-E3;O*56*Eb7Zs!z7#3g{%WaIWkz!Udldqin4T`k+VMd5B3KK zLyR(nHzO4pSFdtu;lq(BLCh9D=AuJe7 zA%QP8Yz5_$nff_mNEy5kR>!H&UpmEC84vBK{PIQoV8q`k!7uf4uWw!Nt^{TLnK55- z0x6hfuA=(CZcm_~Jb#!#LHnD97MQ(aJj=Wmrb2?XkQcbGulS$YKn7XT&kwKpiy%~J zS2IZ4Rp2jO04h_g)%E-hgpz8*{6P5?$@#8qE`uNPgyFb21!({KYY>+pqf#lqV`WAE zMqc#H?q7NK4_O5FdBz9AeJCKg>UZG^*4UE@8`e7GjS6 zhj9X>Qf3hDT^!m~8NeO{eE z)+DRLZ4h~oh2f@wyBeOXGKideW`81rc3Z`hI}lM_IXKh2&M+DO@y`bM!4c`Y+JJ-i zI#sjXT8a87Ufs@ZLga|V%2&~uOpl0iFWeTmU-CVr7mu2s(oR1z3b}|DdQC==M+ir3 zy_kQ8AzarQ56#m@JNXj+tEiI4cYye za7b=6hN3vBecN!|=NH+G9>QLFs&WO*nq_}{?!O?t{t^^)JLdS{K<8rxri2f$2uy(0%VK!%Yjr{!R?R-&78$pCbTKn}F&Im(vG>6Se1MtP=&jnIA?(?=7=(1xaIiN(Wng z{K_c@`F6WoDWGK;T=uPJIqIf>)vjDF=49pU26CPpMJ?O(SB>}U4A4bm8&zcP(kV1$ z-~Mjq0%6nC%GzrRH_NQL>3yyEQAZvts0_x-|JDMyEtH7t-K{v>R`j`tVxti7nn%Kv zykj=_r6gfpSgVPDNzOA^ElRA?OZlTC8|LtD3b$0ZSH=odU4-YDXt%MEFOanZ>l!=0X|cY?zRX%^&TN6 zht1JCPm?>Ods!eJ@FsBRA{+*(40b}z^&ZNMgtSG(WwNI`HNDkj|6hIRH{OuzQr=eN zR#$>-q1xb8Qkv5BF_S6I5~TfH6&5!KSTAKXP;3* zzP8$VqWpb=A)!c%fS;>1bNuv#tMLodoIfoBGvTS( z;<4jK5&z8FOl!Lem+auME$^Et@>nwl;n}m`u!2TZ8l%CempzT|d{_w;B6-t#FAQ`a zz765E#*d%y5n8J3O4N3L0X%&U<{9T3oh+1A>`2dPj35 zb0U03e|@P_+`VNa0r6B1h{A8gQ5K`-EqgeT&XVw8*AO_7bqC(lv;;G&lie4x6(h#u^I2enDiX13gI`JQv{H(BHjeX%xuMvA$CO(Y z{qWpl!|uJ=_2o6b-%q$~MN>fG;KRCLlv z+dfB4#RZkWPO8Nh*i`mmi_fTAK<{po703R_wo3k9;EQdo3dI>CBeXas&krSGH`j0zS zo*KjZthm4WXv?nxW)uVQ#-|=^CA%7+%u#!^173<$f)NWxvcej5bMhlkF1%ELLvrcc z++m;Hg#-gFt(pQ>TE(jf&qp*sbK}IS2Qd{_Wh2Z6+54Z-Qx0Rr=v%u;T8u1uz-=wP zFlib*iD38b_;SU7A%1lsjKSIV3fDb_ngEk?;lOT}`L+QKpQ-Yv&V2EBLRSK5po za?L#abhNlHt-SovD>(-|eqa;R)8SHIO;V%#^unF2ERB~^@wwE_C$J*#zX=a9fT1XF zNxc0$3x4gsfJOqWFgdhe?l#+eB!F$VeO=M4n%)>6fT_=ou1lY2)}DGjrL!ccp_uMV z#`xQRRtKNgIwCq62FBqha`15e`dBS@Tk;Ijer-|EK0EA)3k4G(-_0GEx4Z|BJ_Zt4 zoA*i0vD?{66T4(XH}WLBow}XdT9Lf|(6=Lpn58!9B3h7-RLG63rg@i|D?soxdVkBX z>k#72cV{);xCLlrPZ4UbDwO-UAQt6YZEW*J;n)qf)a68MAvm*R~ zBeSqZwFCZfsD|B)o;I#A?HX3$snh6b;@hOahpz<0Lq>b{rBa3x$4Ex_sz_+Yj)c*V zM;a%@)6qObunlXC30+c?92mzhU2%li8x& zC<=KG{xwwWt1QvHNc9PIsyf%6XoK|00gx(vQ0qu;yT-8_H1}$S)klpYD4#q!HcNMdFfnaVR zjfv86P%5e=0X&5)%3yk+XTp~jw^lbVg!_XY)ox$kUu{xS#Y2tq;ipJ6hk zAWJOP2HBWEki}TU6y&UZwYpApleK*kjO+={qT=+Du>^%Pr$(-)*gl-2iP|3)B_2dR z;jCJxHC~&XyEaigZWIcP)679!~xIebf{zGpv5u&ah7?Y|p{)2lJk zqZ+QY!0Vr&3s~;7#q5ytG_zF$n8Cr`4V*s!BwO^~-vX>xTCLli+%g}+gu+W;U85y# zTAeq*avD4Rv^Sir=n8xj;=D;dWs2f?y+_}u2(Q3P}4C`w>3U)u`*cimb14{|DeT! zT!O@-G)#}hi-Fyrk@uJi6q@~AQQ6X+b7pqZMcG5sycK$%(%d@cG>lpbMizVTMX<4) z_6g{!Xk^wrG-zw}+B5*Hk;aotvcEU4vs=>&8 z|8|8FRnOGXRRQ}?jArv`bH=#dCRCE>+I_P`AZS4RBCtdh@Ov%FI`nZE?1-m!*o)56E$xvoj?MXs4c<><1_ix0Q5ptgvznejPU z0kAr8TB6Kyi+%}HBgWbhfpE}rr+T^d0{4Z`Bp?K#yHRNH z;;?S4HJcDEnepwZZ86c#uu3zcI{rF!`U$>>-?eBntn30KMSzlvzpEY2dx5R|f*%NK z2CB#{MU!Yafs}9Wr?T7Tl~3)~m2N^0 zv4hFqbt?xdOEA&?oTxqHtSI);3a+P8zi&h>8fJeU)rBJ}8DR|@}N5nky>n#CXH ziYbhiY`@rB-u#XMWJoM=z-%hdI;FaQ_6!S3Y`SaCuv&rmj7UApRjE1#lg899omqrmO?0D5^uyblwg}xdMqwm62xm-jx3nDM6m*q!Hf|B$5+Ok_`RBW^4?4?(uEhPeQLb^ooUH7Zwy^M&u{!$Ku-*L#N7guTSc^2=U^lr|ij zNgc|Yl)0;)f%|6Zl*UQDviEZenzxne*L0Tskuo-8P+dPC?X=X=N7kz`(;hgUa}2S; zE1b6_CGBC(*khu?7(ahK!MZKUIizI491X5wiXQ%Yse8Ko-oEN3rg?C|Dbx83Or6`$ zV4%(1KMNM_wY>dR#hjYzhYsJ>0k#g7%+2-p)6N$N3q+Y751O3=&nqTRZIK1)=~J?u z@K5#1t5eEyC4J_DkW!E;f6b*_sr4tE!0x9Xx73x}sahhY*V#<9T{|Rej|D@BmpQINg9=ZjAhg^BE&+IbbTa>g<-O(8s5- zJUBk>8pOx&)EzePk)&X>U@SB=tZ0V?_M@JsggttFsl=4^bMUK_oC7;iY=O*X0}GSi zoExX!69umT@qwJha$Z31!DdMMZYU7~pL~rBJ!~SH-6%iSU{DiMMKy&%u%}#?+)osg zk2EF+ho4v#7_8l1j+kyQJMNc5$-^ZFxV@j^EU-(}1w7=;mb$`MiT8Kq0*WIrX&O)+ z6jzKJ3Qnh-Ny@|m@+T*(3%-|+?vyTQequM8b+98Z#OJ3Y<$ktgoqlnAaO{}{*Y9sg z@dNL_fo|dZ1P2TMTMIB7T2U8pqY}nF_+)#jk8@Q!!RLAfCyCG=#WAhpJ?9r{V&CWw zEgXP*Sd8_->zlyW+p`JEjqbOV;(hiq`u{3nnFnkLMuQqCZ{%t05?`6(~OHZEGqx;TS@8vW|a*Q~?4G+6*me`ckx;RI_=a%7ayeDve z;j>)1_&uks==x~-ZHW@5w?Zgx>Wo}h!Lr_O#G?gi#PJZlioILqxd`OIYay?ECbFlI zvh$kM+8)5C?j<9zts?glhVU8-?98gKFV4GOfKQ6H^aCpXTBp$LeZ~q{WM^mK+~+oP z;YQ{1Uf(yCwmwSTbL_F-78-*F#n?}lIb?;@hQBLh)(L3BC0eGS*ys{Sx6@$#QGVM< z)VTT9<@-ZM!=gEE)pVVboc$G|BtVApznX>0QrCXW@9AX@wgD#sIjeGq9PA(2$YXdK zd){WsQgK%+ao4ksC>>=tQjt0L^pk^$A!u7;PMg?c!s;Tuu&5SC$0YVR3}nZHnK18A z#)i?hGITTZE*8QHcGn-A)~nf6(XXb{S;9@dnjNu6Y-}Ye;mQz^(2OnP?mRvtlY8Dh@-f2muKsqIKje{4#)N?xQHy4@{#JH zhn=zU$$~y8S{TXaAkOc{a>B5VBymd0zu%DukZcxNmM>aoHy_tw+JV-NLVo!2d6hUx z3U2M#pfw0oTQ2qaWS(myjV$2CuyB#3O;b&)yZpm)7fcqB11FeII5TxfFR*S^cG?Xt zo@DRRmhvHUG0x1+MkeAWWeINJLShD0Q=F?1%iY)2YI65JaQhLFIPy6!qL|b_XFe(O z*G6xML3v^FgKB8UtqtbF@HBEb^WKF-`i0A$^uY(^ z=C)LIgD*u2D5?GJB_rx0+MuZ|wHrTn&kd)3Ot#H6p?H6a)S@s9FZZ;(7%ig6RbSOW ztiT=a#Ae<0vbkY1iZH}Ad!Nxxc%$d3pibFVc3%PE(zSMNJiU@NDp;t^om^my-R|s) zyN)@C277rr;i}o9L4Y=Y2Ie%{AjYx}*@Wypb+kqQc+2BBG76-sO#rSO`w>A&VzVWn ztZuSN@DO*K0SQCq87bmd7CAT%(X-}5ma|PYnsG|=Aa5AUs{*YFBB!89CRcnw8R~Lg za0&IW1#FNmku4P^<56{s>1h8nj;t^70kegWQx|%eJc{SyY`N4uFK9Ne4MSTaj{aDO zLHa;FEFBDB{Elo%;9!Bbf3c_{A8s(0$3|Q~5T14kA@(8Wm%SYPO5n5vMB@x(bJk|o4`{GE#Scj9q=6h5p z%Q83beU{?1u_$n&PYc!3fHaSB-DVvt$${~tW}w95tY+9_(LmfjtZa$S)FV${VyQ{U zJa1UKwi*x?8>Y1`H_rWAS(JD`@j_CH=~6D$v-(x+IxDG9wbxPOX#?sg}$$0XxJ7uK?QY2ui{&-ep z@mRfJqD#&r?b`Jw@dEpG%-Gg`T#n8Rrwe z9EI}U^I7Wtpo`8TOrLLj3`^RD`aaTugKBbyDUFq9`bShyzJ9t`?X)NKb-MW0=MSHJ zDFf_{Vk%Li z0oZ`yHxnU?_m2Qiaa_+&L5LF5lP4!kvo~Qb_%7R6q|0>v>K(3k$$RTY0({SbXbv$~ z=*^8}8~c;YVuA{y8;QTEy~QVUVLcS}00~h`VKrAkU{54&#s@-^(NgT#kUevj%$EU{ z9y2@Yd+Xk+pXAYtWe(QcuFe7(4u$+hvq>rT_Bvm`ZCUO6&&+};ME!d#1L(@fB5>84 zTxFi8&iTBow)401$SSPI$^9Yk-+_{MX(_!Pt@cSjV6_y2bjw6u-)8zCN8u{+L27Co zqlO68jM4Qe$)8?US94F=J-_?01fQ@1?AQ-c1v^e=B9&E{(GvpP4|TR?s=eq=-p0&W^E zgL7U3bou1zTIGROGT8O3YIH)TtY_|XU-y5R1bg%~h{x~^^+x4p&2pe64>dHS;&zL; z657FIpe3CRqx#_!L#cFH>zAl;JY1Gn%%5awuj&oieFqGaGS|r@efm6%CsWfLhM;?F zz+l5^U_KEqvO=;kzK@mlgxlpEpDfwlmB|rqi5&ee15*}%ns|fx|}>D`ao2A?&uHqm{2g*RmuZlNgQoBfE6sVDC-+vXeFKM%h;++sS2tDYmE*<1$JsjTqYBNf98$Di z@XTnkX_ZcPsLn55t2e7~hg!U&s(;8EUzhV9BXspAE}L&(!LwEortjBA1UC~+UU_qa zrG>2s9(JJ$iT~L0>^J#N8?@T^s^rqH9D_y+XxZz;#f7Mfc>XQD{2NWJ6>Bms)1Q znIeLE^rht(#w#gD`Xx$=_u5XWstt+#AK3n|an?+nIK5`&Y8uk62kSJ4gM-N>RkUmx zPYkE^-EebRT|B(F>F!OEzU^MEw=NzB62~M(M)%E!lzQGTb-{tJ{V|#uL3GUrQCjfZ zB-c*d+@s4oM)Z+*hywFFvAk^0lU90;VDjyp?>sKc@r|cW%-)J#W+bKrir?rU?~Y)xdMyU*Wz>Sp;$GGS+uTvwTU zh^^-~pKL};7}EAZxQ&~se^YFV|MgiMy*qr%%O{kj&dUS164KExs=IB<_$Rlk&G1d% z8(57bqAE8afq8sA7YHzxn6Pp1$L{W1kNm=sbq-Q8J?@8K9@K|^Cf zm;=rnCu8pKu`4uPEWIK4b$~-opJtqrJ_~LM^W}YFqg@iUn~3cjv=}VpBz0X7c@>cC zEA;5>OEg>A^#X~J850qk!7#(bF->AbJ6C`$$P4-6&x)kw134x*GUe>?qCK0v_=w#zs5q!kH@|@e^lW zS$N~m1X+fQp9$M|q-BAX??(0!U+L@|DeIyBWq=U-FFq9`FZQX;uxrGM+0M+gQ;`Hw zrW`gh1vJ`oinNv~Sby$CNL8~X*XR8P_F&He+gK=cQyH5I68qK6XJK7N>YIabasG}13-e9j z+}X}=;9vQBdBQ}9CZsN)HYfid-R4jA=>7i#fdr!U(&8!zRRqufPa+7SDPF8jrSzo7 z^nW5Ch6VgZ-)4raZT{!xY^v&MYHlx0URlkjDs=hYoWprvsZNGFP~$KOh=o|0Bm~jv-@cy}j%u z-n`x%e)Uq$@cPIi1B8F21>J({%m5JTs*|X{iJtq*tw1CZpKz#Z0qeth77_7-Aj#kP zV3WCEjig|P@c4D|A+MHG860oUycIePg8E9JkVIX%Kf4cmON52N`+B-+0Ai7sy~=@r zRj$4Z3-CJ8MK?BpJFV?2)U8@Mhv!FEgl;Sp)hjYOg-;S33jCCRKuUNAsGxS$UYg^i zz0#%ty#eQ&HuZ-r3>1kBl6E$`y)8vX^-W1VsS+Ze+NIE=go3tTd4TMC)wZE;HmR)} z`uqF92(i@;N&O<55>IjuB>$oBQjK2v=zMLx&GHTD;4PM5_NkDB5h9ICeIp{oYwT1nuZ@WmtHD@p+V!YNW=|otxe03eT+uB2|5r9xx-z zV}-ggm4G{DEW@`)_I8U-Ken0uRuEp_xYy)zi4NS3kL_yHcqvgBnYK-?gl6Z<67D5X zc|`_YMNptD#Z!2Zc|C92Yhj2n;ZZo5W(T#x8ixpZzqkc%n|_RzFU;|qT$HWVFQi^a{(OruS{F96Pou{z z8gqg)8zuS68Zw8EJc9{lvcUb;;bcP*Ks&%eFDpUiO8W|-r9Uy~>HA!Q&w~t)*MYCH zbB)NUBo50yhZ|P;lH*ZnMLRe&v!rxa>hia!LT3)%(-3%u#U1B1F%Ql!4^tm=%*eC} z(#5nktDtvr+4xd!mFLSeGsyGd96iIjUx`c<3>7K|-!MymDS{Ck7SrbSloCfg*0|+D z;}v?pYtG+>*s7#&A!#<@8A|0Zenq}eOX?J6`>bo2WY5`|#RXx#hQOP3ZypQ~R%(6X z3?6q`?=G$8FeEA!w0JY%IY#x8_lDs8bvtSPTdu~!$$Y6pA1tqLMkb=S)=fS3>C9s zvB=I2S(-8yt#PB7HUH<|fy>`YR-Ytr$+aDmlH7KBKcn3D7%q_snJb|sMDyb!Gtl*e z;TaCkY0mL@hL{zX_lt`#%tgG789l`PmWoP+xX=Gt}0)MBjsHrW9$H{?K54gcf2;)J{}fpomyn zT)COn$^z{s6WxZwK>nVvL%YLzYpy?lf2Ma!yC{;?tGR)BS^Lfv@Qb*!{qxbU5pjv5 zlfA%6QZKOQhD$`Ni#BR(J+%w`bvAu1HYn`NABVvT&}~bgOBxSXE?%uiY!^6C_YvN% zO)!3ZFABU%9p&-LUo0fZU#?9z`|c4_CEMqG=vk=4Ibr+p;H7dsYm29RodW*QKq$}q zoU$IYIv=bZ+OOs@<8fk?ftz^)@vCQw7_>%jj}U0!u9OzSA7dg}-+oM{HEJkt zI7(@>QXbbZ;Ez&9J<+OXh1-~fWn388Rp;MFiA~<)47&Xqn2E?#sT!nAreGpX;6i;gIWxKV zl-D?x?M?UP;!MhgPrsgO+9cZG7*VHA9`IAyE*R}Kof}c=JCQ7em7PYs4?_mxaBo`HCxnTaYk&g7h(^Ji4u@Bxqo9bY(G;Jh5ugq%kgyu5e&0wCC>60%TfC z&hZ$XgvW4j91BFGF6&8&FXv_WxuY<}2*~R43mA>#qTo$G$0R(irgWbsISg(>!0vG* ziPnIX)nhx&n#W&yeCz}&kHufdoN$rVky)|e+VfftG@Ci^VGy)dzdw6epB*;}Hc?O* zPUtw%@?(rSd8GB$l9FyCmC#=ty79(C)~uM zmdVc)&guyHA&fhX=5n&5*q< zhJS5zf7kxt zya`t@5BD*CA;13>p)@-Av^@(9V-UVSqFB$*G2@U}y?-(`Tf6Y>!}jnJ4n93$6TU;b zs%S>^O7Q$j6ZxJ5eDo`qegr8pHs0zal%47wEmt#)=PVT#=!wfI{K>aOk;3G*(L(lS z62PT3GcS5$hoY=ncZr0CB@`6!mGTwXdP=RY;nU9rIn=k2NCLFnGkO;ZzWsg@FCmxP zilEWUF@^<+0HkJFV>cYMAKV|Z5PwjZ*P?~2YTjo<-S;Cy(;{YpS@!Eq{}*ZB6d30h zecLu|(%4QKTa6nxw#|u+#-3?o+qP||vCRn^+xDHG?)~3~`*hFy_sy9(`^?#A?Y-8T zOVgwIMxDH=Nf30&{$oALF8Y0$g>Be$JWBk=*PVWE)q-H(Qk43lx-i9irLnWLlImJm z{0uU>4u#|39(IbMCU@UDR~2`uvtLO3Q`LB4u#!3BC<7+lgvH3Hq=&+O+gp)Mq=fE{ z>!Y$R6(aV7VL~aB_9Za*dAGs=wX56L(@(EI>{Wxe8vL>){nm;fa3ZfI>5SOtE+MJ~ zxW6f|R|-Vz)C6)I&Nw+`;lkQP7xAOl)|PcNqB!YtBK)5RCwr_?VO($6 z(wl7oLWm=0ff_&1`PoU+Kwh#54JgyruWZ>(0qLT*PW*Z_?KJ_&h8PPgts`+>af_nPZ!v+yG%G%Je27m!LZA@`uP>Kn&5O)r z^3LMAwbMG5!KY;o0Q}oxbNm(_@1KEW;SNA^qAJcQ*~br;x(*n6@4sZ;Oj2c#0dKMR zYjO<^Ay=fLqnJeTbsO=p8jJCq7D+anf-@YooGv(HHs1Jt=BL4si(&CpP+R+X**5i~ zz@NJVq0LS2dAfP)i4~swGcOkXQ5VLh?1Agm6e@D|yu-n#S`;+FD~#K#6os%CGZdn+ zV{*Y7ZyfhyQc>0j=IxPltIOZ{XlyKh1d zMj2LGQ>8?h&E!R&-D!P@>~?tK5gROlMY=!p{6_mGdpfRC9D9d;zMB<-EHQ0pAF~<3 z3p7ecEdpn_9Ge07eX93Rpf_&CzLSJhJc~=-y5IZvI>sn{@{KA!8lCaRLY`=3VUF&w zcvNFF?&B8SXd&iTqY=9WHGIme$QqNzmS@IhjwwvWwo794z^Kzv*W{i4)$7`$Gu+rB zwBg?VH0K|HybX4AN?km+ZaD}{Q;4=s9rM4<3#^G2p?958m~?WSA@`gIB!_;zkD;5_3#_|inLr4NZK5HN zEo1p88DL6by>W4I`S=+OWz5r8JK)TP?|`5y%$S6^)JFIvGGFdpb4AxFx)5 zt#>F)ny%tbdD1aUCh+(A<)mFBhH)s6Z+AtL%@AS6hM8O)KTo%B+j;s$_zuA8C(4?= za!+}7Vgy`0i54!wTFCUb8qY~F+#YPLi^{NgPN_})@)GZmP*xkt!3L1@#lbtRvuCV^ z56k(iS>->-pi5yi8ge2Zc z#;Edzw6k9K4RvYkN2VOAUr<%1A!y_5BwW(b^ zP&k+>=aJ|2`v*35kweaY(FBcZ`EC~Zy?leswFWF3))}V$^sjdc_Zrjq0O4a7?J8Hn zq_Gh1;|YYUGoQ|{y^;oTnnTJukQ>-6WtrN}?(Y+6gxExML+-`9XP83XU3tl^<*v9s zQ=c<7&^q%~dRiLzjBTYlH#)G=a6;dILwb5csPR+)rNpAiB7Pqpl2o@i zzb*EE>(T583D@`Q#^vc_m4w2JeWjauti>#Y9d9@4Y0QvPZMI%NfS9I1>Tiju_=d-5 z|7>*0QAj3^d`*_&wU-#mHkGv=;%}E~+lmw=DXl!R3Z=&ly;o+qwRT;cvD@7y)M_+f zKB#GH4bOJR^9QGnZP6t#wY9i!x=L^)s6am-V}1qO)SWAAQmvqE)rUgSYVV_n;I! zkLN=Z>s$s4oYcuQ#1T3!L;}C{-0wnU+LZH^KUBlmNL}q~H14jm>GyY~o7K+b6<(S8 z;B;CbnsNG1BcuJjAH>WJr&LJrO??yl82fKvDMyY-c1|9*un`7?O$POjvL z;}T*&rNl`d!D^kf*;PTnw#2E$K6@|{7z<3k5_0x|&XC3A%;((RuwLMr#X6VrB!Xg| zIt^l1qQ&I?@O&adLBLE<<2i;h-?i|jS9FK+xDa2ST40UYaHCV`@o@?Vuj8C7$?p*_ z<w^Zn7#)!kf3gg{Om-!iL%4UkURw~*)j=VTR0L^jfM>5^tKpqgjbMa8qZP# z1IOnpjnff#rEXg*$AGbb)4<$x+o#5HIwZHD!)9iY+a!i#Dms&pQFFC4fXm%3&RvfK z9CdB_A(jm%WP+9@(VhZK3iGt@#lBag;7D zloWsT*2Lje63Yc38vT%X8?@nUH@wg0ERp=U0rPYoZtXc^HyG=icoOvGtNR}_>z@cu zr{ym2(X_EPGr3^V5f9i>IbsGR$A;R@Y_`v$1~X>AE)!g?pBk=%r_GBMg|S|>jy zFq2aY-GQcO6A`9T0J{7x1;rZN^zmUpOG5_T7!ixhD2ubc;R>IbFEZVTDF`*ah4n*-^%rWhvkoQ@>{M@!$dQDbrtg`hf zK+n=_&l4Cf(R_xm+~5Ty^_D16JBVk|Q5|u;4M5!xglqTwW*=2mdUYR%k9p5{4W0gESeRh6yxW7C zmsp&i^wBYO?fO8uBNCt|fQDNH|Nk&|`d8^2^Fh^NCaW5#=}EzK*U4F|vubRf5yK+k zjz`(?>JLyW_EAiaq}hubD9E)4LFcNj+O06WR^E+UY?i+gK&hu&(op(T$ zZPh?0CIIFVNg1r#P|?BuHdU9rZ9+ZXk%x<4!=|h8TH7myD?89!P*?jJH3!Xg^>jx* z=6RW$wx}HBiC1cwT|7MN^3dBopNOA${ry{|og{cTAKW2`y28&RT+r}T3x2j(=2%%~ zdFD4bF*Uzd-aM9mSC2<)k~RMQ?Sn;Xkfrvq*UAtM2{tL+9Jx3q@s}ZFaF-vyk>FkvRuGGA%FFS z-&3W~F&XkvmC^G&*LwSes1Pt5a`^u>e&dH1;MiP;!~Vr2{m%yC|4;qNjXhh2j;gVX zAs@MHRi_M6b@_Yz#y??Dnk!rFaA4U@=)DN_VJbe0a(fMa3Q`Nc^Q@@DrYQp^;MCA< zGJU95PwjH-Pg&`OkaGVX3YaDQagqqH9MLbw2d4#RY)5oQV%;7`n#NE4Gp+~IfWpc< z=W2emhW!s4A?DoAc%$Vo|2+B5%}B+Ydd9(T1V+7wG>G5*T3qD(;B$i_-ZoFCuVHxwa+;8=KZ>J{iLg~0;Dmg zu+PjVYG)1`G5^n9Qvp_V0#1UAkU>fH)%*4s6pm+s{?5$=GINq`x!(TW(@-s4faK|G z{ZUJ5=*_3G8>X65)-t7CaAx-($|7AYt}#4Dd|xrm$QP;onXtycqh1f{tu^t!$8~@O z?(GE-$du4@Aj-sLTjezk1-jA$wI@HQO=2T!f9si_^7@c#ySw0#;eWy6I4X)p1C}KWSwpD#(qsdVf$spV|&

F{#(Vhf=f}J?$e+;*%!uTCxv?}_ril#=rv`JZ)@Avj}L(k zV6s%^uHz1joivrDW}{=F>XCvC%I8G1A0H{M;UzNv&}eB8KWjBR~Z&ziCF z=batoza5hw>%3h0sy36>_=d%Xw^`o@4gxu`ZwChUBv#nG-Qjlrxmq>7POqbML*wZd z%$%VdlT_acWRvi4ds+GfoZGhI_uzKu2b@m$l&vw-U(%|H9`Nw zKO!MD4iu}CZSm5tD7@4`lYUJE@o@2#Zg@5jYDYbuX?Zs^be{HX0j=cjGR@yS~cDDzVZ*}_O&d>PjwUT+) zB#u0>ZvHxTug5)f5jmS#c2%JVWt%ZPw76_0@pVlRziFr(0$vwAr`MixkGe^Ho8 z&dh)F;6J@91HFka{wA5;Cw$yFCUiu1ILoLKR@|(nQ}b;_n+p^j62#xaYg@>7hWC#a z(;S$H>AF3$^l&?yxW5{j#(z%SZ(FDil^nlSjlP?89S;$RU%Tc*t2JCed^EYx< z7a^kYkfFzyyYb4;$j#~E_GB$Vudtlr%D+Ka%E+y@Dq;OW+9bwh%r#X6bRv!Ba=+fw zs~eaC;z)@fXcO2;op^L%Zh328k63>b@8vqsYS9g7MwsJnCf#m0{M8!1elB{(Na^0S z_Edpp$nJ99Q$Diy@bUS4K%hoBllwNa=PTJwprpNPtJiG4f5t}ZrJZ6Yk*xVku(R+@ zAZ5?)DAD{yJM<9UbB-p*?9UZGPY84ldRhJhdMgoF$eS>G8Pi(4{o3im8oI3J^fcJN z{8Ff)-qX1Y^{zOBrn(2?i!Pj2@w~!r<;;=#CH(U?SV4Sn?b~19`R77#L-5VHd%v;A zJyCCR6vU9L{zPsjly1ZDlKNuvWciM2%&eY%j7flicTx&RvezhhG-chBA-OkF!*WtW zz7lon$fO*d0Vu?c&+(>PC<e-%}bQ9!cvu4!-U=peU zYPeL;fvVOc`&{f-Ccn3Ib_+A4#8+i%9yA7XHC}97#3Bc}CrjU{LV92lc@!IwTDQ(( z_BuiMkJd4F*SlbWdn#1f36s~CC(SFL>D`ecUc}P^78+jct2bI0I!#1 zIq~affk_!gHxI-@y&?FSX>Yn_OEvWkh_LQX(`NG1olr1r3SPF`aBoL<*VBbm=Qd8M zX$ZWjdQpk5c8y?uw$BGg=jW~NuNU0N@Bwo86&`EL0(M;KugA7>itE9lH%);gupkV! zC*!5OyT#o$BNF)l#<-ntQpn}}miTQqtxhM<+k}uF{&?Ra^QHI2j@Q`s z$~7kH)TTav{krDz;o(-QvOTs8BI!`)@#yV=`m==?Vx`hX`xm1R0y?bi7}9Gy+vodF zfFqQV{UT9mjT|7!X>KOs7?{{Ik0S|yI#JaT-*yem5_(}9d-_E}cTNI1r$hS>i&5J7_fQaB&s_sG|Jb7@kr z&}M;+1?S+YhQQcU^$#gM;0@#Rl#{zN7QLdIUAp&l2x2?$o<>EgQ9X~mRASN)6 zH*fNdl4Kvwr~fwml(2E8OHZ}8m`qV2I#P1Ur9lwjoC&v)_U((gc5>K~NlW@?7VqN9 z4f#y)I6Uy#S^l#2gm{I*WjxDT<^x?`U~x4}Nw<UAf3#wR__II|R@Ki$RR6r$Ep~2AuJqrzzN=*Ze@KZIX~-u#q%^V3 zwknftbk&vyN499;)15>C$A@SX?E8Y=(J(;-DgNmT$kKQ9m8PZpHwfP8X+x|>mnVxX z9$fW0m}j>~aqc9#O&FnSR|*&ubq<FX)E!tDI|j{>l0hPXi48l>O)#Y3RPBk7frp)Y-y~zQH5!K&g?eiR~ zSFnXKA4xZ4CA)7DB|9}Q$>%e)({En6E0yrJs%Q;zvHx2-H(6BxH5o?dH`J779Yo~W zHgu~aEBS8C8GxV>-#lhgQpfUtVg9bIp#-8-M0RQ`-sF!e$ajN|EW4cBFwmQyy_Sxb zpXqqWCT(EzH|4W35HT?CGu-_0(2%{yIkW*iN59XG23j=&5w=08=U0 zCo0eK1tSF?H|`17A6K&a;FDhv{mF8vfb^^7{*ig?q0;@gGf8HW8+?2=#Rmk87v~4p z>4JD$_71}7$k<)^tm7WBI_H$bge!DfkgBB7m0tVk@JpRmaRx9`Z)Y76$u#eaxVgaY zn2TKh*l+#cTTiL}a;xz)WT@mfdKk29ihf~b3x}y_Ce!$S-N7y!=)&oyyjy*;yX^=h z5%T2)H&zTu`QS+iG=#S)c>dwxMJJnD_8bA@MGp&Rz-6=g9t>cuMQQ@uwGSom zzf#)RGxd>a%ka33&IPrx9FQ&T1-zwhaB=UHN4=RM&I1q0)UyK7%UT-N72t4Ih zl4Uf-i}sdg z(TX&7_B zpOD4#cW20?V=B$CV+xyNe1z(Ac~3c}Y~Llu=ZW4EG>g%WJM{WYoqTrxa>i;TP5a{F zZP4?lOL2yyGILHc(AeK@8x2>e4y~bO$&TRwr=0gg2QF32s(*I^T)f#-fk(+Muy*V4|b)(v_9*h4QCC-^ey!K;&h! zjtG#lu<=fLF_(B3zcXB5$1coi#og=ID$zyt^YUt;YvpCzf(1;64uS5iZOSD!VvP`G zxqJ+Nl=Tax@#1Z@O&k;1(cr=s@`@tJazmK}U+M)I@`8%UE@q<(_<4d?rm`ACz94i5Fa$Zov$oBCT&mEQ{`wk5-B>w<6xBoJAz=7P10g=Ipk1 z_E!WN=)X-L8XYWAVRs~7q07Pr_w5;-)Ja%Ai@qvPjr5lY`4-lt1|h5|0(9@Ufv>TK zN{oH3i+i!_&HUJ(UaL*o0Hrgbo(Ht2x4cZ!N|rWqf?%?5^}K412Og#)X1e$)r<>GS#gRN=PbiytNk0`t26UM9 zP(BY3mxZRv)d9Ya_KWjx8$=MIX}egb{8dEZLaveW0jKKUUheCD6-)`e;*`~>+LY}c z!59&77lmxY(Y-_hiWRYr&cZe$^=4Db;6{^lJ^b5;=nH<-2Pqz73Xv5S`v$tVs$vMQ zTx1{%&Idt^U%v#SZwhcdla|F*-u^XJMM}7J<8~xxyzk3%vwjrRlZyFKu#5;MZ%~Gp zUy5yJCH5kKXKl*$<@1a0^iYJeO~)F0x&AGa8a}^`=-^o9_-A+V=N5yLc^ZPfExYNC z)9%BJjLSYgQTfX&hxMt1J_=tiB%A$QpvS>Cnut2XeOCjOEOMGd9n>l2qlMgfmy2}Y zE7Q-J^4@z@Cnp>c5DZYp0rcqMl}Z%R+z>-Jl{ImFCW0rF_*9*mXpe2jqJQfH#Diw# z;HW%AjhHJi%pQd#jga0^NDB3jv{sf&v2^$K4PY+g|2CLtvT^5PN#3aV{0!wX83D@w#{5 zhdWqy?3E4$jbX!#h?kfUUORwY1EYZW5fjzXXZhu&kG$oAu^(LI4h^Ynw3n^=vmIF_ zC`|kjhye2%zA?@s)u#vJ^ASysOQV7}6WQGKoES-pwFU6K-cqY@+nvB>HAPkS zbR&1{x@)r8HR9UW89PsSwWT>iIjY|6v84)-9C-6BWF2DIh-WO!Ao{P0+8<{HjGGs| z4$`df9%A^%{Ad-0t6SfcMs@8}?>kSlE~bz&XmH|OVJ zXYtu8VGv^#+@jXRtAz5hBa>To6U*@Qn1K}Z%`LlHJQK|)j77|WW_sR6sP0JnhD_>2 z1%sXAO!dCqo5wviZl8%i4m*{8Yo^q849^iTmq~yenPj-5QbC*3mMP~rW(8bwIPVFW zT~Nsk&{~36*h{a9wr-r^i&qvh^`jeX#x8Q@ieE6xcCQFuc}5+oV-VqvgUIuYqVs$s zAq3m~_-Gr^(Ia$0+I(Ulcu(7qz2G#*vQBKWQx2>s2^uLpHH$ZeT{4V(|`RduWM0|J{rGvQ^H|{1i0d#7nWXOZrZ@AQ@nMaTK6pmkj z?wt1%W5D)v`vF_Ack)COHub@`$fMHpVS&yD`{A(i1JNa2s=)G0eZ^C|YRZZ(i;8E% zHNi>){-KrO;I6${MzOUZ^?`?{K`>mn1G^Y(b!}kZ@D~l4)HN|uMTygGS%d5jVQA|vpzl3=xC{?OZj%j8On=k zylPms3ZJixFPfaNjXvAe+`w+3*nV%9y!bhhSOtN?uHi1+6kEUZdb6ycwb1<}0oW0w zrZ`*>e4A=K;Z`CWOJZC6mCwsLZ!3#tTUG(0*Y{8h!eh4#LX|nc{2%S+_@4oWBaw3f z5<7$|Esc58NoDJXx%ZV|PQPkWdE0*R^jOZeuLrXeoB3vauSdTz5>H(7BI}mIlJt|Y z>pdMuFC_fC)E=Q+zuc!-6F&7_ctJ>PwR=ZNd?E{3s)ctv+SjRH*Yy~vERzyF7#j#! zm5SxaMVP*;&#G|qiu0eST-_0hTaEOCf;|vxmCCHDxApw=deUSgs<2s)CWGA?!3>hP zo00H4Unp}4F!zv$!`B4Saa$DJoCi|O2hIXjb3%V8^E{1{Lwu9_0AbV5A`?c)8K_v! zkgA~S#s#_7m?$Z_|9DdiG^y_Cvb$qBS|I@~6>yW}q;P?Q@pd({BI<%S62NL^XZfOc zmH2SmQpYKU(-@T=FAv~PIojdd<)@__16!Hsvvy4cgg0*q;pQ4BV^#gx^+6d%&~jR7 zr0SGFFEUZ|o6yN48?VEwUB^GfVVezpW6-;wDCubU)iGCw3xzx$3hZ{hWuP65DVkHg zNWD(ytiqtzYf+>2q!|s--6C6wYv2ON=Jrv|f}r;%g17rW?~A=_W+q%~pQ zv=FH`W+-LvvIp_&e13xgy(=`=TM~+N2?!7nO0V4EJ*O@?1K|q(*1K&t-8eRotPHz0 zWvXx*zeb5DaDece3a&GK9}BN*f8$HFO23KF?tHr@7CgyyZ9ez3c%2ZmkF`m=GBYEH zN78;I!tMs)RRd#ZsE&V-8H`Iy{$@t&$z;<$7miw*ym87pk7cv>&zxzLAc1I;+0 z#&`4QCKnpgghIOknYMeZz*q(4EAl(J=VYOg39P|edin%l_+IMII9cyBiOw4s6RS-# z5OrL*27>kM>#E5Wvrm-t9jLbSb%_dtL%bdNOLF`XY<0l2e*XD zc^OThRQ}WRdXh5{h@_20D}mMPMDEEt&#V~~IF_$U&}q6f0N0BB>`WEuWFu8`dj4KV zS`o~Y=(1G+-R*TA?%%UHYQ*49*fJE43CcP?=fH&gyT}Cl+BuhKyp|#rH$`#T^T^JuKuxG4egx|OOV)9Sd zbiGOsWDlG$wl6GTSDXajC^((?={rXI6S&jc zhv;LuiRK~o1U&Wnn=rLkISNm>;?Fy2M`iwoyO<_SI<=2?)&G$hiY`1;{-sR>^CqAL zGTw0BDLiB6X&?03`$)X@IYbq@dJx~KMzun}o`&gu7^Tv_NAs#ar zOhEHf{ouI}1PC6&ctPViAAKi#_Th(AD9h1@_&U+sOenG^CjT9#4FMF3{;s#`t?0+( zcK^-nythv`S6t%Hi$o@`?b!J-@mR6l`+I>us-=J0SS$#I>2@1ggNb}kYmoCKW;ahcvFD6p&KP`pyFDwEPcgjaU3F1KW1PVe9VSEz7MVOFO9KwhZ0kBsgVNzhZ>Me|-9!)a zkK;)NlMMH#={(yen0#XsBbW)9v4<%*Vqg~6C_}-l;wqT< zzyril5u80dnO4+-K+PGXC%fcxc0I2j=uWoY+vL6^5Ykqqp`Taprlh2TOzd}PRdau> z{)}{v3=}9{w_I8Jsd+-t`tSx$mFI6LmJVC@VaL8iM7VZz^w+H11hq;A+zh`k1gF00 z7=!ir;)eJ;RyjGyK$H3;!NHgKCQLHHFDh=i6Aw!O8+wq~`lmfpjiGg7NUG8XZ8o;v zc$>;`E&9#PoWJwdy$j&(ZzB^Ywck_y&;wh5Lub6O#qTc!aGZ?2AX>VFq0;6lJo?m{Oy>tNDoCM0+Z?&w>1Q*Qs z*Uz{;|A5PoDrh$ohpZXuta8tMe&7O!%=kJ>fv_|ONm37tCZy7aTL;&R`-^czZ|8w} zu~aYmXhp0)sq`6kA3-{Ad;5#7Z?6-ct7O6jWu8&Bc41rrrMHO2*xQybzVt31dQl!A z|G6wtG6Ls-a?>erj5a4x#2^D}DTaqBExWb(!NH-d;xTH%k6RPBy$ZfxZc^x^(i0`P z5r*tORiw4W5(@q4T!T;^BeTOS#pqKP<^+E2$QYbOH$#F`+rW$c{%ku3|rvSpsS)H_gn_6-mu?@H2FmSc4AIB9>mD7Ac+T4m-?u!85tzYrJscI;&O{Iw|ct>Fu zyScgnxhqU;?zi~N;gzhHu_ZL*+(?i@IJX88yXD7szoF#sA0Kbk1CYx+lkc&uNpMzB z8;5MEf&T{Q828KQlc%*#nUyN+L zM;?<~Rm6S*tLI(~&;q_)Ph@7=nd6Gt5CU>O`DQXNZ9cm@O^PiIH-cs#H5a%%&QtpQ zI*Aa&h03T|AMZB0Iz!Jau|u!Na9V&Zb;GZSxDRDNuA)EH+v|}n6h3*7jkLl_3pDvW zs{y^f5rQsQL*mm@swotQ7jX~~%~C5AM5mbiawszw*QZ9e$s78-nD0jwD?wZa*MqO9 z+rk-r!dudQP6Nq7)V}5fGxeo!O#!dJ-u>zF30@xx5@xW4Yf#6^#vx1g|41tYT_#K& zk{NyN>N+(0x*}s*!GJfo%iVRA+-wCWisvUtk6xw#n-^tYidjZ53D6{`t5fj{<}r_Z zfVZYJM)K{{ldCU4wfi*nsYsigCkkHmM^Gsmj|l&eSOIv6Jw8`XP?8`{j~{pkd{%CX%QNDHX89jNts5O>7v$k z$6(B@3q=vUjm%(UNu1^U`ZKR<6EB9sl|##D{#WzPd!v1~P#H~rHU->50&xVS+)aZQ zxodmkObk+Gj{eVSnjFz88R0i+zrXjk2bAyTvQPk719kn_r&3X~!IH)~>7>W*L8N3b zGu$?v%-pqiCOBipsUfmEOP?gHvfZ?k$X{&U$w81aYP~f#IL^bKdu$K9^)mf} zg7u7MiMI?kOlQuHhXeP6P&4XM;W3iWF<1Flw@=m~+c_mhcTX$6h7{z0Gq9Ru<%ky(j>z1^E zM1eC&U#k8vzqGg)X#rx>lIY!VrjP12ogL|sYVd9bjwSGY6>kc!;&S!5Y?mE#r60_zkloI+G|^pYqr8qbKAdpZA|PTpQJ$Qo(6F z0*?(nPDQ>?U$%n}QXTo-M)xLof~g(Ln_p)hu$T9PslZLyT@y!m{Meykv6PXozKAyP zAi+0Ye8yYVz64*fl!RG4l<7-DACZeko zLIK@}sGSKyGP4TR42yQ0-XIo^IPE154&4SEWK5vKfR-txJDs7Pmi)Y+0N`;mgtOZ3 z(W1;f_|Fqxs#TF=zZDWanLo3{HP}=z z=S~BrMgro5>sf{T@@9Jbx7 zQ)yVxVKY&nX4zWT+{V=`cj%K65y0D#L+|>^#ATB@s5%glx)tjXtr`*-flSIyEPJg3 z^%_1U6gV67#u||NJy5ygv(TvlrZ)5j{%6RsGx}$!2MN~m&6b_WrQu7gm&>m72DQH6 zc)};VI`n3PLt(d^@V(}BJ$!kwjbMXJF?EDY8};9PwmrD=TTK!UhCs7v<272eb8Eem z*jQhaS}#gRu+p}btm=q&C;nB6&}N}ni~P)cB-O!I2D#d5xd<%+f0=S}gY&rR(uK6R z6u*5#zO{)$iZvyF>F5T>7c;uGyz=@u%fw5n-Lmp?V{d9~gxj=@Mgg7&iVzQ>5|zw& zd>XJdGFh4!q25v#6CN@=3(*U!!xc+2r-xF0vN4-Zr;EPy`y458H<^#DNgb?JLcI%r z5+8Rxk3a`}O6A_M#{pt}q85tfXFvS$8K>oUiqP+D`N=0f$6URydJQ#HSi8tJ z=9tgwMM=+(Z5g#r{e`aTlXMdOVHIzw(Ij_;l<~9x`zdDs^gnez`BeaVi=76pq5o4A|{G-S_MtCU2aV;ZXb`bHdF>Z!mo zd?)duEcGxWh(SS8jFE80N>@n6bdm(hTX=ex`i{e8M%RR|bRN~0LR4!jNEys2jt|Nl z<$nI-vA*VPaOv0c;I+~WQXMpaG_5-OMlf@0`xtXmnU%cBvtX=$wfF&{uzmzbj%8@F z<$Jmxu1zOxTHzzatrdJ4N&4*wa8MbZwaeg5`Vqz9eFR4{#g82aKR*pc$IIOZR*hmP z3n}S9_+ugkW5!4mBHWY>uQ5p?&oqWH+ zI0eUP*+DMlCwDKQL3qg-tlPPg!{$7%wzLv{yz4ZTqoT4tJY~oE1mg*+B7` zb(6ME`eRRkdqv#!8G`zWxTe)M1$CX`IxYpClD0_R?~>iDUolg}k*N}_YP=YNt#Rb0 zRrCe<)r6v=P8@h2wmx;5raIBmF=r8uq1@lQ1-q=+^1pj`YDdtMh~gnppo7Jww(i1z zZ!yM8nXDyNm%~_?tV|vh-CTLIZo)sJz=I zi4%NM=OJfm{OImpt13tfZ3g@w7Qhx8p0z`CZ0rMQvThafC;@;s4?vBO{W^pWXiKbv zRcB$iS#{OS-hOO;6sT~kwQv}JQY{uV%4B9{S&QVOK@VbCI9cV^lP}zU=H`9w`8F{toM^Bf3j0*_2(Lo#DZHm#YQ*jHs$-rv=V*qlRwKX)*LR1sS{+GH`8C z(5b;fzE{L=665Q0H123BFNN2^%%Hwd&`pL2U!|#Is~g|L^jgTn2KIVJWe>7bNo6Ky z!f*Q3(c1IY#{;psXh$b)(n6VIJO1GZ3b?NIoh&<6_1@RH-o6@l&nMzDlaU-axq6o1 zv>w$+zJu^8{>2MtPA#|Mcz22nHCVM;Jl5?}Jyd9J`P9OfR&lQ08-?YGN61ee;S!gv9w4RO17GUHB;LZ>9p-m zTw$~m+337pW!*Jb!+Pcm9po)%r{sE5*i)Zq0V zi;=yC-%H((t0p67>wOq1ipQl;F*VG^4+0NxsBa&`8utFu(vTmjDVVIP%njkwXk@{> ziOs`L`$#rSL*{c8sZi>}D)Aw!N%c!W(I9x}haWc*BDM8$e2xTn1Nc*zGSm@S(2{?z zs;Jr2UQm5sv*(9T%d2S=f+@{bN(T}FeO6Jbrpt`DIAW+i*ksgBjI2X;oLWYP#-95w z0*MvuKAuPCfz*CWffD-q0nzC~ftUxmsiVfBrrB}~hIoFSC9EMSRBa(@ec`^#zhca= zt2U88bF!>%$A^-*lF@*LxL-}eOa+k=G#_=5S^^aa zQRq;`G#@K~c{?>ssH#X@c^wUo!%=}rz7&N~zpyZr|CBMXy-sP;qgnaFaYYdA=a8D1 z7|LdCpTUu0!EiUfkHrdPrFgjFNV~PY^dT9)SYWKxb!n&y-fsidu1JLKXTo|bA>mk66uSRupLUOACZPPYzxe6-#k|)GI=_1 zXijqfnN&LDzX_S~uHs5-D*_~zCjF=?1dOE(=&)(l(oZd@41A{Jmque(UR?6p6QQ{P zB>KDV^Y`F*KAQ$+AwAt>f8;V@oF*>(*&O-6PXhvuZ=Yy&Gkb`Fod9 z(I$^#eT3P!mTi=+Hq9DLF_;C*3uxUe9uO~AinQ0#??+oigIg5jMG znUkdRoSrun*}XI5ah_Fqq-}{HCD_(eCE_hjl-jyZ?4Z8)$5oea?>{8NY^v`LmmN5z zN~-zv3Np_Y|1!v}BS1-Q#&Efe)i@4K{n@fXL@s>0K06iI#fstYrD2G&wRu;MA9Vdd1LH)aVbsg{^(7=Yh9ay0<1R6%oT z)WEQ*6OsyM3aA4(?-G>+!#xrSpH#S5?g#NAla^s?#J&4mK8gtZ(-yI=9P2P;vGY<$ zgYi#N%nrZF=TX_iN7?@9$~&rdFRs1f({581o;z#X=&#|(F7k(S?_r*5pgA%E}C6nXqy zUG=3DIJ|XEpKcmtGBWR8TPNgIBJc>0slBxUOS@U$Z#X+*tUm)?gzdF_x|0_y?zcBg zPeD}+Er{e9)GA$jUtTMGql>(D#pzS06dx__<{n!D60ylYpAar^bDVwEGie}-1Z>t| zdwUnc2oNzow_W^(S1m|1V{aSN@)S$uV7$Nb*-P|uY4Z$fTaC>LamFKhg&vS|I5H-w zv}26a5tl=+(RnWMb&ub!^+nCi9F<>9F=VCH<*->dsq_Bng}rSi_E1CdNhVke%NJqn zUSNDI;4H_-B)cQ=B|&6NU`A0+UYrjdqOq9iQSVY}X?(fe30h-*JRV*wCXcP2z@7#9 z1+Th0NPjyGYHq<3?Z`t&;hk1mqxU>;#|?tC@_aVH(xRlh3EGA(Yj8S|q#;3NgbFe? z)9;wV^facf`JKvcgHy2-I86J|L+XpHx6h;W$M-bt_eU?=--Y2{#I(d6Kw5)7^}RnQ zOW^BjP;n8wkUpCtnI@7M;a{pGR?Bv#=B2YZq3xLBV&gZ=B~r7s%Caf#wgf5v9uBRh z)XhF7xnjfKKse5kwyYtotX+GcGReNCFSEq-v$$}*R|@tUfXWac3ZL0~d?GV_`O}+p z*dqF(mbjMb`H}KK$&5E->dzjnFooMPo~)7lAl)wjFFgM5Zg1+chGT*5?EzcUg@}>N z$V8Ih_h*I>JLW}#wd;eCX++^r1^7Z8HyOb zas|w2u^7x1sdM~6UMNsto5c8%Ds?z*)TMv*^-;K(O<++nK_{YpiXKGD9DGJ*dCH_Y=~uoG>c8bS zJAs)U-tentlCM>I&AK6|-JrXIcXufHklst$x}m?SKWgOb^on=PX;t@TT&l|AJe<$^ zys;4rJ(ILORxsS%1IDa;pzwRy&907SI&^wy9n*;$NQ59*ZJ}O1SiSue{GC0sMrIUA zs3pnFNIh}`UlX6*;1Zo~4aHdrnfsLC{Ur6g$ebU|o{sjOkA%;#Vn!=ta}qW+8*NH} z;X`=nUyblu+!275`nlo#atk1rhljm>y1TG&uT>pQExiYSiHM6B2Di)QSi-U}=6Tn= z9|zH!KLM1c9c^AiX$Q9r#Sb)R^eWXODi6h1EUK)N<^xZkMjPp$9j4^RkP(w|72{mA zD;jMpzol12cve=V107fUp>JPjG5}=B+q&pjfy z<=*stEdRJ?ZYt&QoOMs1?;QQ3QfPj80qy-{Rz;~br@TM$`J~oUG>-qT&WRFqOmBUz zOkz+OCz<&v#(w>$;bB^+!YC~p$u}NQD_&&42HEfuqWlu^c~c z5-(veTW5jBfJ0z{*Z&IoUJrp0=H4KP7%?oNa6W`(T;kkluB;Z5*NQ^#aFyOsQd zTbGj2llL30>6xFMvYzYJRIzf74(h&FTIz%K2@j8b`kz7`im>4kCS%h&;yBUyo)00i zQbkK?>*3KKi6PAIYbaDdx24Hp|@p_ilI104`t1_ zlJ$SuOEBxafq}I>C=;qv9-~~RD#fEmW@1#|Ip~Dt?nQNk_E#Yq$4-6BwOGo+&rVBR z1yMB@jN;^~t`yW=M`jZ)QUo82F`_8alxIWYT`$DnZQ7SDL-msz>y7x#mT~gm6)Au|9f-OtWoFFz3RZtW<<^YQFI)esDy3krJ#4v@f>$vrU&C zA@v5dcVK5h=yo-|E4B&FWorOnpnhmSJYD!J=C7J&U*nP(1U^P$B#5rM086MihDRxe z=x{iT-9!@JUJdcrA*dJsc%Hm&dpb(U@z%)d+Pe!)oOEpX+}}}n6LfIozE%Qi5ag8M zBF~n|C<7K0FQ-{5y`BUgC6}keGqS?qV7ld1Fq3MJuWaTy5BcMk{T4P2H%qcAojNRg z8%N2r$9%{{h9}HF44Rdge#qN3S>Q3DXtx`Iaw1#u@a@}*+vPvkv z;$C7qW7)VNgiIaT*Lcua+g|n~#rJh;Qok zd?EE-2ff-+IGk}x$&HUvNjS{Uqt6#!9_RPFU+`&ibQW-F0DYavxENO3r-R&GvCFce z?;Y7~Yva(Qoi(lZF_Mjs`@j0g`cftyT9?&&OjtA!WR$aJEVlq5Z`rXEyiDsfT0d76 zY7Bg&-LiRmZh|l9(bXuN9s1Nq~70)|cGUcQnh&%1zd=%~6dX z*_kb+OQ;}B6Y>Ni3*?z_H_yfOgYKEKsGmQwRp7O)75$fJ{y7 z!j3*ON!2E?59#G}TN`!m*VGI^a-r^Hv#mGXx$uUn+|IN}ts*WZ*-w-ier@kgFI2#P zRjH|wk;+yL40mdIf*SFmWTd;2*I4h5cLTq=RW^nSXSgU5@opfbgP6iyXzgaKqmWqT znwGu!HU!S8MjU)D0w(VMQ%o)c=m?KmgG5d*GEh;WoWsU>G|&uPAeyG6b%dd`GVFBTxO3xhiG3rBnKAdz57V5?%=k!A(HeTZQiZX_;iP)J!=D!36brn3EB7hDXLuBLi`sE1zgE*`=o^UN zYsw6(w-9ei=$(@G4#g1R;I~i8rw?DGiEvKVb@?u=&%n>Zk9U&qjvpLhr#b7gXfoN{ zug~=mx`~VG(@DWNTq#ONbeg$7w zSe2BL@RpnRL431Ec~sYK=2FQ^#bF(E@XZx{I6YYfj)o~`#!`FWv(SZ@_O_@&kt{O| z$YK=-PuCK@@|rb-CJeubRC6B(!YQ6-}Y=2q^S{=ifk?cdle;K{Bg|4FZV0Ml-cDQ-RHyEpvw zZuE*Rc=>53f{V-hjf!rH#@kkHJ?KDE&XGi!&8skzX8dIP^wGoX{Hrb5n{t;IOVpU4 zDs7C1XH4Ut!`mF#@uMg!iwv?oLsR^V)wv{1Ck5Le#r2aI@B5&t*x{z>%yi26LhnQC z&a3@x-P7AbJW4mB&#{n7uj>`@9^pEqyKm`KWbj>zTFX-XwKF}7vr!c$)GCGJ0MAlo|I{wt9e1gS|Cs?KF3XD?0$L{#+^du@EhGVH%ls?M+sH2nj+GQEr^XiB)^ zn&Bvw!6it&5Q$%nRJoz!#_h+B8jN9Xtt#9+5HCTwdfe5p^IJKtc4po5I85|R!u_g5whe@ijE;HR==?@eU-jxy{) zs#5pm3TSfbn;;PP@&v*{%90yTy$4ydJZk#Wd>IX_be1CV)Vv~AiC*GlnHi=>uw4=j zLyw7|Mfqsl(K7U6;EhWlJH-&^#D+tV=m__rF{p`~`ySD$?x|HPsWInp_39yuVF6ZJ_08cy z@QstDHmQM5*V7nzBJiXQpM}cQJAVlsuOqk245$D0+B~d5l$E<@S!#8e7#+rh6394k z4sy+=CD3E6ZGT-?AQYGS?)$#|I`!go=J7lbGs(dAs9igEmvI$Ggr!@|8$v1}CNZ*U z+VS0pmZDlQvE9@%TeIzX;0h#{@_K`V%lR;D>C`gE`J9A9H?6V!$}*(yua5Ecyo_G~ z1oVX@)2>Z$A){8|empJJO$+x0r@&&0$bpEv3J7ESEMpv&vnW%G(q=MdIw|yBLV2gTmuo z>G}R#A2=S^>H4Ty>B*-%n>j>koEyMhyy;1-%MnDJU_y+vRm3U=q?=})rb!PXzV^)} zpO??^v0!MIOTH`_kgg7!${0)*I@N-(DpSGSO3PlXvfoQrex$a?G?|IvV$i0Cd5Z*u zB$GN$*~fca*1YBT6nhum&E6J#bB`gKoMT=YvTc>7F-SObz%kVq4RGfS$mTU59L<-x zoPPJ%+o2kM965OI%9Y)zJHLqn(0w^ql8;yNJbGZ`!@))MVZF5ZnfT=blS?&vx?W$d_Xc?aoj7x8;8>6rbluEq291%JK}cyF)tqPrsa+4CQ<&TB z0ePx`I>9}myt}aORtdHmrq7(}q=W1x>1`o1dQTuxKe=Dv;`+ut6dY3L9gyo@8ZMs* zr3zQot~DtkJSSk7BR=Bg(izh~T#s29NXG4BYn1#&9k-R?4UeXxQa@{M`Ov1-t9^qx>scXNyV+0(LF!=GjQ z?Cy3pM=`}GNs>B-6Qt>)Im~gtiTo__|NRz-rwH)etgw3CD)nT0(ic#8+m@HZ${eXJ zn1dzRp`11q{5Ko4>pF$JiHpC>WS6q{jp~1+bU{GuWN(J=j{Q4szi!$ZH-dg_>Rb)E z)ZSY^;(I>ab@JJ;vCm)i*JY%P0CNJuQ#bug43p9bvUqHGw@|ie9(TFh;(}LZzPmQ| zIat$vT^i1}rsN9)1?;hw7dAaKK*Rghg8g@dx}#?1eseis=I+p?Z7_v6`7-BWj2dgH zDfC7+?uyNz>RyCPzYO!%n%7sf*N1H2z5%YU&|Qu~xK6O*z&<0vfis9}X7h`DP6dby zCEMj1Y&G(UqeJ-|-cLK)D(zIPuCpE!mVY3DKO%lWz5pjmZ#57l`<7Ty$SmCU%34Yr9oQUq;hP|M9a-CrRI~opoHiBWq<;VxY&N zqU%ab26-jc^mZu30{K?1y}W!FUs+Tvc&1Xh`7NBWzOa>c1-=D@O($e@yhYxO(2d(h zUusAD*MTtNu(4ruYq=yn{@Dgt%OJdJK>_n1)QhC(?+Ba8hA1%F*5ZG1hTy`{W^-%V)}MdR=H?Fl%%IoI zLrcWq+L~GXoX(WfrDOJ((Xx+}H+u6K#WJqUp~T%4-x3Gv>cb6MtggGAGG~8V3#UF4`@erA3SBlnt+0fbHO~Lmlji@vm+Y1n7c* zWID_Vr>#JrrUbvcHS22%Umyk+VB8PD*pDM7TUm@;UnIxfS6L&UunMdk?2_4HKLg83 z+;Z`9QKXGpyu4MCw;_pb|{`6_7 z>G)7?eNp)Ge591L)ammjo%!A(;R!&0+BV0hsmSeqLF( z?2H%EctL;ZrZFuZEM_;OBuucPcF8yTnywWzWmk`D%fFaPfI`Z_5i^@4+5j6xBlah8 zryz$kE4*PAfy(+fVW92YwFrtvugy~O4?vyM@VE29!q{o&;Ym(J8blW(^oGFuh=7K$ zQ75X?qN_O-Icot4F$X-I{qLrGXa!8_PHee8xLj4z3AIvgD*2raSQQK!v z0q*|}VK-Qhk?ofGCY1EOfosu=43WHe-mXHr^u)RP8v|WVNPCBxFl3aMOYz<944Lw6 zRrU>L$yQ|44u9Vzz3Ro0UQLLyvgpqAcyyr%Bd6$(Q>>+c~#fBmNs<-I#-n| z#7j%Z!+9T+;yGePbqd>_`;}}jZ74x*3`^9cd=}uX>?;6b*LEd4xlo&-e~qccNFPbK zp2oP^!~$73D%aHqJ!#SlxthO9#-Q(wM}3xywm3IEz8%AjdS#N6n$B{~(^;BPd)u2Ic) z*>RdO;L%wRSWzV(E4{(4A$%UbD0+b-TtdgQer>4+=((3=ZeX7@!N}BK0FeU58s(L2 zXW1E;B;iXTWRxYn05Lua7?OLm<3DZn{=^`Jv^3O@4>Tx{Ex4}|%2;R09Z3t&EjfA~IqVQ2 zv+nywT2FZ+%N%{ICAiQ+Z@mgvl(GjqlSV`gH~J`Bo&-5@_xuS?^5KyWcvz{oZs8LV z#DN02fa_B!r)9xZ)AD1uvXE3e4owkl zPN8>Tdl+;@Gc=2lTqsx~8s0MDec6o!kh_mM0@(A|XechM6-UM~bykcNKU63j89_L} z-hmn+Pf(_uec?DJpw>yQE8`g{M66du8<|IAme-2Ph}9P*k#v(l&=@cYsoDBA@`PV_ z01+oGLdZs+p$HjNsTnj+JQ*sB?U>+j0aHUkCxkIlZSo^&4ari$9xntEayL0S?Q+j+G0Ze9^wxBdn?N=a!*59$d(qmeee|8bPEvifA z9>e|d{(7~&m4)(Q@;5=oI!F_c5dc@p$0A#g+ot z0L?yS>~W8fxZAKOkYL34mNJt({t#lJks9zZa?p-X=rW4`Da=5A`6TBcmq);vJxF!0 z0XF7zA`&2<`V`zl${YwZM`sMoZLrCSg+kw?X;7tf&jx~s^+neHy8Njc8)_jq>qcsM zkw@;wHpkAJ8*KA*lDG%>6|?RXw;`Zh!+chv#mdN@TB<3M(=r+RtrS+mdl7sy2P!!C zDAe53s9=pQZE%NahwmTpqPJ4wLt*xW4~bL;^1=WRf;>tY!i%vI)=H&FB%914WxeUq zVeXz0cNS*jA*(KD2>{oi*aEt*Nb?Z;A?MHL}|RLI~8&a+l&& z4WflkM?KfNC^0v1EWe~Noe?z!X~hL4S^KQa0}2{~WinhByU7lsqD9&K{^PmTQ^v%| z2^&Se82TpvWvtmmPxW^ZA(SnY+e8D4u04UklwQqpX#V9pJYY*MOK%GLWhh?#ppf5mywh87t6KfCf^v+eF;l$HqL zGFX}Mhl8p@H2+ExbC$|v`~zvm#!8T`2+RgZRZ}Z3R^d_lO}xCHS?@TCw#@FnJggKc zUZ43kf`>stMhEq%O&^K4Y1Pv!)x|~Elu z%&c6JGU1kF(l!|h(`KxiD-|BM?G4-g)E0VU6!6jul$;q(X|Gqjpfj~e0#%KQISpk_ z7Rrz;1HstGQ)9Fwf7v4mir#*0vZZoJ5fB4)h5ygQV}bfSSEW_)A-&EwT~g;s8obg# zr686ZP^#)#XnC0UiXLy<9oydI6iXSm`cmR<&Bf@0x~j{=bj>odBX_toofia+$*`$y zq{_{w5Bh(t*zWfLZv77I&xM_X^6vcU`)8`txd(V1hDx8E{7a}Lqr!os3cK3q1}o$9 z%6^dj#HmfK+MYmQM+Be^OOI>SVi@$a<(KGJ)HzJ=w+OB<2cF$m*P;g)jtVZDRSJui zgpH%7ckn5&D5pr|_`~RbMgXq495<&&!pl~jDLri&Q5;_y!st3t`IPJ|+_8={2C7B? ztqK_5sfU|dEV}`3v~DhO5lwwqLwYIIGyvb>w&6(M;VpuT5ThWf9@BOtvh_^FmIB}r zP8cBv4IB#Wrr@F*QGci6OpQxb>zEQ!;0%;*eoUGLAVvN207r;vFg<=5<8mKDz2olRPD%c#0u0oH! zSU5ydE8=x22cHC&$16Gs`4+KNEXp*lJBbXoQCb_3pWi1+N+iDh1Oee5_EY>%3Z=hL zQfdtYvpXXuFWaB}wtD33rw3Vc5HwW zd;80lDDZFpr@>R}Ug}iSD|A{l)h(1yEDW?0?zI^S>cE*Uw=8<=wLRR+(gJVFFAVfA zU;jT1S8x9|N5I*Ov-2uvt>{vD9J;}4BZ^d7vC`A?v97K|PT*Zx2NH*kD>wOI9U0a=wQx+iY092-SX zm&dz_Kr@Rw^d~@fJNpaL``_6FURSTb2z3EpxPlB*{EHC${lM<)Kj_Zim;bcw@AN&H zj0vRPzN0a|Pl8n=2|o14o?C(-+sU6VbWZPlm7k7IXKlWpNB5_n|4mN#x8rt?2_axS z5$}EaC>JjP4J;D&t@mxaOZw)+R?+8ek3#kH<8teLF9(>^>EDK{H%K$8Cw!P2;u{H~ z2hej4b`ScQ@*f20?{{#G22&JGX7ev!xxvHX?2xRoYp*Hs>51(K3}rhq`+-s3h79Ok9H` z$sv@RzaIL7P@hEYbyxrbz=`qF)@Edp+dtjfCDk}ejE3`8UlvPPu}CVScP+q%P{fb% zA`_=XO#F|He+~c{;A>frKRY9)qG!+u6EU4VVc!_7oXqvH6f=Wr)qhI>`t%pfht&{` z+S-^P{=q>0j^=hF6R$j^L5g3-aK|&}3OD+LLWJOmh@cbq3{I?Mv6`QA_2W^?>a1_*fAjCpaPG1{*uire3EE?4Vn~ECjxNS8doX{+bYShRBc{6$m zsCH6i^bZINikE7>aKPktkQfPba;i~V6W=W>s-rE1uwZsbX2+1G!g?l876 zP6KA^q>X@!o=}827b^!35C$-oS*Usv@R0xDX{6uVMbt7y4m%V5InVv?a`4OO5lZfd z^!u9f-~&8Y#_?D@1UYFY<|*42vf!FF~l7s4+!y7Q%@4iPd|< zA+lscp@ifKmFK>QU4C3pYa>>hK+lg)+=%|Mv|u>4t%%?I62bqZg-B ziNiwB+X#HEi1`v`)MF|6<{%gApP^B$4ncCYJ>E(bXa1A_-VKNi98iGhaSIWfr+xbr zz9yiV9Q%f);Sn3TalA@o*FbMJzdz+-h|IYvq=ltM{o7xRZd2^$NQ>BUNr?*z2eb=X zRt!==U)V&EJ5Le6)B|oo1CdgpG;qm;stonD_zQR$Ygy~Ut3cGDRu9cv@p7)hAC7wh z0i32PU#Otxh*q>8x&*~ZcYq>uVb0kh=9AIZSTLcnpARw^Xsq6m)lbvie$>-RgnbAN zjD{@~mU zHIT?$HM+r!%iJ;s%W_+S&QT;KTq2w%0{n0c@3Uu^%w!COl!A!&Df02h#BUcQXPr$o!>ZemsaQMMGtuCs(ckj((km-$-~=Vab3nrZwS`}q5svn zi2XPiDT;&9)o#)uKCya(hxCag?2>>zH(yK5JWWt zsi2nIS8fDUliOz{E9b+i<&7XzHJ}uQ9b(D~)CWH61Dr@BWJ;ri#$j%nbR#54WRUyL z8FNKG#GU)+=SEPos}+MAl}ORg5G|4ZqkMqtw)x9N_*KCp46j#f>D{Ao-D`!6nKX2uT$HxwLX>-W1{MoEFwu7Ls8o;GtOZ zr=3$k%Vx#8qv>78<)ldz(C{r`aniJS3t+ia$^fW`lYmfq8Pgh??5K<-qe1~BOtxK? zY_Y7U;v!h_VWTrf<%rZXZv9Bjq2pEM3m{u+I8iU*Ux~h5XrHp5NPvq$-If{0j0=Ym z(HVvxF7<_05qUbky>vv|+c@oZw3rz=9H>fv=7%Sj|I$a_Y4>^%!fKZ7pTpj%v+-tMt?J{b!`jKQHr_9j4zJ95+aB-1YP-*Dl^Z=uD z8E;V;dfXLxeDu;vyex`zt~@Kgh9=B)SUBwNEoB{yTrwk%PNr>YlKfLr?^+Yd_k4xR z_3mc$LEKUr1|ieq|0&|`iQUNvM&jX6aN-|F6-Bw?#P(AC!#-RG1^YaKI-_MXUbndf zg*)RCAsWH>Qqavf^^5bt#7r)cliHphMjim;`V(am#*{HbrWK>aLDIz5?+3z!C#zD*KtWWFI?!|XOBlhazN9H~7<(8;uwi8gy`qkfQ}0-qG324_@vi^BW|6#X z7#Y+z#{tR+U_saX)xZi(#WdlY>TQd!VzpR3@gl^9BkB6gWcMe^VPN?hG&#zmwH+); zy@O=HQ$VkvY2lbE5v5ETx^h8@S*;Q4a(Fb>qDS;##b`l|D*4!1h{aXOPiuZNKqu2^ z-$LS_b0d$0pZ(W%6BPdw&PD6)@`tW!5d13_QNKbAB&3xr) zt^=`7`>ubW@D2iv(^>xX6~RQ~gEZ3w{zhwv_omq6)^raMqHpN`LDehA#ZdZ0mNMjs z39i|jr8wBw%E}OoU(1bbD%{hMG@nm3Yhbqjy$v^(1k^TPwF0`n3?uiCn@?SnR91?9 zxgH9PzN7G|9nkIwec(reM?ZGBMhu#r4Cw35&)8XVh^I1h(f$v1BR)Ty4=qHckiN`J zn_u!bh870{+&2sQZw$E>EVdCp)e5BwM4mkuhf96=7A`bAA?!W>QVr{AHQ+t(kwM77H$0ncxP}^#j z_osxBcC6bn+pJuEtpA_}39H3n%2gJ!&ZT8*beD2cF6$)mG_F)hY5Ojz)IO`Rchyb|D;D-?ogs<55ae6^xV9of5SEEFjRsIw^`nl~!N1ISC}7=ZiMc6i z(i*uoX8ror!cg*ihh^I>k>2^_!?LL%> znB*%wjdrT72=Z^kK5#mxk^VKuBmL~WI=qMLhJ(4UPMr#671;Uk29_b#s!AKV1^bD0 z7SHl0%fES;&0KR&n;_7u+l&D3sO?q?+6d^$TwN4C&N!G>TXZFiF4o&2DLS;z_%Kp`D<-TmuGtRq*^j+%(b`CBK=mr#Yb~ewcSQfTW(YW3kAixvPiu;I z>w`Jvk}qdH1tV?eDp+RAC@XqvuoERaFplKtW%6}1239yy>bTo4h5Rv;u`{zvd_SDf z2=*+_^9m_lQ@N~LT1RH$|C|cTNxC-U%56NEMFDX9vzJ5*=-oFifzQ%kizIP#lh;x+nKCJ-3>O&Rn(DBAaOarkB6fesES1tSOUNKLxo0rl>y z&Hl5hKgWm!nT=i|N~iLODX{)Q)eyc z*1LQw50y%k&hwoYB^^8t6Djqi=$=qZfkYVD(TLe)m>qHENCJMzvcW6bN26S$V(|uf z(|#Sz<=noStKt!&dDl!Y`_RnUwGu{ z|K+j7$0fUE~ z{WU;P0+F#UDK9v?uA>}<{(C5?zX0kwZJM*~PVF*K{?w^=_`qVaSwCaR@e(oXdAznF zCKcopXWvc?UIa3e$z+;#de(TWec#@?!-HgP#)&`U?pZ$Xuluk_=bp7oHo= zkpDQH*~vz(Tb5Y95xQAMm{*Z9W)Xe-n!M%#j9~hXe(12d-^+}vs!+wdE|{P8;m)Gm zqa1fLJDrJ>{$hwj&WQcUrnbCw80B!f+F?;+wKQ(cgcfgD{xdj7vO_9LB1l|k-qVuV z)`kN`Q(2iB?#W;?LCpu-q(DVE_qumYK}ZH8B%kJsykvP-1vcD~HT%=Xh2p1vrtL=g zJdJWqj}v{scN!WN4K&QKbK^M?eHjdqt(Bifn1>>SzSC}AMvqKQM&ee~{eEcwqEy`* z@Y!*UOecZk4c-|y6YaTu5^oS4UP`di7!ff{K@g5N(}A5Tb@i}6&DIgfOrh02uEP$> znUTkgZ5bq*rmY)Sb@Cakg{!VcVY8j{j0dd8!bBrJ+h)08L9*%yK~!7Q*v zIIK9takaVg_gJM+K$hu=Wlh&I` zQwXwZbc{1rvCxRkgc2;bdXzAqenDcPFc;a^Y*A$N z*(8##0bvUwPYb^gswpg=Q0fvXt?The-Gu_*J&7^}#KH=;2+B*aWIdxG79+r4(x@j0NSM& zSyV=iXyMUt+`w|UzrWb96xNIi$_crP{MpGCOeU!iW;e|DOA7o`Q^#XwjK^=j1VN+B zm74xZ_#zwlb7x*mtzW8V`Qxj>&$Gl}7H*SJ&8lel$Dc=Of=O2G3RD!k4q}xC<#yG~ z*%yDU&(RIDhojOi>I%XUk*!Ku1XxfhLd*a3kU0~|s82$AB~&TyVA<8=y!S`& z#J4^BZu5OEw%A-UX!2C?8K7(o-1L~<`gU=a43W9@psda4CGP_%g#W1{P{xC|^xJg! zJXUbed<;ybO2Cuvb>vj?yz*ruM8*ST-PXy3D(d=hot#-KDOq9u)Q*?9Pc3erPbs4r zNZLj}bbFVReyQxf0YHI^`45r7q9*?4zQ=w2+yI&b+9_vb?0z}I1-p7Ji+CI4Pwoe~ zNN?L(3z6`L_#I)sF3lw;$v^VWDdBvmZsfjnAS|^2%Exw8zIXg4$!ezoC?GWf)e)0W7$&T<0kY< zw?(`zQO4F(0kA(Gd46R~A)jxX)6VAS{2&P}f_z^jfTc^w7omT5k-inNu)tptK`Geq zCdU0r#Xi_uaR%VSC4OlL(eX!d*gFX_(?UmD9X(w*#;fD98bwXtF@ENPE+HPnPK7gA74v~ZylP;oDID>J>)%RBU9GV?at&Wozko~JHjk&3fE zx4!1mNXMi-lJZS0+B9iBea`5b)JQYZ>v_n3Ip>=CX<|TMSV6mL^f-^K#6A6Zb(1+M z-l-c!95-?c@1~ts#?TQ2f_<~5m5+s{_)5(R3aw>1k)d31u%=x&R-a6t0d!iMVD!gh zMV+bwa1Uq8e!ScVJGG$uGV{1H)$uPxrBxjSzf0LkbIypcUV3PiA41We(Xi^-o0?b0 zY;P|B`9T{8ql#6z-d$Wx7E5Q7&bHWd2qngNmHR9ZwO(r2)gFDYIjB`@B9djQJModk z`RX0Ol{&c3>v-SR^?qsn3Mb`!N4forpj^yWDSu(ty`-@>AE$JJx3f5O^7_h)t`9+b za9s60LBi-bjOAC8*^B7m(V|jBA9Hq&WBlN6frx(Y&qHX8ra*l`*l@Zl!6ErfoqY7^ zXFh@N8ftSe%5jD)m?NEB#(&&k!zq3X_C`+ugFV%ULK^&s*pw8-mEg0&XLt`QDI#r; z`8#+lj4=#il$K~XHH@JZ;#If?y@^s-@x+F2L9W3d6RDJYvOPFvq z{({DSzgD#?vxZw0vVaJfYbue5$5 zr!)qn5`hm)K90DeCnsm*_|#9$3HxI25E%@6rPjOa?>94+rpM&oOZ6j5yCb*fo$rZh znl9N!CE9Kr<0Ay3=dg3jwn=ryZjib5qlUs#G}WJPV2)J6^iM29jOZ>neb{QoqzMHI zuPy=B9oUClh}cd=9CO(S&40&83ku#K6S4U0?nqFtZzI=OI+nMoTH+O)s@Ym~!`D+n zSz^1qlbCzVhk#A1%Si9VR&8uAgT8Uj%uw+eujQg!-tLrLw zY?>0{HX0}Xc%5et|$1~zFJzciM|-aFT={GYe$Re z%iO=16&Du3-W!p}#An&V2u4=`(=zFAv7vp?b{MyF6B7~#RN1LYN{I*I8ODaOr8q-_ zqf;Ky+@PE3G&eT0U_3P3|IJCaPLl6VC@RqQerJSDCx|KE?a*- zcVZXolZgX&!1)U$Xn#d0um4h+MFN@I9w>6RJNs;$e}voL3+#rfZ#6|$Vk-$NgRwpj z#}^&n#s6VH9zXM@TUA>ol7kT{qYgT>GA?R%G3R0z<4j_LWvi57MG7Jm-k!Ek`?CaD z!+Pg9=kTGydCJ>3C+Hex&0=XVRU>;k21TXN8Ywi1>EIeNcFuV*&DD0FnwCF&VSC3= z8&C?*xYH_AzdwhWIG({W?^%X`n<)^OLv0wVo-L6VUX3ykO7W!Aqe+yaa;amWCD{~nsHi89PevOOj?QO1WTiGrz93Y07>Sq7&-QX>e7E6qF|D!wDYloQ!t^5iiSdh( z9HOjFk3fpM(TUjpU2T^DCWW?HX{#ID0zxd34Ej)GIm&?TNUe#fM#%*>hiRoG&0t(% z*IPwv4R1QxI8^>(wU|>}Pc)SxSeQgo7J&skS81qchx4QRro%iA_q7utox_r|^_jZy zwjF`JXJB$rKo2b1N>%&(`0UypJc8m&8Qa@_oq;D}2*fF)?ECq*wphSS0$##RdDB46 zm-)|GmJCdz)Ix*zVZZtCIa}}09}r(_0B~4C{r%)|Fy0>z=ywS@(ugb@CYfKz(I<6r zMfK}2HfZRXcFbfuJBk*64E5Pmc2Mj{+GH_K(sf{EGRg>JbiPOD5r-z`*4rwc!A<~O z!5qAx;zJ>@cFN_|gh(FXI|6r4r9Z~H0RSVVBo&IXZVfEt0aCBuLEsQG1;OF?!yrWZl_TLm6m(;FOiN6z;%s8&Xt^HFLY#kU}GRXrh zd$Q&2c0vRM_O>^VF4Vk7XanHTJBf3X^n8#WUUhY!HbOEv1)UNMeQ(uKuheD*?rgj2 zC>Yfm(l(qOg<^A-;pn#RIjHVZCeN9@pv(9&(4~4_#!UItd2%@Y#6~lktkNv~JaOL+ z_%sq2TxFYXL7md2i{3+Xey<)n5bs59kl>cmM@}jPJEWR(yb)wG-#GfTiQy3| zn7Z{r>_Po5dgYMsPV?a}TEDi041c(zPr7~Fh2wPsEsrsu=a?sw!**jsu$B$Bl2um} zTi@Lzy-AxXNk6;XTnV+U3EnVATER$NwIZo=23z^+*zh5)k)>TXW>hwqHym3UP55mD zjaMl_l~}UD4xMR7z``2=*Unm)gMvP`a=Hl95ORJLdsg5;z7naCPS~XM49WtX2Q!{4 zc&!nO0cdgRGpt7nGnjpX2mp*YqZU)0KeS#s1f>)56@xu=rhA)P$UK0$jqnCDAM>WQ zj?yluMP!}G_`8o3+EQUfs8$4rs#74iX>Qn@j(VU$q{38Kr}@4%Dn-Wm8l#+uix|dh z6|}7xVGIR}jckEFx%e8LGkr_@WP#5pw|X*^bLQl7DQuhdj?#bZhHmfijTYj@gMbbu zT~D5or@;s3(0t41_xdR@Bn`LfusfKD*0LY}CG*9Wc3R zb6)KWRkkukCTZF&`mui=d4Ea0!O%p!AV0Fx2=w}^tj=0M1qS57xGbXM&@I}$CBUMX zuW1l*i)HE$n;RMY4sDLrIar(QgJ)XGAfrAI|5;lZ+W=ZeuXwMQ|$pWbTx?Dfd(obuHKo zb72I#dI4pW_&6Ftwl6?`_+oo$F8#-I{5-uCB=&47VV!xF^poSx&y~;+a(jH`)5{P_ zhn6$$n=_UfVNV4V8iQYvkb-enBw@wm<;M9MBz0O5x3A4m(^!~4Qn3;&mPAPBhiTzr z{ezg4OmvK-sp_<8VfQB?DFr;J-)Fxfc6@za<_vW;lP>KOMbc%_!x4~2==m=izE1C2 zL>|DBB3h{1Aw4c{S_II$kFm104v@Fuh}XY{W05@X6)sU zuJhgu2~kX9TbRw`eWevM#iAlcIEw^&;eN#(CH2-jE!TB~&fz#3+JMD(PMJUL+6o^r zjZ*9H@2ikpST2-l<{}XEZp1jx4&jX%gP2C8Y)}Y3FRjzzZ8+^{|AQz`vrZ0p{jr;$od&eL<+V%8{<(?46-N$!4c4&MczbP$X2TLYejEzMD)tUu$*cYXy#1S*1s7?}?hxG<2+6FJC} zV0E%PiO7f87RfQ&9aHqZr*ocJS?3!>C%(^UwB#iLZFB!HFSvTJ<6}vNkkrV(Wo|Fb zElW6q2z!n7u}bH@o5TrKjE+7`H5zqZ9f|5v&fkvSlUY~2dMG>JFlcVa;v*Y7ne65( z*7Au8M4xFG3MHSw+3sv6cckAeR=F(E3#{G;0@zs{N7Reouuqzb-`15Fqr*AS8liuo zYMh;1sf<)S`yFQL2#i0ynqJa+UN?5){nWki&uqgXQJw(*^zgk`!I}EX0Ui-&bBtUM zb{IO1hLOFASb#(rXD7${1N$r_+bgz$!qFiv9ZeQ^;`QeI8xLJquLHyq)X#_SK=mas zid2;CwXP3FE||zi5~SFoI!ss40J%6^EVrJ7lRvF@KH(?IC|nukAn?i+tq&e`u<$7L z40ufKR1rZisiF8Ip9MJr*0=~{e@;L_WH@dQOwpeU}gL?HgSt@8S?2nmtWg^X5a=%@U3UkQ<`q_8- zWr|q6v=q)A$rInEujDz+^zt?R*!f=+3Epze4w0a&D-o_kIH9wP&PYXNl)QF>MuxhfiA5?iXg_vmY0?)UcR z`Tb!p8!=@9KZHs|5Aj|axBW}iTd)ArykG#;|K0spUt0P&;AI^B-a7!ml|L>bT_pD6 z1~^)cs0;*=YK%&WD1-=uJ7PJMUC@>PZ6WH1A^H$aDc&cDMrCPQ46wF@Z}PPkiS|H~ z6$Yh#qR#K^q9s02RL$yeLc2t>XHF(8%)@hnZadL)w($X2-d+Tg4rRP@$^pje%2Cvl z6>mACI0tb$U3xyZ6#g-7wlS6YQ{=H03`Y2tI^h-#gY=k~rg6 zhr6`C6rGnI{fcuDPX&w&h66^ExtSYI`pG^}dGvroC!>*|>>AS`%Q~x_(5)l0*kli4|8)Kw)*(hG0>6H<2*X2m?1%@XGb0!nrM>vev@p6ghb_$yf}r?dRP9Kn(#!(p;e;r&B|y56^NXKJa85f8jqsEV0K9z6|1W7 z57(`AAyH|AHT3l`@#{^e{qksNOY^pCM})*Ge#wThL3CTjJC@tuh@9P;A3TEu#`3qN zOLx0%eDc$Hl1odg3i!GD5js)+tq<~yJ*LtBzSBDVP*YmVbyd!~LyC%gqhr}68#go4 zy+h?FCr%NGp&tDbd}_>#&&jy!ml~KFyCz=|%L^CVQlB|L3 z;5CfX6Gxjzc?y+vg_vJDAtf$DmdxpaOzPY)R+M!DXA?hbi6an9DG#AKr?L+dVRvhfavh-1mY@pj=~QtH8A2Y7qHu^(?+E3u(2!eR}Wj;57m5gC`uShYbW$|INGXB5m$2tU1Feyv6FNV z2g2TLDQWTDpSacXk~P3*SR2A&xC@~W3j3EO`bowUC<~-@<~k|ZzMsnz@aDuvp2~f; z``$21;|6}^IA(%S-|bPBG}g4eA|izz3dN+N7lzDC{e@b~o9ltxhQNEe1kR4|zJDPo zfG;aDN>K%!(^v#8jrcrb*M_~1(7E(#lhdZ!8W}b+OLqsrYJ00_{2zprx-kdFM7UT=DXrU9n3*Al~AMa>Nf z&Tgh6yJ>+X!an4|cNaT~`v0zXz(1Q@dTTjQIW1%VJ8OlL#ozzfSbhB8wb7(Tp#@q? z%7|UZdiRx*IiT~~&FuW)0AeN|LJo_kYe^wLKD$d5ztA%WsflSrN(6-#wce>8-S|-I1>v0KYIc86taAk} zX-164p@AtNu4 z{L?5m*EiQ*J`3V=^G@Z(F4cixHwU0E8e*7__A5RlpF5&9s_9s3emJc;R9b&h3?y*V>eRU-#=KJCINE!?x>e&BYLd}f{ zCvQ^ku+GiG@m`q$$O}?6!*B)Yf8))$O4lLhvX6lX{zsec_40YY_4$_T%xYROGVU|I zuf0u*M1LaTW(DZv1|^PxKR5+tn;cWC@=E1a+ZRn8lL<3{yiSI8Df}v|POXtsaXc z5mz(#aTGxu@Z?<5@w`OB;3hqCyA*LGKRgL;9&kdbEY&PMn@3lJqbdb=cHHtERI(p$ zW~+WsDNPF_M#M?`nkkJ+85)cc3-Jv^d~B~W%Bc=aaBj|l$@|U+brRfFV3Ggg@ulxeSNa{wnluap#G_Cye`Tt z^-%SFGk|%Gub^1MtREY`SF3;S^oqy6Dm<|wq0@wD5q?HxHL1$?=v`AvZ7IO4YF=6h z91@qaRbkuA3Tzy6&&^0s0|l=}r=;7?{aTnmEuC1CT5C z;T+egCPlZewx_aL*cr0ztLTPBHX-?y;Rc-g>g6Nc+lh5Vnh4$6P!z75A( zzvP1<=;-rH(!y{oki%mXr z3$DCPWm&kud-{H9u2sMHqs-kf>PnutWWmdH#pSa|S)ge)e&DJ{K3j+lSe3v4iMc7W z75JdS-90`i+iGax?x9;PJoR!&sQnzF+n_axps-j%#ib!Ea@9jtVNNo0`^e;aJ|@l=lNj)tT|8>xL#l`rIlqx8Ngq zOH6P@T^Kk!NvdfqCfE4@mTBCi_b*`8?I`R9KSbzUZf0+a$!h_lrSx;1)N#d#E>r8^ z-vK?Se-A&ued8k}Y;l|%i*rmT{A%{(!+5VkrVyN*V2L&gnffC}hyXIA9t&};&L;IK zj^^r&?_6xhTvjkZOC;~dG}Y-uyf1X;L6v#{I);@sH0bz zq@9q9ODcc21-8rSzk5R;1WQcnq>k`fJ;RWak=2_;SKCLSi=MEmeo{!x$k^*{PiwTM zOr3KOrP_wszzK@$T4tHj=Jv2I$8UwpZ%PxRnqBOAsu}LT{rov-7cpJ4uK_Rbr+oZJKpt9(YLKuFm0xCQ&uaO zo>sTHq$P6x4cun}`#F}<#3}lAcV5@VzL{7){;7d4`s(dJdc+)3KNbE*3El;bNXL$r zF4%}kz%z$fp4}{+u8H;2fqdOEs-GU6C&&|E!%AKM3XKQB)({kYyM-Iv9?E-)N?(D;Tya^Y| z^utA(a$3d27T?*D5NWEyX`Z0!e39ES=iD_zA{_~d3UJzu>;$_30-P;7Blsp6tZ}A_ zY*xo=a~(B;lI-oNfT6vhHDa>DFOpxWkl#=&){~>w+VpL1W{&ZUBnjB0)H>8%BlHK3 zPGB@oEB3}-Xe49L;%% zY_8eg0Q=?7V3w(b*QKG5fHnw2OiB+y(%iP>3fFptTE?f-ja1wi2XU`U;=fnu#m+Bc zZQT1!i2ZO0Pt|!)FQ~ikvH%|bH{{jV%`-e2(_A(9F@0)$Dd-mX4x)zEst^UrF$MMe z*`X!B{qPCIU)!Z&rHxVoJ5_NBsYhRiPrAP-*lecWpL+O1dUS{L9v1*WdJfFxvTg^gTiwY;D-seIR1{_Xux5OF$Al(niR9 z=bzU6vHSDa!@iK({3_3R7A!|ra5Uz>1JKwkf^C;EnUryvroU%x#&JjOw!YpFe66`@ z@niH=mKX3rl8HuP8O5%pjTPv84JuwnVpRu0iOnw1LBv*YOYq~B$tFdI6nbMhac`Rc z7!v*Wur~TCA}Xzox@V%3aGve>Bj53DwbiWz_rpu+Cqwc=cD|6hK`Qe#-FfL zT{8+aPrIM^2u@p}vf4Oo;sL#-Fp&o)ij*9=Fk330&+B?ti>J5lZKboD+TD|sa!E|D z2fRuii6#t5IGYR_oEG#$pv#uTZYgV&8=i1xfviH63O6rPbimOFwT|GJA5^T$l4#Vh zkWNJ#L=1#{@{HaLja6++Wc&Wjf7o0Xsjjzyq!GHt6xpLg;_c1II6{rUEiYYR`H19k zL2W7^Zg~iQa$4Jy8~F~cKHm*Tk>6Z8N`LI5gK|No;ptL-1mXcPZn%p?mHXo%A#Ote zRW(Hk-+ZH4jQWPmgeFN6AT8t0qHZ`CH7J4)qj09tm*A=>SqWtxxd7GJcbz4;fEI@Z zQm0G(m`4O0mpt^0w&yw*Ca>ook#tu$VUTn)@+NwdvMKtndV$d(fky1c;-!_emA|zB zA>%5j8e-+@K!LT}gE{RdFK~=)Uo#@SBaIA8`#U6(%PJfUFt^q>CJQjp2U@&jAB~f9}UWAO8nzrT-a_0iy6v ze`!wtd_fcN|5&1cKmD(RgFt|7OJur3GF#rD5t3Q?W6U+AZUw(pw}?TlSa#{Rx!E3# z+5FD>Err|i4+{dW{EOSVPc+%{B?x8#|NeF#-&=VF3<3a$eKAF}H=u7XChIZyCYddW zI$*BbOw`+zd1&6ZivJD6lWHUj^K&RuORmFU%Y7c%{gT7ZS_9%g=L-02g8Z0kJ#u&vHN-fQ4sD#Sk+jfvRCX_kgwDgzc4sDcLT+@>i_`&Ke?R_Vw0m{ zV{(lPmXa+%y3;)(iZ>-oZ>{?AF`%AG^?0B?pf144G)D8JlaE5gF|jD&ILa6?PUI~w zv5Byt>zDnJL;iDCuXA2=GQ|J`B&i@zha#EN2^;_@!`@tE1G`Pn2r!{d38t}tnQS$3 zs?}Z(9A3!;z}n5(GC7(nqmgT&HD`~DE}NF8ElgSg?SR6r&m7m_g%~*7}yK9&Hgkv?MP@j7O-1;A}vpTG@)(p46Nwi z;72IkO)xR=s zwoYG48-5xRGAf~vX$T?3`9&MyQY9vjwz$9_Bj>wU)1t@dl)*vJ^#-6^M>_9Z>6E)C zA%xo!1fhOjc6 zD4$JBWQU176ipS>DC8!qmD!06s#Mjmjj;gQ1!GFXl^<6{ z;C=pA2vu(FR5eW&Pn;yApB|__R+ALGA8j;LE0Lmc(Ju-GiR{APPLMt^k6wh^t>0Q- zibW8fi~}NA?qfPdzi4^nlO|LVsB5AwlqeN17Zvq#RHuOos9F6t$;~@l z(ni#U16L&Q7H3Dq8i{_)((jE)VUA7&IiV+=nIsYp;R__tLo7S!3tsHR2+|e;WeT!N{B%^(3 zvO`(Gs9}qY?@hrZz9>umgjU%ST$DDsZ>LFF_T;^W=D21WOIqoIT#mAJ#}DA^Ws0b@ z`8ljJpomp)pD%B03j>_upJ1%(d)5{2F-U2ut}>)@xWjq)dJ`LK&D2$$+u6pZZSy_W zfdse~!77=pT3tNqi0*pj(3Ix_0%qy%J;01iqI*SY3bzPqP9~D%YXY_Pj;oX(gNz`i z&q|2gdz7BKkSdyhepWl_c03uEGF*p#Ld9^?Gg<{{CR>1q+7sgySoBD_nIx(4V6}cO zryp4MW8aoDO;6%V%5+3ShKRmW=^JbZ33nYN znp6hA2x&oT!#Lzh@ZR{~D8NZlV@U{gSpfZe=TNJgHHuz&QAG&IU1g*JOT1N=DxfB3 zBO|A6B^xkGs;H(I{7Or76PnIDaBjOF!E~g20(g>nPNd6d2AHk^hAKPFwM3~BI3Y%i z7&U3JV!R$r2C0li@E6DI|L*OZ=>LTJEz|*b(HYcRVrnFt>EJ{i!K>w}6%xe=`@o8( zi}x1W;ndie%y$RI>j(bmrx&qYgmWzWW?V>(a|Eev%SoVw zP?_h5m9na33?c!4g%^e@FuNYpyz%4$Q$MchR=(iaYwtooAS4Bqj$K)v6ljeP@P-AV z$!gYM^heP^Bk=jUz_n|~@6vKF-#{XcE-NzJRZ@pkA948ogkb9N8^XG<+)$-cxP3C4 zt=T=JjL(oIdJRiI9i?e$=vcGh8otQwAEE}(CdyudTQqonvh+x)rjI2M{rqA{gTt*k zxNaIovEz*6v<4aDW1lXQ0ZJ@~Kbka242QoluF=w2)+W%_)wzp$d+G!P13}<^6!**I zEeJKd#@@%Jk&kk3@Dh|fV)2Yn2fow+boCrY z``fz3geiNA1mCBsrV_#iZ2WV*;wTVB9cLuxK27>XdS&5_ek(~@)6a`V=`>Lw%?ig{ zv9t~++UAt0IUauEGCSlbKe;~bk&uV#S?!E38C-@hju6<74Wgj2rSOt$92;a2<(TdE z&_AjTx3kh>fk4UNS|s&k1o_l;2Go-X)P;jUARyZ%Fj%XYxK&bzl~R$P7DA;X$ard8 zM53V%g)jLNYaCwhTWXluk7)=QqUgXaaRR`zZQ_LhZ7hb2scG%%SC2fvst)}(x*7n> z*l1ODOWD1(N9g#;C$?xjsub@)+E)VD(N1Ic*$5A11np{(-f8!VnG-u)m&p5quBSD-4O?94zuWShk0=3)oMHoIv@!o z>k%L?P9gyjQjV?{gYN{kCqZy>mZJNx(Sp%Iy@H9Or--~n%ZfOmgb0oZ*axO?Jr%uY z4gOLz|BmsWgAh?#Gt@@Oacxj<#7Hkf*_!m~;E75*EH>^@5|>fd%FfPROGZNn&DF9j z96E1k?IrZGt{CKf@&ka7OEyak{OL)o$vrQsMdlZliS!~I&QgZ~3#%`D+h(2UWo)B7 z+T1P5jXLo-$*lbBtF+Tqgdr=UKampYQ2uc@us<08c96TU?8}qF*l>j`8k#>X{+4)c zh}i;-WQ6`jZynf)8T*8-g>owk!>uIm%h+HTc<4>EorpI!(ctBb^kiYd+D|paOYJLw zg6Mo!@>AD(7icZ|zx_T~qCaHejDZWT53(@B5nxoTkv9+fr@3-d^bqdY7`Imnp&zA@ zc=pg*dtVa@<>!cO2`UL<1U7%KZhrB`EJt-DAWQO$Fzy$6>x(Y{jus#get`NL4Fkv- zx-$FcK2)F@7(~$=dmDat&{s|Mkn6l6D|o#KQL0|6UjY?(PT;@jQdlb3I6yMj|37yEn<(D^DCu&ApLu{ZL#zzl!2_H| z+Y#KLh*{>>@O-f>8MddN5^M1;O8t0sF-_}KiIPF%>^dMZ@lbL?by?F{wNX-cmu$KD z^zV8Czcwn;hmx=5S0Wh@PIFH@dCTVfDq5$WCpZ-r{I3Jt}Jv&>L~66RNrA)@tQ&8U|I3BMkymE`8R@@B0glt zgb;Z$npX)8p7`zcgeamTKTE0VEVU$!0Mxej!yXd2$e?@g2o{9nU2o~F1t|&o6Bz$V zk-9Ws0c*|L^>D48G-?Q}=C&+li%gO0DtSI^ddg$rzLk(OhE#gbQ z4IK2(QoEV*!jO=j$1%YEjW`%+QJ~N36%AMR31)BXwf03}6eP;hI1_S?%i_^VFrts9e^>;JNP45HgGe5~?vRpx^4jp{W?7Lk>II7cvSsOGt*9ZK}mt zYAu2+6qrY-0iy;MJoGiw|Db8Y?7t$bj_H&?=ZaXVvP4zNHebscK`TL;9{_Ht2=1q;8KBd64$_wxy?_#@(PdP+r{u_$!b`srQy=o(8#r?%AP+?^=JPLN06b?{ z^aJ2Pr-b4Jxx%bo4;#N6^8(OM)c2vr31`2@w5dWj&d9}8u&t)>S@|vbrx~MQ{@#^G zzuMeoGo=S$90luaG2xpNzIp_jAmp&RvtvR#SokK9{JnYSe~Lj5L!S2x0DSc&0L-D| z?6Z0^i=t1oKMx~FCF=*Jyc)?eA`4o|9*CR_8C@gG%Lqypy^C+Sm7Rd5Cy0=lFX)E{ zI=03%-tFI8iZgydc@YAuU@4U8#v1N~~PKk*bTF$QYYZsQ-@my}uYW z2+ZFTw+{3Ml$x9F0BBx#8K4sqP-89eSFzMNz8?P|g* z#5{3I}q1s{z#e#hoHby$0&(Zh(75sob&-?VV>o@;r_bSys^q`f- z;s?AzT@HYQxSbw~H^HCMub0`ENOO5=SE;*u(bgTUbFnDeusB)?GLAh_>cE7Uo|O^a zpT1RR%gUr>!`I%5^{0DE@5&E*Nt=i90ZJL}Pw|bg$zX$6CFrd#d)GmO+)AB0=bGj5 z!u>))7ZK=8NBi8h+x^^P8;}~D_Qt)d*h*)xncP+HtTTxQtK$f$%Pfzhu#KJ9*LIK@ z)aOm-C27u-I0r+3>HX-!usin+Q@Z81pOIl2c5E1W0A>~NX51$)HOT6O(<69n)oh9! zNK&0~9~D3Zk71w@L}S%=2*%h;qavh=t3U0pWAF+63tafrG}}U0WlWQe#WsX0DKHJ9 z4dqJ0=scpbA@j1Js^F1?flEA}mU4hCJ=C=E{`FuC1!DX+r~xrF860nxLE zy`%V(R7xw0&h(zc17CK@|Cz+^!EAS5KV-vjHdgeyyuCZ0aa0qo%#J`jP;6&gK3|C@ zRDrjG6Frgr@%AEKa5MN);;DAYsyA!7_8{6KYb_QsY`kRt-3~gtr8m>!YotKNbhS?Q zjpfK2u|cWMF*q`m#|I&S>RZ2=k&8kh5O7M*2m)U{=D_Z%2EgG3lt>%DEwq+b*E z>FGUawR>cGh-oFy!{09k=b+E99Bv3rMk5fk@uMg*SJ=g zip<`OJB=FV8-8k)7lmNlMORTz=3 z2S60n#FVJoJ*w{gm*vTm{-_p|7PBN3M4_WvmTRpVxfw#9VcM(Sf8Z)|YF3m4rE<;8 zb5g)UKYAxlHbURcSRkQmQX#JdLDcY;VaZ{@(%}mgh7h8l*a@CuW=*^6Gq}6SwBePW zQ(TX_z2x4(wD{(Gym(i3a`Kr>$EIGRLdH*vzIQh+*LnVrE#<%I%jG8hb9mP!faqcUn4%=T zL#glrO=M}J>ho@qWBJM^{ky-kWwJtfLnHfmw0NEvM7=^S-G0d9NE~%b{A>UFer}qU z%ZpTp{1c6gJ^;hmPhRW#OUmskm$F0LA;kmn{0^2;Y5+?3xWYy8=jG4y(&;A)vHQ zH#Shzu!jBVIWsCeO|F+1j#9jamAEOTR^%5Q(@Rv@XOK5Ue&4S!(4IGhrPH|C^lwHu z-eg=0A@bd}RcN5MIXK@w>t~wGa<)FNY#zlb4d*^3ARF}gEE>ns_|R~TfN}zm>>uV{ z{~kE%`_!|YwU;xb`TfwnL|+fF?;2NsElutg-^6e}_ck=zTl_S7p8Pz8T-F>`heY!> zO?XDGhZ-1|zIPMrmAQ8R;mnmZI1Tv#ywsjVW zvH9+)vqx8S9WwbPxXkXk{fse&9WOtR*_nCyc@`6XRW+a1FlKHEt(A?+G!J(CntkWs zYP2Ga902ene!LFF z-EQ!&s9b6`56F^Se@V0~BRn3K!<1mlM}^kHI2)Wic^$MmA1G1b84DPfk^AkU-+Ioz z`+57;Ib=EQ&5d{XMnA0@MNYd4`3h32#tZx1sx?(|9Y;(9$a?3$9j-3l(2F#(@vs2k zFwJjo)kQ|Vq?yc>mgqNpdbtY`y^1zEzf1^~JQ8kXn--T?UA(?PJZZdK_r5A^oB_z6 zZiI^+xoYoy{D;Rzt&0tu4Xzq-lXQr)0%%W%ck|O(>^fNCYhwXf6%t%O%oJTfUG34?T&U(Q^S;=nn7Sk%b1)Uq80} z5@2$yS9SSe-Bm_g8D$9TG41pSY2)wv^6OhZ)k%=X_>&pn$^4bfI6y|9oNr|`VlH_d zJoY=UkJQ9n0~4T3^OZB-R8+ZOJ3U|wH7WkwgS1KZ{Sb}lYoC^n5?{Q zlLoQ{InhZzU6+o2ZWHP#qU5;N+(T7n{+@?1@v?h2%<%eJPVAu{4%*wPm6=z@M)W}wW-ugt24vp^emz? zlg6F3ff3-4nqckXWIk2b`ncWc;pWtx`rFmEq^dh|kQ?`h5?jz0?h3CK zJ4XDgy1u$XLETxARB!a&Qr|bF%D^we{j{m0>6it~xVz;)$PXmf9Qt_9?$GO~`|qn!4p%@OT>&%XOsI6KRY<7t1m)bDh5t|6!P6_7UTdNg*8DeYpXT$;bu=!akM8((Y*oEvY0 zNsdedUW6oC%$-URg18te3~#2Z*)E-Z2c(PCZ&M25(9Z|*+}jL`+5^vFfnvYT(O8`v ztn52x67meL<&DIVoC8f!0z(*Gak4YJ`gDS}(Eg(xMcg6Wjvv`OkBrr|nR)ThShfJwR2rn`FT;O5stp#)AkN3sa5#glwo za{cb!c#r&G!t+@K0w!PKc;>h;khSoC7(M`caAI`9HLy|>wsTRB3ppMZw(@WFL5{b< zt!fqPzqJ69+7~W%{qMeQQn`MGgQ}FwmXApIsdv7Y0;=^#{nMp|;a2BVEDYb6>=5jj zE5fkZ<8Do_LUA$Z9owtlZBoB%-A(<(aARCqTybg&3@^J1R)6Q%UT2X)|HJL~2P1ir zLMIHFLf$!HS15QT!)=AEP;b$9)HnU{NK0rK1f%hMaPV1Xe?%!WI;eb#^gu#LgEZKC z$kkz}E&>|TV%b`d?&`k8XIy2__W8)QC#koDrAEKeR zVx#E1tKkH@PPGk7GXtn30WKD)|GyhZ+xa~>DpQDQUi(<6<`&k$&qZSi_v-v|zG498 zEP&JlCxq+F@@<>Z1;d?pw%E7_^>Q)dK9fVe^Ec0|=r-9EjO37n=ic(=rs#)XRzw6% zQ2b)NhkFYnL-tO7BjwCKV!wWpR;|e`1(W5JV0v-N)ve)dt!2uyKnRxw4PT<9q@M&w0Av5US0Uv51rH7{PuS)8VE{=Pv_Qq_` z$01OjpR-}71ddD$=CE=UEX1@(92*X20E8N|6RkCiW{-T{Mn7aP62J^^^{AKK@)}=; z5>MCVCZ24Ydd&S)lHtTrkNx5l8pPKW3(CjbQ7uMCEx`k5&keYDB3Vll_H_HFV|#$R zNXH76KPbr`3n|$OJ>cR5uM z1D*f;>!WiV3Bn23!7tvns_pXS$Cd$k?IaW&rd6}uHRNef-veVH8Dndp>pQiViMkul z-`;HNqmxtVg(sIjHmxYAYd-CfF(d%6@FabAvR$ z`{vxgQy%%+%|N$%IPcOoat^D(-^@ItBF2aD?%%v&x)>Jr`%xNxe>Ct(@T|Hk6EoUX z;j+rW<)f5yhlITRE_E%gOb_WUBM~zx5u*A$r{8ygf3HZRfQ%-w2!mxZ z?o2-dna*8hsiGroEG5atx&HiIbKo|0I)~uy;r_PY!B1O#*7eQIWWxs?FrXPrDB%?7oBVQM~T{V<;sl53uI*m+wF6z zP`B!w64XTk=9jbjlFd!f$wK0^twxFPf}VcH9uDPhX}{(CA0b@_YlPn)AG-9 z?xM!0cYnir(F^g%;5d6g=UQYTCircAbF@&;Wg)QnVSSBfH=;kje+)}c=+=>1=46@s z&XRi9sqzlO$xB5o4fb}|>ob1AoOz}8R9zin?t-wxy*Ov`)3Ic6Y3UG3 zsK)ulnfoZg<7wJq9k=mhgK3F4^D4a&&npBccM%{YSxWRQ$O(Iu>CZF6Zq@Y22L!A~ z-(QpQIE-Qzy|roj?JDj_b-yhw|Y|%Vko3MLOJp;de_08r_oQeh$dlF zCuchM@R?N%%y&auplM4`^v{_Y$YV(*?{j<^Ov_z~Rgk0WUoICDpMIM-bKV_44}vmj znit`rkX{^QhSx}&-pKdd^o_V(Tb~&CJx@j%)K0cHOZ`S?E!=R;DL?RhH#1v_K@}fX z-Peq)e9QZJun~FuONAa&qf?j0-PuHe<+GO>tRF2PP0#@}Kidlj6~I3PQvrZ}=gl8> z`I%VYzwGj8N-^hO0EHI60f2I2bk|DZMLEU~x~J+n>;Pa`Zp#5|*wRSel|_Ua0LE+D z0Af;^Y)-1g;@2hwy|z#xDJTvB%8{1CeE?C{ez=H&y8oj_>FuY1sCyCp^!&C|p~c7@ z`kRxxUe9RnTkEs0+*L8;U}NQJTNkbx7RutU=huR^v_hi?CJXLMCax!oivz8@ zZ>$DliNni5g&%#*gTifbq00BSk|#)TLG8<%f!_PUfiInhRdmUE9Zx0Om#j7TfSxma z{Wfq4CCg^2#do37zC! z4A||tT~B->`xs$ctDo9?@X=;t?+|+I0e7Y1+ifbJA6R+$R@WkLHA9d#y-UT_xOCAi z1K5dx@vWHS+i$mvF5#Z~%&0{3r}M_e0MAv`qEVv(huu*AmO6g6i{-7*oy_0%IV3}B zNs~@LK>%LeLW>BD-FenpkzL*lCf-74?pZkDnpik*&y+PG%pMDbN=4TUugga3_}L{a z*V0_Id%Ll;R-QYHH^man8aJuFXVUwH=|M$P0PDi0yFY~^h!WH)ExS!;ClrcJM~mAJ zdIsO8Y)Y9QhC$Kb!-#_N%KBzy@z`aHSimfj`BhvYgNjKG2X(tN|mNix4q+L0_eo>#(W$d zCOc}WX=#u!tLiPrBpZ*j(TfrtDKp_1el&9m^4Xr%Cf{ZRc2&okecb7}q##!rsn`XB z!~Q`{Z#viwtCvv5z1vze=_FQs0?5D$P`M;TpQB0;{@8_goZ5P<)Ue$~j=sxmHk3DY_Lk zfFLgj(iui2Jo>J^{w4btwdXLn3MNZPhv8qjDCj~HzHL6aiOE#z0ox++xrz^k{QOy! zM&7`uKx7<%B+L*e{PYEq16w$mZReI`;#@|BNKP%vF7FjjQfwMt%_3uF**GX-aVsJ* zJ1I;TrCq#hrwK~0CG8rShH2iWIT=(jyhe^wc41f7}+m1_o@2=_Cw6V6HWzw;Nnj0Pmpeuf!ofcQbVPt-GvLqQv}PLif*3DKs>`aY}tD9xztgGvc^WK6%Y(V2> zRc0DqXIxL|x70+=u$+5)e_^E-P8$cOcEIKkno0$xD9V1b!`U&C6|RgKt(rt37(8ZU ze^*43h&;zI(tnUT@?nxFMJPTTQ(JUoaqx>}|AUYi+yVCwzLkCX^F zyNaNeTr<;BL#0Acz@SUj7de|CU(Cs(p+>bjQdPTZ$=e{>JzL1Gg2x(Q(3qqT=*CPQ z(Ze4%xxev~NhHM#Od)(YKh&=;`MIPh<`KF}#k14y7O8Q9=Z``(Z7z{`|hY8C}pLIepM)s5L*Ke@}HyAs(`maNSjD0>lw2G==` z07x#n64Y}o)P>V>EO zat>*{KuXkrOsV7nvR?cIPyjat%IIHyc}KLub!ye;P-0KQHXjjWfd5_gw-#WOi%*9$ zip&3XI10ZKY7`}h42J{um!q48zv!qsLbO>h9#`le?}(Of;Cz8S+Xk%aS%`AM?&dNl zKp~LPJM1XQzEp;Yu2Z~oT`4lrRm*3qQAkAK5gUCjJbx@&$u0bYgjHA1B}IVBWWJO< z5Qa8mv6R^W36keBjHL_0d!JXf{e+Oz@+!#fcbD{ZKPulhO7?}BNEA#>MFppA{o(l8 zdCV)Hg>8d5sp+NpKh$p?sQC zqmZO!9wMBqfr@|%P7u}M!F}H+5kvmPjPW9PQPst%TX)UA1${S^mUZtK=W%$2+tIn9 z*6Ew?_lrr0cpk{mhyxbUYwiCC4c_k)(~dDHWfDuNZ;xJ z-Vl9O2mmq4^j8vI9?6#m5(Yw+C1WO7ebn8Yl%JrrbPWYnB4Uo4rh2M8=Sj%3Gedg{ z_%YBJg#`R+l(Eptfp9EbI#(%a1NZWd6?1YglN0LAmwZpee*108Y=Oh5Hwz>rRPGTZ zZvqOd#FE>DW2)jk{k2Ce;*Y_aEnaTxYrxz-0D--BCQL38KQQnF;&#`u_`eMS>&zQK z2J6Ls&0Jg;a;yqK5!-jD`Z zi_?a_8OCapbe7Mb!wl~UspRt|&!EX!BpT+U_LtRSqgRYj#+D-x*#-X?M5ibX@SDF! z0@Nwvjud#Cm03*IwHD^p8cNd-shal5uw~#xgm0$>_-skbpmd%BOnGF6K||b|RIh#O zORgyCgN9cL{7hmnX*9m7kOj#+Kfh5Y?kKK&jxiGA3K@ya)Ri*8>EvO4rqFZjKylsG z)m_;a(08hY#b(!O@=^fIY_|hU`=H}kMU(ma+5f$8ARK`Y4eN(0p%&OGQ5zfN_mN71 zR*Nb_XPcDj8Jwe!zF#rCz=JkC$Zt^tm5h#~(w(H^>~w6SW81cE+qP|XY}>YN+xqW5=e+0q#`rG3 zx?4AEtX->it*7Rk&zug{KMKf3FhZ<>}Jj2dG7F;vEY z$<9Cl!)(|ug|#T{ZgYNvV2(PfZ!tG``%(*60Hby zH?1wPH2BpX2*u!x#A^=_10B(Q8Lh9Hcel43BVg7QgPIPc*j29@j)Wa78!0NX++j>T zrY~W;Z6bThKR%UvWuMla`?l&FOWwx$&<;y&;T{(6#V_|VGxivnesd=YOuqFs($_n3 z&Kqor75Y~w-QV}##R zO}}h}IC7Wv(C)5k5Wq(9&wpU8|8KAa%NK7qA6P^BFW%5L&$xs~3-s?omh>S1(v{Hg z{C`a2Ape#=O`p#nkCxAuEvwf5U11mW+UTIb=)*}M0Qo<_=I^w7Kmey5m>+RC7}tKC zrMD>bdE5Dcx^CX_*?y@f5ZL@*Ws~>e-_v8)S5R~b&v0($fAn`-RA2sGZ~gP&7k_28 z|7Wa$;<-crbG;SV^fGDn@%-7*ine?TeAWW@Pq)p_ZLhSQ+uoq*pI`pRhkA$nMef4i z*@22p95AzzvBO8nW4ixo&_HNx{@#c0udl>fw-oLFX~hO0@Kb_zwNLzf`{wuGTtSd6 z1ncdmhb6+IslH}urRw$zlvElowwu&42z5IBi?*B>T#6y<_D>S~&a3-ZR1a3EbMHk% zj(^(kfWKhNkjFpv#;U1(Ywo}ZSI7yBqr{SmEMMVU>f&OsX=`o`W(Qd7pL&{;Ovf4t8e^a&+@TIXoc|VbADuekm^m@JTT#EDrEggE8W+gRLO*h;~w809PmRc_Mz-O3~^&se-wH5ZZBJY4s5ShVk9%!m$dl^q`#uZ zf6kA0Xr}6wA95$~FGq`ry5ekPOt>o`9yqf7ZUvagOkKBGHL_6N;feN3s0mfiS2FnC zcON|P_xm61pS6nW1Gs%UU}p;Pa5nse?hRnY>^_$|C&BDne++Y*r-H)^>A$gLp~cT2^*?%=q|r|);i#6Z9Hl!_U8vNn+RvX=N8l^P<4n(&L5hS z)YOyZd349KR`0n26Sq)NQf#+~!@!(RQDzvkRSvk3&>K7Vn7lUn4bWJFE2=w@qbM`h z0;YZ;aDI=LLjUwJHJJM)q4Q&8m^&b|r1y&lP@LdxV7Su*zOR9C4N>pOa6g?VOV(BL zlbt*&WKwpQ&-89t^rTg#%ULbDYYiDY5p!HqaxQBl;Gyt4g9p#uejHPQpZe+?E_!3~ zZQ#`gjO<}SQ+cs1tO)@LSqzhwbI4i}rb5r#D7)0oY@df;FnrK@W^di+ro1|=O?_eW z9!t3+&&xhwCLn0{p@?&1U@3U0L68G1s?3*MA_*y1;$3cWxW5}VQX`iyC~e)L#>#a~ zT=f>+5DAt1>83@Xa?b;?76(4qSx8 za#h>bqT}0Q+mUapn}~yd2^o$>PL%gTl(LuN=EIWYYMF1}uAW;Mk!dn%$D(if^$`HR49Z4~SV_Upx(*S{57f__L7<7YaCwdH?v06Bt)=)Hg; zg~%CnvKS!r)!*D7J9riLZrD$LWt50luFT{3AD;NC*drF*RMo0~?)je8{q;M4(Y#>bc-(dMhol#>R9lL$Nx){cL#F#}pCv67l1*Q^Zx;NOA1 zdE2h3jKdFTOKevT<|yr|RWGP6c{E0Sw4KnXCGRl!d0R>jcf$g2y0ZHVLV*60(|BDA zpk_b-j}#8KBd&%TGYOO^`^{ZP!-e=gY|vh)zNsrd$Q2=W(8QC)KHcNpzO_KzWa>lS zTtX*Wl_Gz9r)nY9&GIJ+{+z3Y&!dL>I2(*ZOlpat^qPBU9_Z9u+Eh)~winYf!FT4k z4r8`_9GCb!LPm>P?<>vnS&WfD|I6^~rmtLU{;jhL^bbV9;(vDNg6^Ms@m*UnPU;wz zD>&FKL%V1h$=!)rG5+T54<^*F2{kGwpQKvqj2W{X?z_i|zc}eoM!SjP^dyUc&ta?Y zL>1^{s9i*_BFwIw@n5~7D1Y$!L2w=r2)nP$zw|^95$bxbY)#hq-eDR!%4)YM!r#K} z+HQL|Y;XDcIEdRQSNH`t#pTcGTp?>yNs+h*tUQ*NkK@N`*3@n%FUHD*rgB>}j?hy_ z6usWi0v;z`6L*2O@aE_76X1a)DtCMwt3>gB!%V2R$zrui{nu}w2VgA;H0Q!eAw`gl zb6<-R`n{(A^3tz0?Pp;m^U@g|+0>d;8=1Z~BY+F~*^}RXF9*a-U@elCuB+PxVi`FI znlKVnrLU;h5EJgo$XU5AxWFB&tPol*{{;e~YPk8g2r`gxIx*$SQ}k_J7GTA+g^h)= z0A0WZ1@SlAcSV#6cCyQ!u};~hQ-&yf>7lm#REoQ{!)!S4DTJs(2(+SBdy@XpJ&=1; z@B-r-s%~Zsj&kT_IebKJa7zW+*W3G$#SU(-4RsQL(4eo2x$pp65%b{qC~*QJ93FRQ za}BrsE$~bN43exmHkN-EA*PP?ivF{>L{7uDD@0$xG_@EKV`%L zmzAdpW7o}P=W&K>=d5msE}p774Q!hkge-g^Z)=ma=C!k{jDz3)E0L|JV5BT zddB0uoyurkTeEu^3V5Y^2+A4V!`LMQ5PLwh7 zWOM>$v)TTVI_Jpnt;(y;yh^XY-Ci4SkXO)5s-9(w4h}DL$|1-OilvBsaz* zV^xp+IB|TodW)FMf`$)&?I6&=@TIdVcfJz6zk2+Dtj)SjU%3@nlPz@h4&6PDM<{Ld~mcd|3n*ftvH6MHu*kcdh&(1An4BIH+I-}+C zkqqoN89z(2`~3PBi77gy_WDnQgX>N8An;h5^m%54quytR^yg#edj~%-)BQ&z(~lz+ ziq>SgAB)hab9_usy2V8%;F*O33@7=qTE;LDBiv<@3Fm<1fHbulxhZJK1M3G-OAe+EN{ZN;^v@vOoK+_bH zwNv-F9!@A;qb`m2$}HlJ}k+ zM%KORn)M${|3WFYB{aBK{f5GVa(8(n(aWV7cL>8GnRVB5vlLu8;+(7QxV2U|1^}}u z782#N&o)-|9BvZ!vp9zO1D8IwyYV?TBD~dnP3s*I{*5$#vzrn6VZwv@JnhOR5#3VK z#RDUUJ4QzPVA=op5mQc6Ic9kj@zeHDoyXhYY2?_Ao$Y>~{N?P^9F@7XzLg2JD?OVB z_X7IO>m{-@!C*{iVAv>!2I!-AqT2>7^EQoIFQ^|-3-nd$gyHYx2E#4(d{6?L&3C5n zXq8B4FvCK*i|79agADog3&@qXP0m~UVzO0N@=+!%;jgEd9}TxrV{jeeXz1Ja^2s>P zh5parT0NHIa6dN1@2?YdBsxikRixKLa_+obki`TnE1dNWky^Yhf2SWIvCw^c3l3D! ztP1t{IA7|eSDGQ{X>02~v(hE`dLuDAlEFlHtb16~A8`;LgGn_!7*T+J*D0yd^?1S- zf-~>xD#rXX<$3nFc+mluh;@YI^;~k^T2WeWCO1N1M-Q z-zww=)%6y^ zLp*oCHMzm86s^bdr`Y$6oK1hxKUiItdrSKkaupCQ&kJK~%%TW|kV--HK`u~kK77#7 zy!InwnFuSaB+^Dd8jii0=rO`#g2}vU;QRRg8H23UYzD9tomRfXGZCa7F-)~@TUAW3 zRBK~x98xbw>XT+cugZ#wad8H^R)-8D%>HFx@VC#cv7X}DG~BESOTOwBCpKeBB}dS0 z;LI%l`c9q;OOpzY1?YvCkgoOyj7}BD*abfm0}{OKRPR|8-$rcsf#~IxUPSgJFWx5q zHMEd9;v%gAlG*oaBojjrDOZD6g;35L^FK7H>@xhyWzWUpDdf)CtT1MRDsZMV&^<|T zYYl=}i=*iTwMXW$mx7YGY^)^`69pLx34eYVwV)%~jv#3%TOmI4W5ZD*7kKqhLhlB} zd{*givBmXD+sekwwgPRMA^!0FIte65#;!4zy#%>nkwNWwlS#(YiI#&; ziqO>8+)7nF5DsMYgAdUA?KE~-koV(&cfqL6SI|mFhgmvXp6#7Neqgp}%uD#3%az~_ zqdzF#hK`^TpY4Mz+sEpR8cF|^TVM}nRp6zy#Z6VV={V)9XG z9Ho7Pr37b<)c(*(U9O5GOG?l}S|Xr&@bp__do`pAgjkYUg{Od`EiY>%nI>v-U10uM4*n6$Pp;8!SC&h7-T7=8;F8H9}cy*PE zbHZtg5SyK@V!klNpxl@Ud3i~ZhmiBs^E%Z!kn%mX7|UpTAnD!zp21bq>AYJY7ynRuvZKs3F{N*+s$Ap0?~dj z#LwMYC$XftvCHcVC#9_RhGql5#eK2Vxq{rmEhgZ*XB|)~yO}~Z0gsq$8^T6I`JSBj z9;S3Pc*2QvgT4le|+0ouBi`t z{rGPbB_sAA_`0EV1b-ar;UQ#ULJi^1`UlV0(2aYdInmh%{}`K z5Y*dC6l_h~P&f3CmL_a@zJE2R+PJ2lUF@u^t-+=Y))(B38O)QJO_cn`nKZTsNu{y% z2c2jX&Mx52d+fPich!3nhAJM{2u=v&ux zBH*_3t>iR6&CO%BE~_1*Ry0X1y}boxch_7wS)F%G!igc19}x%Gob>NbzczHg#_6YS zdQF=O^*0N$%waIYN}dQ`-B=!MXDXj~@Szpm+q9`l>tm7M=cUF0<2_xT+hdfVyA(y? zYRLlnU_62gBmDa^mYDnJtab(^ss@b&m5OrSOZPTNMn2X1Rn``GxzZBd z-+^(kdrChstL`8MHjNRKnsuR;rQ-Z`-nOg6rl~Zyli$Y|AcLQ4|D4eOnzEEo4>7`_o-0>(40KXxGgV}-Ed|& zBfKul=likU;b*=o4dBiG=-=BoqdlsoYI@d5<^uO1>7@;!4dGuTZZlqYHG?Wp+n%Fg0i^5SscxT*`1A7-mTNFdKVtyBx{oGoFx z+xmtfM-wrUyjoyFhWz147rZSql9OOhd84MsfUFfrha1{W(+u5(OqW3a5C4m;C|a{0 zuY(gIWIH@|C-gz-XhggWCuz|T=4pG~bofS4T#eG-8O$M2N+=n+Q}njs_E1J0!@-WW zs^>J52u6MNqzT)=HcYDbTYaXoV(y9MM`B9Y4*KPvd`f%Q*;gze2G_;ug0%o^H{$FV zLsKMfl*iN5!AK~A6aXFuXn^|0v*kzQUIOG-!ngxH#%#E9ATXXqdUZ+_V+-QX2&C7h zIw-LU$T)rpb9}4$ZWe6q@{Pl;%>EaPdHu+)(hL6Q8eOeuyRW^<()ZV2XiD~mr;!84 zcJJ>p87{~oQJ8WXjm4X6Idf@|&(!=ja^bNC69&5!V`DNeKe<+2IS%<@3KfXF;{m#un@_1$HLHl^QWzE8BfI~$eZwmDth^uIh>@T zKm;h@<-N3iuwMRN7s?Bp^$|$+6La9ciY`xIKux?jWsd4{eg z8_&~}V~L+3Aa0L^2toKZ&D@lIr9SGET1N9KIY|g#GA1X?o!*R0ZHcH)o~KKy+e?OH zgdPrDCE1v<)v|)08ImhzMA`z{GkbXd+EqtWCn)k1B40351Y-_XgO1hX&T~ji?z%-s%Dzpw)DsO_7@0W zBm7*RL}=5{rA@oa$~1Tuu#X)BL)Z(PlV=L(GfodC<`2eq^ae#K?-ihg?oBdg0SWpA z2*5XA$DDI^PnO828lsnj`xsCT+Tm1H3J^@9h~)|@%5(oC)R38vIbBsEdzHmmr?BIi z%x)`(!7-Wi0?5)36smHji4Z)#yuj?d_jA|jAgLUAUJENmQ?FN6(Y`1SiD5o3GoT`n zK@7HHdMsxVX+Kj=YW+e1WH$36UR!0x2)rH~i+@!*rr+|t!qD^E(29EtE>khpMHrqt zgm`IdD~;M6up+DJE5>Fxt_FseSmk@?;#Ej9fi z`D&INjmkXiCDIf}>1gFk11f#tY5$oKWuvDHA*Qbn3ZU>JA`2*qEYT*yh9rQm`yLoP zlXK3jdcHdyJlI!0>C2CG%&w7ypT&GUnE_4d-=O)oOWkgLL~x=7TM(u zc1_ifnO`WGh#xcR!U4FJWE3J^BNS2ag{@!9w0SM^IQKQT7T$Gd(Z|*8=wH6*ThwLz zg9QM$Gv|@4NO>ICkAT{O%Fj8*K7XEd9x80B6U5nYBOA-D4@jUi!G30N$&?V0@ zWCmc zgRy-I%_KQvNOZ&Wy~I?isn`v71Mr|5NR-jf2FtG3cN0oK3G39nyZE}R5t!2dyF_xc ze2dR5Ir5dnz(juSL=1e{gcOFD^+%BoY9&ntQeh|1Z^1A~o(@<^iztyR)MXk|xqwCB z?IwE1kl}%bnBqsxOkO(HhS`1}3im^`BS7AK|75RH+rz z8#NR~C4*J+$c4>?t=JN0CZa_O?M?_1h?#d!j^{J@VUlD14MhYORXISd0z&|Cdzdn` zhH#H_TMJI@k2oR5`=uY8&4 zLWb2gYW9ie%`IDevc2mmW#<(pilrh_Ux?rIq2rt5eSwWTIzKMw;7bXN9AMTCf@)X` ze3$3Oj_mEI{}^@`j>!uf-DOZm<1f_!2({(}V6N{nSWQ%BuDg@WM~elHJ*K`0!(LX= zfUQ~lUn;KU@p}czA4FX>!UY{JJay5K3jd#a>wcZ~`|CG7`G~#_k>Fb4cE@_rd$JT1 zzRO|SKaE3lSM?2=Aq-*Gc`w4iU_ol%OXC$-);OzVdFmimnXkn4g?^0Wn2oHg42&6_ zLHdh_L-v7w$^AxI)pS2`X`Uu7r&arE_g0@)`nV(#pwg$5RUL-|1FS^j% zpp~xZl;HC@ zhS2(i)(!p?Y-#$M-jbjMf`!8cG@}HX0uD6?wHIBtB1*M~B_g+2qlMdmSWnW=9!|W~ z!UI#bC#|hQ>AL(PgfqEgpXn~hhVE8jP%l?O%&dNRp;&T%Ni*-G@e!W{;%l(~Vi_sH zfkdG7|FPcr%PCrmHOu9~{r@oE>9W|1#kV_rXc0RC*q9DZEIPVJ069pEzc}cBMi=Q{ zYj9qR{p_HA>N$lrbTyqOZLf4;l9a*YZ6SsNEM}4o;Zj|K)3#3%$HQw0tk2K0cX(xm z!;H&_O~Y6wk5L-vadSVP%yyoD32SqcZQ1-Ag0y3E-JL1tADlw`PSn{svdh@E!j{5~QvHR4gf);vN!4r~DgVi2G^l^&lMP`X4GG7haFWMJ2 zu)sjyJx>xU+8)!QHLQ95O!3 z!4)NQGlyo6Bx+ZZISyB+Zr-rZT34mxpYuyNp%a1r9hRkw58|By`d_v;Sw8pt`Op+= zKsP|TXHDNc(tOAAnyaEQw}QmtJ%=zS)}9w-&nnP$=!H+>%wDQjS>3~Dod7JUHY`=K z!VIRqxCX&RiZrs6mH3jquiM|0c~Je{#XapV{_cf|; zI8|aFcz#P>l9d}&mU;N}E@g7~&7&e+g019{74)f8PGAv4kVW46s4tubh1=wV57!?lW1!@| zXd8Z&f$(hs=#mH>!TYJu-YhG{&ebX58w7Q2OwS00OQW<1a3%>C9<`WagUlrBa$* z-8>j6#Y2Bm+~eT#Qn`uK5gu@54AV`<_ zyYCui3kC!lqP3b&zqxrW^1GFEoxkFUpPEV~xl!6oZ@GehE!So*aOVNaLv-ge`Z%SkB9Ehq44pw2mMRH}xkSzb zjh6{p$=2V+1^vByb|{2r8)v_1;^hK%)B z{4OWxEjCu<^VAOW*a0KxRCZHlc7u9ocY5s?vT4FMcw05^WGVcJAtS}mvin4oBWE^x zg*h$|V!ISFyFbrSx#?13dW)Myb*Ym7Nj%LYt44;E0z3Lu!@$Ps0O7FQMBVackhXYv z=g{cK+GJf4;A8FKK+P-@#yOvuEpl#NK8WRnPXe)Op0!|(Uaz7Be}{a;*|K#QHO$|9 zc72mC7*{G~>6@CwFsoAT5TV_?g2ayUJe1-WZHT{bdwB3kg;m9Bl_n0blWjU%Lvk#f zG2e%CKewy!+|^n^*+RGc^{)F0j82s>^Bx`nHkO$Eb;SvS1zpd013~r}I}m_cqUy8QP;J?!?P$wKM;L2s-72O!b0wWMy}Kf}Q~J;Tf!R zEV#+^V4qAw2HG}us&R5H6TbG0!vk!y(di^@n##59)fGELz~N$zq0>(ewkfHFKJ59Q zx$T<UD7@8&L($*K-4LENh2kEdkocl zj~}S98Or0rST}-r=~qIcO%M(=tGPMA&ubufV zIB&knEgqwsnBGw0T$xk6&80!#W;6K#GE1PddXsur1LcsYxgoYs8Psk%dYq3}f!E6R zFnd>-9HU;ZZ=~QBT4All;KdF?1DNaBI4}=uP#Z&Ji9H@JUR9NNIuV0TWXs~C5Mg~D zd@zu&IhR*UFWf7?IT1VQz9vUZ$Xrj=7|zNm{vK~MNGAp|W)1wn(mX#6ff{DgX{0W0 z`DEs6tm=txzzJE+CAeG}I~%eyGKI)dn;s#iVe3Dei~}A*fNg;;`7Vei*yhJd<{WM= zXv3<$r z$I4`J(^Qn2+-b55b)89OV?;*h7=|5u^e>?Ge}dyM6AqLSRL53YnN%iP3O){5 z@4X41H?nQwin!QhZJdg(n3PZAPAD=&qFbTyaS5#2m}o{YjLJ^;VcQ*9jjYkoIeE2S zIb|oFlyH@=eqoNKPbzM~YX3EjHA+9+^-)i%YDep;SCNa{BsB_Ai_W(g-ZhmOOF4Kz zh8j{@&{r`1!SMS_{D-az=vwb@x%Z^Ac@f(<*!Gc!JA*THoQOgUx8@yHXMDRT`Q3gx zSDVLZjh&Mzd^Uj_$9cedLQ&L@1?yTjd>t>hw8=|9x^R0Yx<-U+h48S}TB(>ptzgEGLLX&bUe2ACna-Gi5 zw&MQBMtLu05ij)P%^J#c^FM8OuKUbFYl?S>mI^AKV|ZM0OIW zV;zOb&EzNxxi?y^HD-B9n@V2)&=af>M4Hc_IsX#-kj^Jn;Tn*YM-qd37&R`NWrhZ! zIR^?pwMA*H6Q{ZCQ`KVMh3ZlK2D)GeQWFc+DWz`-WOR01TOVThj%QRYZG_7$;c*~9`E(9sOD7-ZvA)LoJWHE<+R8j0hrv->&B4R8oM60$ zUAf|RAS3<~yu-SwhG&zqa_Q>wxAr#E)uRAUH!3r@`SlvcZ7~cuP6PS$zzO3A`*k6D zRuCh0ir?%$7^;>NpIo#O zU9N4*flM$(3?gi{WaLYn-*PjUH?-|N|FE?BvFH~N=f8X%w>HGb)<`a?t-418d=%{ z8iMrCzJ^!@Zjfb~PLCQ0stm-7KF9aU31zKNz^-J5Itc>PvOQ zU#(MSw8;(qS7orRx(qW_fu|7|!ecn|Jbnu^i(E+_;*v3J$>Y=)`v*D}6h!TuVQJLp zBx?p0NMCW`l|q&3V9gp9qk6$akS^q3PW=B2K%^@dNLO<0GpW?n`ZNzrmFK??j$LMK zr~cVPx7n;_k8I{T7U1NPTKkESE>xB-8I%7NmUvJEPb^0T3(*JsH&*M)v>^>1s+iX)0WdOdr$m`68 zruxR<`ZVFNMgX-59Fqj50PZ&5$V?9f-@!Ilv9&ZCBaXOV23ExU-Vm* zN(nA5Ux!y^dDAssA@g7e@5;9Uzovih&psCSVo9AN^4{naH5M^{i|g5ixZFM#`*>eq z7|c$`LYMMYCYH*now`HP=AvT6tQx|7c^LVj1a zA7Bd$e}vWP#Tb2@lPfHXSujxQ(6ymT8BHnJShNWf#<0J58W`XE>7eaY6?6LjU_VYQ zM_k1tGLFs25+(`fDgG5#c|m?WLL2>A0Bcq_H$JL`wQ^~XXHZil(7rW&GR?)ME(CQM z&FH1aQG}=fWeKkuMk|)s0>B0P;hDj5BL#yBc>BDenM5G7I$yMRhtty+k!+2$k0*t=(_J zC$eXcYy+o0;8=b9I1fV?dQm5Y?Nep*c#Ej$ZuEy~VtpkFF!RmiOaU#*MX$PfkW+4H zlcMrfMwoKu@_~}EKQzH&4OMuH(4^o`DO2iNV+eWzG<|qBGkyr^myjqLGM;S2qVc!G2Wd-(~g8 zv!l}DVFF2^zZ5u4+xtXu*Llolr}>+NY9zKFU#S9eML-!T%t^Z4{A)enS&`48^W9z! zCCR5_WZwNYs3dDBPnGkN8-JJx-UlO>xlV+b=%z zfE^u^{A6*#10z>7>egbUEZ27;5?!UleSlF4zi#Y(dmIcL?m}b;g$|22>Zxp=N4Cr- zyLFQAk4i>zg6ExExjc#SxLxacm_ekA0_FWH1TEQ#76WCLjG&bXGB2n$NoHsXHalk) zGS?~tFWPEMXy%Jq#yShynWFsn^D?>w-Fnk_?5rh>!X&L`iV5G*8^KhUY;r9gQl96~ z@UJdD^lohDVarkC`3Y60>;yqSPHdjurbA4aZQtgowv(87yS|R;MFmcMdfpA=0*jQiAqB(P?FHJ4OJ&O zNEfrCBjUR^LnkRG-2>zo^?nPFA4sH{X>qj_gs(V^BG%FeM1>sqDC|O4x^b#`;olNX z)-P8x9~5g|N^3&h?q#bb!;h^Y%{5L9Y>SP#hO?@abHxXZ!^6f=Hs<}AN!Jyql*vAi zGR00HtYYb?Y;C0Iocg7<=(*s4vcMjeOaliGrRJcl>O|Vc6tyo+BTOM0rIK0cP`tgt zxo->ND|vgvwfZ>T7Td=6weh*2|q-0j{Vd~?+$nOC$S`LQX>ka)d+;q93-AoZHh6w)3R+y6@L1YMlo75 z(77Tl@`D$VGD5agKRU9hRb>;~`%_05Ux#lFKv0I&w@31&u*6}3megf>bk}1GPT+P# z!yG1_#|3zp`$dJL+A7yO4zDSNgkI*0>fP7W*@dXy3}gz0-PJz>%*SiiIy4ByEhtWJ zn!l##9}t#cRqGoia2mv6Di^&C?`Bezp&4`rz=b;09DS^H)N^%c065V$_z*6i)EA59DZQGQN5pp9?0eI!$*Qv`;h?X!dvHwO$ZRuK`RYQXiJ zqxt4@pF<|1-NRO;6{=AMB%qyFVdw_H~yvV>z>qR%mG7_EQq4C7I?0`qhT#)onc9k_dF{ z=Q9x|uQhu$nIoE+BNlsP?HVoqh-j_ymd&mm+%@jV!{K|}KdUTo!j}n^2qW9cQVH1Q zK{GCkVKyyo>?~E$|7z}5b7^B7dw&;KnqUgd+_y;Cgg5OgP;1n(`L6#{eBt8)I$^=Q zpK7CRpC7jZ?N4o>dq29)-8P*V3s5Xco$z)Fcw3>`}0eVMn zIR?v!+C55*7N<>X8EOI9)?ckS?{JI$o326BBGWE60y0IocF!SmhWCjy6LfhI5D{y2 zK(HGGWETR6G5UnOP=69PanO+!ss>Jw`xt9^%nr)+Lh@W+Wu%P5L$^nH=Y@Y})f84L z)*!o4EizDT?tO1ViaT%bi~8j6ysb>q0~BT;@R-ae&R zh_w+Rek6OwVs~%@;BdJUQ~Q4FUL$x>_eQu2?p4~t5-wtMWsJVD2v$6*&8krIn_{`C z*4anLdnbw#t-6oeWRac;-?(t&o@2%DF(!<(|T3hW(>sUpd=EdnVI{We#b2{HnCZF!9~Ln;(8~K%&>|QT4t- z(c31j4Z6pGUREQLr0SKg@69+#=|oYdqE_k1L$~3`vbs(@F4jJij{n9`6r)V*$0!6v z{{YRWqq8%HRtNKUoqn*(gf{r7V`FUW@p#$XWJvPnw#3(a%Ah-q(c?_jx>z7+JCVrY)UP8%l9x`h<22xZ)Dy+~$`|SaJgRP?Q zo%8u|h+-o|W(b;^3Cj~7k|LdlshiJ~6nggcN{4ewgqmQ=BBrhM+Fxve3kHz`xSb@! zv>?1p)5$ZC8_>ihe9KffXw$B}>oeaYv~FHDk$CjDDv*kv$Rm}+pCyXrS=T z15e6P;>LD~3p8DxO=|JakE`-GxS$VmITO#RH~mdtWv8+OQB%^KQ4SWLDO6YS%Wp>S zWk}}|D!z`4ZdiQEKQ1|~)<|x5g)v~;BITTNdO>~ocR@R)c$txAmvpsjxKcjbXn9QEh ziZyW~IlX3BD-gL;7~jFxjRAJ;h(yg!hZb33YU0EeI>78L;Y%b|d1OK_#9)WIzLkXZ zO%D}0mDOFnM!mmF`C0F~#~<}Q-JipioGE5?L=7F3vAr^3Og5KS0}_fC0drn+EHaXx z(`4Hl{TzNw{)1<1dYgP-V9D9~CIe3Lh}L-kJlGhgCK3A{W^y>8ek9+Om$v`Mp*C z?}L*4d(ikIXLK{@KWv?YImfq6b#SoHx9e|g4{p-qZ8Q9dDM^H=tt$ywU!g~-QF#DA z$De*Z+xWt0<^C>zxO)c`?c8sn*;+#4j=9!U4W|1;)e0a^=}^y;FSq0q}D<#?|1PxVe&m&*$viBOMbfbOrW(3#jfMR0?yT*oMx zF?JhA$GG10Hhg*%crmq)e`}>Df6kA+OL7}AMnw_fRA@ZLs{)b5#l;mdy#^XndR4B_ zo%Tz)_I2w&uyHonh z+xQK(Oi8m)$=*>xl6F*ptxZPNiV=w7rf|6=3CPEst9vYcv-7d0H|Mt!F+~kCl}6E= z@`G19&TtjfBiVc>`))rqQc>Hl{)9q58r(($<1vD3uCwA}Za~Zb4;Ik&7U9GVueKw^ zWM8u1tk~!e8FdhSW-658SwPfZrbSVR`Y)6cCw@XUQhne=msCTKzXP#>sr>tfZ8>NO z7`coG|Jn77_g)_XK^%FzHEG%qOV3*V4dW;XZK5#jT9Z_mL({ib=5_4?&HZ>{p2##L zOknY%dOt1Hyc*VGZ`1QpbMQSed+-mexY_86!@xLp6A?xl*FBC(zeOjA^E&&Q9AF&i zJ&QiPbx2r(E9eLtmZMx##6;ccDCKZ_^LAHHp&bF8M0H69r#ljxmN19EQEeLw7y@ph zoUMJi%9(C%RNL1oX>|s+@`SKSm@9MH`aBZHcZ4?+yT@sAEyh#47Xsdi#RxJj)ypMXhW_%M#60%*_L!S@2kyWt~Z7dO`NFclahl+$gF96Tib)su_A}T6;WYm(^PwLJ&)$f`o z^6H?aLy7A!2lgh_Ez0(n+IfGlzt2d=?0xVBnhM^^4-;kX@ks$}Y zwT1M{)x&e)oe^EdrzO98+wz?II{J0I%6!MUm*V?_p+qEEs_-G%#xDw8;cvxEf+f7C zUv>p-L;fg_t2Gda*2OW!nop{XJyLnP+`efw+^8P3Uo8|&eBGSQw6G=GEeW@`{m?3! zB82uH{$EnBI4I%b3msjoM5pI#+ur{5f#b~z;=Vf->G%WF1;>pm8$O#+G_w-J?@Bb5 zC8b{KdX1Z#zK|zF`CAtoWusYYdOC#sLNU|+h_FLQe_?%ThbsFEMO7B*8s)PPGJ?um zPKz`Q;UGq|bSX5qgU=gf8c;dH&bSppW&-mX`pLFnD7v=^_*psesC?YlKtU)FdYu1y z#?m}jDdVg8KR<>cLdp0wJB%G-t=;F{f#=W@Rpww>;j-ki_*L}=3hIOIYdIBRhd8%C zfM+QaIVMZ%tf-}BiD-xup!YE_)}xjS@2NRVP0h$lVH3;~1R0O3-TE%!{YZdp3Y%)X zTyBqK-Z7Mr?4CMs2gc($1-6yavk!m9+~t*|e(i$W(fKEEeJiu~O*c{j3Z9-hq!=IjZC#Tq)DMGf8W&Ao7F+^5Z)rvJc(+Wp7}FC0dl+z zW202NThu?lb0Xc|QnWr9=Mx^Q`Jl4v@4sQCe#ib$?KV8Jh!;f#Nq*Qf5)4RvO8sM? zo$L-rXsmpofeM7SF-*)K4DOtAIoRI8)thk1#imxorr%WcR+*47(XNemozF3H#sj~1OpB_&8C2zlSPDW3x)K9m-W z{~uj%85CF3cY7x!gdico-6goYySuwP4DJ>P!CeO@xVyvPI=H*L6WrmQT-SX+&wEbQ z=?_yiQ!{&4O?U6zd-ZRvKSC+n&l3DuN*;GAGpF6#BCCez{2PE8cD&5n4_N>XSWc?!{aK>d#SCh$pqklK)&zio1UI&>%CuuUK0{HEwd7`pn#3G0d;?oHR`IeX^Dr4C54Pb1hHkg7Y7 z_NS9cn-|iZo%DbHy>|a|faFhqsiXfknGgTd;gF9C-v7@uguI*n|J4qFT-7)JcS_LR zzlgi>J??zX@_B_6HXEV(D?k6$1gph`2U*_#GkTd*D!O;tFks(xe$@^1Fke~1J7d<= z^D^VQ6U6BJ@I}>x@A>`%1=G6`v4afhbA3;YcuS~Erg!&_rEGSx>p=}K(hL2sg1N#c zyFPueXVVXd;ytBHZelf&@7Hs2P3yinR@0cq4};kBP@6&Q1x=!BiF;L$%?WwQTr^se z?Vnn;qbY)!q69zY_12BN8EDO0l^;->izl?|*GoMbbM*4HcCXZ*qpT^L$9A2En92aG&+N`Va?_ z$0G!K5qkj6SLX${Sl*n@if*PFeOuU!1(=@F!o}e%?Xbczq%~qxQqX zNblxiNuCuJiZ^8rD9(K(9>mY_A|KF~Hs5<83w2ByyR zM*rzRU=Kg@tM+g1h$Knpo}xN?j`>!SI=6xSS~Hw+iW``$*A3hTtG&ea*0tkcI^Jd} zn~4;U8*ULlm%fivSeF_Q|=7IaTYJyHf2Wtz+1uKKR9R6{gv39+};f37(Szh`A z;8@#bbBC?xu=?dSQ*OhKHyOzrBJX{EXTT_iPMNTvD80{&gX5j!MxJPZDD{8K3rS#{U;Dp}v?}5`NPP0DC zKvJOw+CprjU$@r~1q$wJ&SH-zcx}?e4k4@h%W3H~8Iz&h+L@JeKEP64JmA$`i@gr( z=B7J|sRU}6-}%$h7c}@=y@a6E{qG4cRB@DDc=ls6maV(Gx1_D?)EOqAiE- zVkyslUKgreiHmT$)!8xdIO%l2N>AbSE!URyD_C7;S{>moo_iv$peB|$fuQ?AT6IxaBIUlu%~kd5<%<~027ZJlZY zM#RdVTr;?Ia8=0k=VUkUb-s~E);x~TYaWiJ!Q;`19_;HG5IZR?VPJ7k78@+d#IcL9 zx<^NJO(XjOk(t?ezh*0=m%o3mQte9$;pCisyxkrg+1L)C+&uxqE~xkd*LF9uRfF8) zaiX^FmXy8Ub+!fNIXRr1{s<=Ft_J~}`lSG10!bc9+R1RBmvlUBiD@q68UX=3T}C7b z7zO8T)Zx;;Uq9M5o#EOzP-P^Z-um@@;vzu{&`DPh~#Owxp(*OunM7ZgqIu|m@ zVXt{Bac#d`O4rocB=^)AF5uZ?6y&0d+4Um#eDCwyEhUyXS10g#=Qt2Y{O(GRdnlwr z-+84*lqf7BpNre%h`jkegVCN-aX&PtugSTCVJUP+LhX*8nvXSI9kMymTcAUQp8Ry!n?AMcBQCV|&9zIFUD zN})DYN6fOUN%+ud(>rZnmc-LYLY*Lkv})cs7kI`IXF^o#w-z>(swSkw{1uJXgq9wQ zxHoCJjwwk7B{Rxr(KyP|=G|vx$n6UgqC0nDbX@CN*Q$QecN`*Ttn~=;$^W&1uy<>w zCO4(SB;(9A`0;mTCHT@z&DdRL=K!*os%%Bjyk;kBo@FvUxzMVYKRm1)lxm?mH`_jY|EYI&yhh?z+90wVR zET&@C+t?9Ob4$Kb6yh=4VzewjHs4*2T)O}_Tz0>j2*}QqWHS*+bn+Y;}bWX z2D=J;7AtY1Na~J<(8$tklefHZ^0|8?1a<=OV!%tLg@}XF- zo&Sj4?u?wlBQShWsm+4k&I4Mwu(OIdwBcQLJ|5C5x=(^Bs*~Ha?#yLO%pvkT#)=Z#y6lqT_gD=p8VBB#UF33yGRrc1Z$_CH@SkB8bOd>2z1*<{6pbs9jn zV2YfyKsB*Rd!fo}?nzJ3(*@frL0%~E!kI%maV)C&C}^wS~wV6t=iZ<9@A6=PW$J>Hlh1R1_c z=3c;{&hHRYF&g+9Kv&ldlIMe!QJyqN>X+HQ$Bpbj6=U<+S~ z1o0SdLA<{g@ZMG5QYMdeXmPFn2P{tirlFDSTsW%l0ImBqO7g1Db;BvnW;L2#zagPK zR?r-h03y(MRlij_O4Fdhxz2@?XkK!m1VGBaN@XL8!n{WMKA7=ehy?t(nL8*LM#5cv zbBU^yceL2)O9<79J1=h~lZ*ZDtUoWem+gLAiURUjAAldv*U8S}euwOpboZS*bpNPP zA#=u^PmuWXnZdTi!S4+87mhb_W%fJG8_JL*)sqd@UVC9OqCH0LqbkMbrRg~ z%9c|pMkjJ##U_sr^A_hgu@q5!h`#WjdLFFhUTU$t?}ipCjMUp}THtHe{ap!z>@Jt0 z`h^GC>4&Bh!V`+jzTf4NUz^YZ`F_SEWr?1|m25ngcqP?TmU6qpu#2IlDMu z;YkPZuoQ^S_h4x(_hr946e|y>w0j3E|As7^+KQX(>v`PTtpwl&sL%JwciQMRQPfW^ za+(26fQ;kcE@FJ;ld66V@Q@HWEHW#`BK68Dgr$mNhRQZfe4qVFlmxzzzE)=P2>w@#njGAOSJ zxX?b|9qK+ws?b?9>a#w!li3 zf+#Ts06e}KPE7JfRAO1#>@UPtmmB;zpgs=cngS)*_iUTobgY`qvv6WeiF(Y#UskRg z6>fDpJg4thU~oJ<$%u#lu9wtu&U^q=RGN1>Q2M9;Qv^9&r6!BO$=%o%V|zLi6l-mS z^9>36X+50qH5dDS$Y-e3j~4RF(>P(0n-2UB(Uk-v%Q&?w9NwXkAI4t{$Jb_>tat3W zkDq+eZHiDL92ab8Vvy$^N$b>DFmC3VeiugY<@F5&M88ppQY>GqYk`M|&JdHFDa{nz zw4uFZ1MnK(NjREks?AM4@kB{iUUa7}EAVI~gXXE3um9Mq$gmw6FFUb%OhjaNbmno) z-}Cw_d?ELnDjxRA=v!{I;h9x0Q;o_C?b@GBDa+l|mZRebsXYxiAGHv6m}WbT6Ru;m zop&=gTlECeE}1#))4aM(j3zixQH$UE%D$bjm7J@vfVV|!Fn8_zedFkrxvZ07zl>N_ z=vmg&T7{8gy|jtKlO1OS$M^1boR_ten85cH_r{%{-XfkBdK2r7+gYq}F7mcSKQA|1dX@Hnx39=yoDlB}qKU z6!5u(L^)15XqR7ZUB1N|Ly~AI>LAkUWm)UtFooA#JZAbqv(9>xb*jg0BZhdo2TeF?p+&8<3Pl+J4QIVwO!LGSwY9tP|#2s})O zvlCCEsl*VqKuazU%u~i87f`IAc{#<)l>gxyyjVGbUlZPZzQ}za&HMyS67Sch!)^3z zWcjST@?0sF9A^|1rcbHq9P_;cUKi%@+$5#4H}_!|Qgn$mW4%@dZL8T}WFxLZHS*wu zm`_mbdi-qj%l!26LogXP4#=$4^%bTW87ujP0gKENPg6n(C*zB8bxA(gjPM+Rl@UR) zkG3$=S612t9sJBWFW|3WEiE#^!bSiEUA_TQF=$HGXgjpFu zAHHGPo3roNkmFk3gtR)z{#nEzJ7e6IPl?iS*i5@kQ4nq?jx9Yx(d zb1Yf&Cqo6=33amIQSs3_4&kfNoQUP=4lc>`adM!FY}jy^&^sa#K#fY_ycH3nT7#BR2I;EUc$H^7*9agZ3>_VnU8 zbbyY7{GQ|5#ezF}Yci{cSw{*?E+U;VFm9twTvHT;k$G)d^cHN#gvM#AVE{Dlkz2gJ z6pT|r&0TV9*7rqK7yHFQ#z~AP?S{&(Pa_wSZqTA;_EU@f{^Onmqs6v`w!JeeVj(B7 zxbdw*DS5s_z|QUtN_#Ifbm{r^?>%-(nZ3CgcNsM<;fAVoZc9XrSd4^hp`F{FR@WP8 z${13ODFSlxv#ru25e+aj*ITF{*9c)htwp*Nm0Dbq!-hq^Rr|^|)TTU7a?vR|C!g|x6 zqQVp3i+cwYE@bJR2_NOASXZk9+YOZRNVE|eS_ySq>k2HH1ur+;v z35E%A@ffiWuQ6*7b2Zdt2$4MEbIs}B)Li(g(zM6~3OeMLhOi*ow*>3bQH>mj;vV@y zp;ad4S7NcDVmEm)HHuN+au2@dFmnDPD`*O57XND1qffKE#xIflA!<*wQNqWu>V(Tl z4MKGe(@PYaEXne4j1aU-CZ9`HyTQYsFzK|bgCDbjDtV@fNfxtkoCt+J6{K>CV1p`; zn6jKLq3zujOoHgIo*G2pFW)FcWECF8RvQhmQRqS%py8!4T6oy6gd$@wMiEg|ugP3Y z$n5U)($aOOl`BB3nkL>Y2pL2w&D>=z)|0#tBgS&M>8w@nIzy=8R|=={Ie)RF_oO`J zj=$~lO2>Dv*tkO+lqW5{E!(ZK)QjNN150*bNfzZez4pw>&dEO6(mNa3d^ZIQ*NvHe zax4VLHCp}oDJhrI$?z1Ovx_M zq!N2#{rjs{URWUzBY8%%{P>$22+pl4VltPpfYYrNE(O1hnI?jz?mNV7ExCkSq*aOg z<9(>0&h&ME=eBGAKoa6-g8VP@&H9-L@$f#x`p(l#9NFTI^DU;&<>%)fJLK)Cit}CY z!;O@_ddeBfYN^bRSVBK|4zobJUQo{#E2bF9Bzk9tHKp=$xVa9SGY=9|b}2KC31iyR z!zCD+GvWD(Np<+DOMUZj%b`y0Gu7VXzLl?!;7S#-&X@P_^fjEn@=G)ZKlPvCnY-qf z;1&#ZNg>xLq4+KX;Ok4td7G!Ojlk#w7SruZ=ofFbBB@P6D)RuUym(Et}(bz zv0QBD{DOnM59I|YF+Bg(0*p*nh7cj{1V2jJv@pq8u)$iR8*9r!fC@6B-au;kr0~g% zh>2DJ`GbDKdG?U@Mfj94)|LID{>s+16dy9jWF@;0X;@UA9ZjTT;HN=K;Z}yB9;_KN ziDIWE^)E^JCXrU7j(aR;5hjXvFuJ325OtDXp@+}%Ig^C=OZid87=Ab_eWWHfd|!6| zaV{2w{dKr9R<54GFUfQCs5H~eSME_FM$`Xs|B-Is#AGYsNhXQ4QE(aY_K)m(UHs;D zufBP>b`C37pg_l?3MgtAJ zq!X^{3~qsG6?S-{4AylJ9L837oLUg2_%)UFsrF=M=XODtojZ^`B#8SweJql+HF-T) z;!j@kj~RTrC^Sv-{S-1XXd( zd@Yw(V!^kfA%rlIVH_8?K=mXFs!O=W*6nXz%IF5f%(IblMzFFS_Y{b&Z4S&vljaR6oieb*<9&^IyHDS*c9D~ z+LG)l>ab$hAp7oTD6finY4wA?S!^oPSgiu$1Wi9aCAVFM;tbo5%WvO^siSSVimuw%ZUtdc};7^H&@*vJ>L-1SbDA)Tor`E&b-IfrIzKV@5Aq6~TL5MxAnbejHgfmvdzXMBf~ z+GTW|=3(ieNxW&O>{^Hte7u_AFw!`5>VOetSczT0iL@R@ zQ+pOAR`YD1>7^39-dk|B*~Dx=qvqsS$3mroJ+n7%`v zqIrfNYhzkiVMQgMph$i9t4DSChxSsQ3@?VaQzcWaS`#88r&yRKN_4vwTry_g{S19a zdEaR~+!B44-gJj{+tK-;?j|i%l+HQE@!2G$EE_BR#WM7^WwD|*$Z5!e8lUl1mGMat z@LfABk#)Gf1{gL4GVoOiCFioyaK&GtZyQy=|gE_(uXy?SD0EdKpDw2TJ-eHKmmfUpWhq?uRXmW z9Cpyl#xyrIxNq)FE;&^jM}~|zDX+6)p@8NtjC{57BYfMWi3(%)jjJNAO$TS^L?*F+3X0ozhV&?EQmRZEbyy7X$&{2ZKNK|(KXFIg zG^`Nq9eRxpWg!HabVQeamkQdFaS4eDs1wL zCadzVX|HM0iLfTfGEub#3IEnQs`&lwm z3Twm1k|}{iEl{lJW#A0~PddMq&`2B`GCQd|*eT-Y5wpK+U&6n=9J~wFkrSi;=4Bfd z^A({Rct1_*g*)?c+XCa~=Kj&V0owW548|a*4P|~AabhGc$>fT(% zhODutZAINRh(51o;As(_Vt83WJf(b+p-n~V9K=kEAz zUA#lv&m~HpXc~_CU>YP%%jEc)7AsFxt8rvm6#)y|#wC=#|sb3=jtapLDI|BtrYMS3bQU7S=-z_HNGQkV231{ze65 z3WF8%^=?S4L#=60p1oo%iO+zN1e@VblDi!_ZE0IB;J}vn1gG?SPGc)Vq1i}L) z`zZMI_RAVUXW|7d_J_87Fb<#NW}63$esf?^@wiBYO|D*Lt`Tr3Hu%I$|&l)b?1N)8YpyBF};TdEV7Ox$K&>D?SgivORAv0ruOu zG0SDOD0DMgB8{2^i{EEp7FTs%?29g0!bs9`!p?l7siltCcKhH2-V&6{DI>P3bR0>& zQXPK<3v-343CU%#$~0+4VYKa!sVL7H`c_Bkx4rioa6VSa{|A84#V|Gzps7Ro&X1^2 zYVsH1!zh5|L-W^`cLlh2r*1;+k4=}1JJ!q=hq~kEt0v@A+KL`zrfdsfBgvEBwRP@U zr8Hh2J68=nY&`LeiH7_%KS1s-kmd)29>TO8NP8;d_`Se+#rRApA%(RYCHX-HND=Ud z?C)0~t%(3qhZrdS*xBuE=1X2!(iyb`DB=5S^5mixMDQ%3A=xAWfo-M0On@7RInQuiKeXhYWDUpCOdhpmoOP-ks9c zog>)U%dhnWlnb$}*{UV(+h2CxmEaBT4xppqQfXOe{Uoy#SI&ea1wreD1vNfrI`?SI_dp$ax@d0?yA+;R4uYZQ{IDac z+NP|WOh*04mRjw!AqgX_Xt*Wv_P`bCEE;<^9-04~^xZv!-RPN)rPD^xm!0rh?vf3WTCGaXCyckG;OF{k0}^XPC>=c=XXT41&pCzg-p`{_GdpPKUgIamrA+8vwj zCXY^c--|PkNa}OxnGwb#niBPw5J~CJ9+jI5e0)Jc>b<`uhtM#Lk+V$HqZ$JFBs*!| zLs*`F+eTKJaL<*xH$8`Fkk;x8Z}DWc3=O8ECrks4&V1dPq48E9k!_0?cGPT)%-+UC z9bZMh94ybFB@w5rW5|R8FrH^1in9JjLhhS-<)AsIggR|`AoQR~KO?t3hK7lxj)CPV z@R^+Fh4)u;WU^`r85?((Zs9LzM)s7Z1dHugY!><88Q|kbCEdmwRnsROzoMLMI(ay! zmqA&)60kvcWZh}}@E&;Z+ahJ?u%YOrgA#DSQ~RtpziRRTK+;^+0nL3GdZoHXPi2)L z!CibwCP_s)v2lRwlyK=o7fey&X`>kLoQ;N{m4nw)Fj8f8E)|*Td?RvO&#{S}t6Y5W z${5@(#*q8J^Yzt&-lu&fHH(|RDL2r!>zCgGYFc6bdgY}(B>V(-QAijTB9J?DP%mCN z9J9LC(-Mhuh%1QtfzA!d=ycTcGIuLKNjs;@rgyK1Nodveft1csB!SXJA7n-)s|M0k z3@3Q-l=L;E>z3Vka(fBbip97hT25n^^;nC#<4J=9wQPjdHHHUxgd0S+dWj{Q4PdS! z*BFH?+(K0@Sc4c;caS5KF--^d|+4tm?ME~In@0Jc3 z^t;L>sBXqktbMzlI~Ew-5kI!}6BbyMmO0JFv7J=;H!X}d6}wcit@Q1dmt0+RpM{c$ z&3*g>BcO*=a@fu@yGb*hR1iEb|E2^V^DY??_xkm# zYYlm;Yp!Xj$c>!1(kIt(2_ZS2l&;U4hg6nC4wVe=MV*`3Pkp4Z-9};^S5SMyPSx|L zlZPci-*ozUNTe+NKLuL~xTv=b&^WqmH{p`gPT~BzIFFz3%S@9$?RrhF4^#x=WzyZXPQOHnqHo%wHD49oM-(xYfwyM=xXG!T|qo)p% z#Uog%%>JtdjQI?!GJk@nQ&vkpIPc_%0^gOPh+%NCWEW@A(T`I|dpIg4H@V^w7qFsx??& zLxZ8grSyI14#iT)@qxF`(oZ|HK$c)9K>lOs z3i0l>0d>6zZDi@0K1-rBUo90Ac4Gq@(~lS>ZF!uO&omjMG1_R+LbOQN9W`Po9ZZeo zju0iJlx~(B52eFV{I%MryO5HVncd|8ZXIuAqsp0KR+m)2WNgO~vthc3>{lP7<`Pw7 zQq^eF6jUX4WGX5BHrbqU>ZGqsC%qHCypGJf6I_x-+88cj_g+|CwA z+~t1pye8XeBd$w>%kx_UAD(3TKeasnew$~|9eb=eTmIdO^#7u+DErx*Uwv*L%s-B> zwT#=UDlaTJrp5gR^E6GgCd;7w53y;m#q&4_{L4{7nc+b4Z&;DuVgA?c|M%dKwE#yn z?+{b#U%TxF{fEx7ZODNYMa5S7rvJAlRTFiQD0`$B-aCWapCTRq{_gMV|Al}4-GT1c z`I}thBmHo|mq8GpuH?RW&I!DmGOjZ(-rvh`IEIgw9IJGRzi=vLljn*>FRd&sW$-u` z&2<7j=2$MKl-1B>we|1n&@fw9dGpYY8iaUDZ3p*ZzD=tMdQm0z2M<3R@SW_vB`jI` z>%HUvn6a>G`>mJIb@I!?1P1Oar9Ye&JGUjY|4G8s5y{W+El5$yH6&q;R6I7FC+^hU zh!)r;w~c>?!6NGReJUV7jXLB#>I7C%2Z3yB!J&KA%O#iI5j-Hd$>g;X=ppU==h##y z#P*xFFEPl<&`J2YM)rjkg;Y1N9oWA?b>CbbVOBZ0dpkfn{_T*>q{Da#lA(ui1|KiT zhE0$Untn*#gZ3bLeuH>uAn|j0evsJAd!a04NDfOLPODXXiBosP*BtrILvfhAud!*V z(PYh0jT5rg_c|~Bx-g4|9}dCu5T2EeBwlRch&RRKRSQ!DsT5KB%6LdZ-$BU!`vlC( zXcEo7{CcBckcGT5@Y@J65eM10({Z z{Nfm55obDJUCQ^Z0S@bKJl#4$P$_E@QG(3yl9DIoFCBRtL#szaf>{+Z6R@Z!4LpFP0#~G>k*EIaQ zKs@!;8E|Dgi*M&(vmikF~oNb9n;s5Gu`pua|3OlPf{9dhEcN1_ri_g}`Zj z8sxT%8N(XsA6a6!I@+`H5}uA|>u9R(N~yHNVe((D9tyH7`6edmY%i9?_sz37hJC`d znhb{+>-ujqK=;}`Ce90|cb?GD8Jo+&?3pFldhsrg3FCer;qexFAfDAa)6&u>HJBfB zzwA;7B^NrfQ>ZX}z@v|XwxJG^+SjRUWw-ieuD7meHI7<4!>0jlphYcaFmvxO(>{QY zOfTbeo2s>8)gm2Win>1Vgb_6Hm^GnY0Z(cceVO>``mIB#rFa--Vs%m?i6HZj{y!C8 zWj;Y>ePu_HPp@dGRAUp5(UbUC}Es6PEFuc$DhA3^!4?5d%^0Dt>gnJfeg|KzRSE?S!n z+~6PGkmFe09%jRn-&m6jJ=|^ubekBi4##Di;2?j8Hr!pTiEKH#?g%lIgZB*aRw_CB zvzCxa160XB8ETnTiAyyYSLj>zTy~a}{@!jaIvG(B5XXn2L373LBt4Sc%t$ zz^)~(xA3{2ootrs3XcOLCO3w1B8#!2WJj2;aFK35tB!WXBmIzw{1)QYF$1Y0$Gf+Z}q!!TV_s?PO(`Dn>}+W=S{2O+MD1& zcHNSD8Il%)GyoCSbC~3*NXS!mv5(b#i0IQ8v2~UcF%A(jJbLQB_UVzyK>a=i}+z4!Phj7V7)^dRxxa)TeJP)bok<7FHBNfzuJi3 z4Gq7Iu4GV)S~A9Y2jZYc{2usy8MSq0xx*54Aj4<(Arp-bAQkoDQzlc`6l6U+ycfCU|mQ$J11kuUPUK;_LGS8UT z>%JHlk@Va88@j|Pu;!&hqk!)|PYZ{$3b%Nw7C2V3Tz#?fTysIlJ(MsinMPzNmMLDdmAg7W2P~HuX|$qG_8j^M z8?Rp0Y`DXPRRPA9E8K$rDzjX1F?%UmWV3a*K0NTx#Jod(=WcRekWz?N zD*0FlrEL5>XdtE2zFBA=7hKb`rOc=x_wAX&6bz1JJly*c^{I08TWlM zg{GWP)ErpacesVY-b`M=3miiiJu}1!`}kXZqUp%W?{0Mm`ZOeDYKq$x>qxVNKSb&9 zuuYq;PJ{g*8brgn%X%P(k!elI=;66}ez_)8Gc5y?T_X6w4o9^H(XJ?z5tZh?zG=TP z;*6>&))q3LuxF$TzFMmE;-M%e>${~$W)PKR_8HWdcHHu{`&QH>wC^Z0`tq8B_R^HE zytX9J-^K$>PQ9m7mt$sP6G3fRFO!zFZadN){Cso_J%!c>m2}M@C#=@8T%Ul=lz$wJ zeusaTCqXMEtE#i->#@r|1+kRas&U)>fv3;z#A{~kW%#Ct>hABNG9+o2m^FpI4M8ZH z8k3)tL@G#QK3cwJR zmqg&AJA9LB8dy3`3F6fzp*FtCf04q<#fTsiP-~e`xa+%+{q`Bdr}Qc#R#q!8G|XJ zuliOO7vu&^IrVzhdP7aur?}>z4{irB{PukV>dAy$IJ1P_p%U{srGGfqNt&U_1|6Iv zwUf@{YB{Pw%w_YNta1H?dY;9Q3}%e?E|X#io{AyTb6;2?LyqbD6gHV%>cG9uT5I5r zZFPf1AVR^cp2pfr=JAkpDCI3*09yfl8usQx%8BR2@tWS)bwW)DZ2{nrO2^9|p8&l8 z$D4lfAakARNcFc;pYLj~veQtA0v{8i7L>=3%qv2yo!B9pIvuYvGJ9Phc=;tjL}@+A z!in~9qIJy~Z7}8<6I4SG+0=zdl9P0TsGHZxYi$n^>Oj-ZT z{i0EZh0-jfGo6(J(f7!|WzRYny%%G%b&Sb-k@5od(pU+#vS@`u;{Nneb3!YTI!nlN#NEQO}Qp+>1ot(18`xhCIeL9uii(%m?_(iC}(kz5R?>CeD?|3Mpl(V<&CQYB+&BXrj0a7k6H`nhLRCiQJnvRtry zZ;O` zvK6sMiDOllTnyvPKTm#Yfjd=wy+~L!affr_3Z(n?;ahUXRXvS^K$@T znSNEaUEN=8i1qA07-b%2;?LR8|7roA*>^I#Szq6EMzUw>o@zO?u({#C7j&v6-+#C| zv}>>5qlD`8`Af}!niu%Aniv6ZljyAXA&2m#-Pf5+*Rikb!Se@GWcN1-mMirISvFRC z6_+zVxgg-Crnbwy^I1)3yNnreUzN$Fu<}yf z{gDaQH493@76OhUS~ayEEd0FZLf3@Dxn=dZMK{YooJS4!!day5;eU8o;@f#K39zA? z?LuH&t`!&mLRm_)ASr@yYza41WnCHw5sJ5cwMtP(3IIpCC?Q!&~U zja{)|yCN_=zB$7EtCBh*=~9YoWv8I02^fJx7o%7mi@hn{(|9n$C_L>+ z+!A-brNCDlh3uH{iw06x=0@j1PUY5y$g5#unV}i5T%<;>P{(QJJ=K%&q#{GrtVG5k z3V?!7H*`u&>Lig(TAA9N+C!qAGvVXlb4)u4VPO}KR~8%C@T->@1(nLO=dKKkS>(X` z1oa`bd)LQ=-s$mhW}Tz3@i)#!!*KX;*mntMn<_lGkS)gL{R=#*;I5MfADPh*(bqmk-{t`T~%nU?GJ z6u>=0ELuVk-~LTKfo_Q`dRDT)l~&fmSP?FTrp#F?PNAc(gfCaymZ+EIR=BJ;^-+Gq z5(epwecbUusr%F za*^SvV^1Ww&pp;{Dx%L6H(@>JX{(fs6b?fy7>k1PDR^x=g&sd04N zP!BmxVyuL&J$_AMK&?`lhPpjGKekAq!ND12n2m{uIyK*;AQ$zWr>y0CD!bUlvdHQI zwe&Q;YxQu(FPfb25VrZqH(A@gFWa}zqS7WPSH2WXmmd8eL!I~n$xRV%GJPxN5c9|EP`#eB-ntf zq(B`WuEQW@^}RlzT^yfh$WDyWG9|~;$DYSSnEWYelSBYQ*(k({2NYhxFE@{je)qc` zcgv?r+SauEKI~bcDyD%`#6`nXQuKMuioYjKuuwd0-cNoFVR9`1Hsv;{r_fFkvZffl zY=(pQKf5Lk554yQq_%IND}&OmOq1#IR3$36^|f^hj5K*H>`EC_Ct5Z{%~BH-`U;hr zS8nFs81V1D#oSIoDP_-+=upeKA?g^t={2@F1*~AKxNn0_Ws8;P=9Q$@_q>X+$y^!$ zSA*f4b}J04l#_bY!>Y*gyL+Mu^X;EW2sSB-eE4 z<^5`S)#u=msi;+~^9qRzXYFPu6*sTEgoKFVqHsC*mz&7w*H$XgD9!n5aWtmZb;ncz z+^uB5-f>)O6}l*m!m&2}{wBkd;jO{A9Wg28Xc6~ZHKtt0QCQD76@}Ah5ywX0m)&i` z<#Et3<6~xO4~w9Oo~+XC~fh6?4+u< zoy&gDL-#|ze5iE;t%8(t=|E)8l+QRYeHQvPwL?_uvgYl&b0iuY5wr= zoqMORTRILfp$G~iK>r7ay2G|{jRItn)!+x^-Xb{)=Ub&s@PeaRh12CZLU1@GUJbHI zgl1cME68-gM=EezIk0EcU&NfPDlN$A?z@vbR=HSl{3*hIp064C>7g=dwyuz8(bm@I z+4{EnbIDYqnslPK+@V3kns6AD$#$nn?;Wnmb@G+gNZp~bX%ew|DJswm>uDLYwtPnZ zJtvwIkLc9X!q>f(Du!3}(2Ow_O3~I$n2BmKXP*34U!8vj7mnRCh|)(i$4owfSNQfx zLXX5FVTB_RrJg1BZF#lggwiLMTzqNUOF&>ry6+-%JhP?v#N&1bhd$gf+4i!*Z25@> zAZm4*PyRRv!ayAK>Zt%x`X9vvTL6PTopXAxR7u z6as57NvEpQ8$wnH3_d1>*kMAW{N1Oju?k{v<{?v!vO=T+VAy!#&k3qpBJ)lD?7ySp zAc^KmYlM~>JWZ0LyRmA0HCZ}Uwnew~%x%kN_lZ#Q2r}h0v^~~MLcJIw@_Y3nwpQr* zS1JWE4HV%;#LGVOq>i5Df4YgItu#b!$Wqzr1C^vy42KXgMJU|KsIv>NJ`|y8lwHolGr}Ub%4Br;UC9eXTc{YBHRuK=OJ=3; zR%?L%2AdYvAVBi(v8|cQrF1c5KI1OuhoDv0)k?T&m1%#Y(lS{vbYMTpb z^k4=fu96O^&6=e(E-|=7L-`>YzX#_Z;9WpvV{~L$31-Vpmv3nB(~m z4>wT~aiU~oe$p=wrWn%A#{q2`@BDsaHqQZLWB)(e-ZCn#ED9GSAp{5^1PJc#?%u)O z0|fWr9$XUKrEmxk+}+)sgy8P(?oiAv(%tDdGw;p3S!?PCKavGib?>?R?DOqUirl}| zTEMvQXP@KT=4B_#2wm1lRi6pJl{fW22Sgm-4smsFyMJJJLJy@g4}@gdEsyF)C2nlM zA-@&A4cELI0nB5h!>IzzB)zm2J-K4>+J0ht<{zNM2Xi}#Z8Wt%kK%U9WtFOi?F@^G zN;toF(eKO&d_Z0bZ2&hSuci2+JzNfotY1hC`rB!b#fwE?0j|R!KOh%MT!_Pd; z)48QCI5wI(25PGo)8?|8>P6>BW-IHG&QKNAlcg4&<2JfRD7=__*dt77_H`}MGUCqi zQ)tu?9r48t2S$!gt8f}tRsmS(Q1x$j4B*YL0@p!5zoOt%#*l1v8L9j46D36H*~qp* zH`QymaF=V|#4rhpGn#%Ou_zE4P-5S^I6H6Z0?b^d@v2!z2pLN8$=M|ixkAO~+<(M# z1o*>Cur0c*jmu3_OGRq6`Mml~2LawE!F^Li_`Dh$fw-T{+G_YlVk`{9PRvmZPve)P zX>Hces$1MytZgXh=WvwdCeVI)`pl2X#J?2K(Ys8}#vKnm>y;K; zY)^RQrav1P`l_`O#e`o@8ZR+F$6xMTMZ@~R5Et$=LhL#Tc)Pf8WdTNU8Jnc!84S=< zFA?R8xLj$Pi9^-!mvVNowdcc>CWu>0M}Bt0(n|~~AZohWyS;PC$xuQK7V*O=etZLU zMf9)T$QLTz#R@cq(v+k56xtUdU4S_bsVG{|(WW&9yyoP+gWJzD1*w^mPt2o;18@#d zdRXos6S#*lkw;Ip;yRegi8oDKTJ5nkiZ7~fLw((=s^qV^TE24o=UQH3~~j5%E)9k zu8Epi_%2bIBKlMd%v9#(mOmqGMz$fh6^xyd7{G$6-=iIHI4VvOX~}(CgEDq=o+cRe z?1~Tgew8nsfAeB^%Z5}SekaF&l5&7I{a+fX{Lk#MprCZIofDq!9PpzK!Sit>C4&E^ z6XNR)1yvawu*rMAO`_wvKk}AY=3mCommnduL?i+57a%~8NLdQ)>d0Y-`Hx*p&>8Sp z5JG*K8Dd@R{&Qo3eDcVkjCsbKF*^U9&>qtLO$&0r)ZvS=t%;w1JXgVV^zs+fXk^Q@ zGw=RTdf=vW$GOelIB}$@-@VwW4W;zwX^QsCU72p#P=}BI7W|`j zWQlsxxnEU!a*;S!uBqaiovPYcD6|vVS%1%T?ax?*D~jSX-E%VZ3m&dw_$hv|1`Ye? zkrSr9xhnR^Lt?aFWhnlZ_z;Oa5iV=1{C17K4D#!{O=x7W=Ii%K?f*Yv! zY5dAb-0-bfktb*zWsQCC2f{DRO-t4~Wqw8czfkh%Efpm1ToCNXN+xWs$h2Gb-zX{0 zpq3@ueZ6CDSOqs0ZxL)*GMThYm}+HPz%%4%xusYlQRPyq4*XTGR9bgmT;rIwhZ$GX zwvZu?ZrfQRe1Uw{}+r|ZHJ%eil7#$L

rcnzwL;CHLWCSVxcZBmmJg7Ipz|UC1OseV%9koIV2WW(S#|S5u+Q8{E&0ekx%GE^t|DVM zz9D!;$=h9;C1jZ%=cUx%@R&0b&QTIh#Y~6bc$`viO3F$Tp(44iR5dl*8y~EFQc|YK z^k#2OeOK0D&;07cWU6ciN^GG9czR)l6hS|Yec!*mYDCL-m&tR1WepH+7@yP3Q8WbU z_`6=W9rjWD+#gUkx|?`&gxw(&i!eLK{w}`4PGR>Knl@)M2`@mOvM`WNJzix-UeG#z zWlOE_^(9Fum))eu#QT42p1w$QDQWdGSB`pVO6#F?_E}oxgkRJ|)(IE0c|MVhKxg6> z3dY|k(^nMbb`|><2>H3K0_)LiEb2S-+5vr6;~JI`nWky16G+}IE(W@rXyzW^(FJ!Z z1EO|uuzdwXr=S}@z_arO)hX)zVkYWmF(Dj)2)DG9NRg7W61sAx9p)6{*(`H@Xjq?B zox43}_rEywKoXFpFt{lW!H6Y&ZTBOgveK^0^Kr>dL{MQ~D_3Px4kk$U^D3IdK;&MveyGeGbNy*P0s7BK3jcuTqXaeF_EZ8^l zAquU=uz%{ML>CXUpL*81W+_EK^Xy8fnrq*jm1l>(i9{{Cm8E5jfUg>_@FQ4Y9OZoi z+hP4djo;of4@f>W-@51O+QB()zW+zOO9 z5i(fZ75c7BH3TgfXUcojVr_gY%b#hG3b%QAs{Or3ts>6f^6-ijgkM2OvF#UfSpNP{ zX<>q9w$Rj|RQ0ELu8x>QQ;TDq3XHu1mc^#w{ONd=*qtL~s`cPeV(N0hPL*FG!ll1C&=I-@G z3`@Z|yF-H3UrU_isiI>2E6ke(XZVy#0OM30$6VbbcXB=b2b@y+M`m|TwHAE#@ei5W zIyuVRQVm(ttmI+?^acwL8L#-sG=OSUA zy1~WOs9G$qw=x$c5#VGgiM@kU13AWoTmC3<2=;l6K?j1XCip0~jzW90cfcFd8J%9$ zPJ#cMRBgBN(tP7OJvU3SrvE1BeTrcS$ySZ=Jo1mZm2A8O4s!O5pd=KKhOjr8=SKLw z!QFZtT`NL#Wfq-An5xdGu;+~vCvyRPO&(!w1_*`{*>fT=kg))DkYZ*j7QskKV<#Cr1S;}AH{`zLN zIk*=7Il#Prkw7?UkUH(n*$|u1?jvgrqhSqj1jcA1{GxWC{#^-B#+X35+*R!7{5Bui ztC@VnktESSj4IvDw$o*mB|TfpN=hx71c!v=5Aokz0{m)Z^a3YYy^qNl&Hs*2Y=cJV zhzEA6SCF6oyzmMCWkCb~^uOVm_ovm9<^bGiz8#RE0wI&u`ngVN3%f^MOjDZ!9*71n zu6rFYnZBSjqhZ3L;*_yGUjOh^`XvdBBhUm@jFU+L zjTSM#B3C9PQ8Jv?^upe3f`?=xB3ePqAmY9qAR!kTa;CKF$J_S09 zw^B$scs;d9eDw>pkj~BsXMDDl=j8mI1XA?yf~0u6uGF-xZv-!@NqBvZ{E!37P)4BO2q)Xt8X?t9{a)9kiw+6?9F%Qpr;6$VB3h z03MuW2)}ZbJh!js`iIDI>GEi|cv9aFP8Z@lLF>tc1Z}eU2u7Q=wj_y-!JqgF>GLwi zd*l-{y^yPGUCM25j(8I7VeS0_58j32zqCRwr>aS+VG_JvcDTCAnq5`zUCa#A%ba~` zWAs^@W|?Zq)^oJ%L0ehKDldhjFhe2BUHOiFq6u!#THU*`VUXlDJLbD`ZYbu6=9`Rt zLy}Eg#c)+vdAIrxOdS?hc?;ux=*A{5KeCP%We_s>zJVwd0HN~2DW1znqqJhjCvW4( z*q5O~0u`hOc@%dQJg$|_J`+(}bjoJKB@V7SN%^)I4WIsW`TZSZT@s^gQ`BR zQh)|sEUS64$o`{xjc$JA{IlgX=;&GHhq(V7foW+!(vWE+sUPiHX_UynNSJZy?Yw_e z(ohgYq5*=}4`E(=J&@sVLQa)GpQPZD!6V--iAD;E9yHy-kVTBUC$-{b(2OaVb8O`? zR`;G2_pt9#h$&HnjStA{F&wc%Vq(DbfT>zcrzy(S+z+IX6ZJo@(#lR-dXmJ?Zt^sx z`b+WUOJ%3TWqrGUtDpu5;iIto3vucXn9RTDX4WKikx5EQ&b!Vm_{1K6D+&c9+d^ChxX`LT7Sk%LAg%@X{lkW zJ)*(55mh;)6|YQn80tsYDW?DQ?a2vhF{fak!!mI-fv*-8*C-dEnK1w4VA2L{jQHm? z*e{gaGCp@Bl8{otee6qC##Rr>xI2tgtY^3uk39a>$f8so))dc((-JUyyi&VRBi~q7 ztC5gkm(A7%q6=rj)RxLRDPugzP`@#ZCQeuO2fFSXf75%Z=`pkZxZJek<85kuf2wDyrAtRIpsrsH&NEP$`|pIQcbi_rBB z7xY~F_5uICrB_Jb?_t0GfZQ-@CkkO|VrlV;deBwMThCz#wH-9lBxa07tG_n^*0u}F z;_H3cgMuga^TH9e*77T^kRfB!{CgqhJK(ko5oeD+r^9*2sfBQM8W~OFw&7G1{k-}`JEllz@?W3@+Yg4`PUxw&GW&$5%b25( z^MIq;x-t(hcaUZ4uDv1f`H}zhqWxb@X)4kLoxCDig+9Iutppcb|I%36*h?JMNPm%g zoJH*}6tt~ZQ~u2-j%D2fQ|U59H_a`$53ZBLjRxDT&$rB-&myl-6p{Q@m<-8J5Ir#p zJ`N(oS&nJ*I!fl+j*TR|)yLW8RFuz96i{^SN*=P*iDDal#Tipp+ma8M3S*3JYpA_l z3|EDCih7zJfrzx#F9Hpi_4UzU^xaY2@^V(Gu^4DS{J2wGM*DzfnJyQcw2@d!viz(P z?r(4eKws9|?2>TjD7^I*Ep0s`!syhGrZsy7wSXv3+v3GV*z%e5syytI_a~Zt>e#)f z`v7a(7hr8C+i?~j&QFH&2>aH~-%X^~%x#Md@yN-1dT%IdlCs|K>pXK?s~26~3vfAX2X+&y#22OND7|Gmvnj2a^) z9i2W#LsU~XA&So!q2K?T@C;nk|9GlHK%+mZDS#@2NSF%{wcXEq z1>CQDk@z3Zqlf#qp1{^+u+f{uv*XSP_nQzMm-7%EVxZGFBm(w+#$^Hi`-k2%_w&9^ z9p`g|h|#TgkS^rQUE*+muGj6kPXu@^TEOG9nn6GmKsGgcw$(L()>sTfUlhFqhkPdO?b zc+lo)MEt9Fg==pU*|b?fYo?qz56)M=POB%L*&IQP3pfbCCq82M;OFa@kZ;7RHbiue z9LNuc{QcL>PYyX?qPUC8`GCT88l8k=xcmK765{T|*#$0+ATcm}RW`lo#J(+ccxdw` zDpBk=ctPA4>LLnfdZ79)`|9OUS9Y5o>^ugcJ3N#%aIsO*tDmm*@t2e1G``nzSa?AU z>%n0zgDS%H-uz*HH&wwhfGlf3jhNV{2RAz_$5(y zEprt^CQS)oW!RLXrqK~jBGI)4r7aaNrcs(&Rt0ky=P9QG+DByBEO1+jvbKM7Dwh$p z4{0g6hi|17N2f_tKdn62iJrzO!ROPDBaduY#O@L+&aJ!dJhL3B(D?DB--*#5Lz$l_ zin4f%awp>B0ow(tYa6wj6GE@ZalLg5r2BaZshcxfg{ta8v@^3{6w?>Nx$IN6*;HK$ z4c~La*N=PbXUqdl8c?A&oYmhT>|}JOl9a;95zl6d5>HTGSv74y6Mg|!c z^QUp@Wcy%Y7qYh5Cr=B`9}R@1nhXMdqrl;3pfz}egOi8ZF`Imf&-pRP z{*t^vaynKlVEML}AAO#*Sd(6Nk@HGWGyk|+Z^Gpz!2Oac>y?7ptr4@j8@45x=zICT z(UOrcMDfT5*|4bAuZzrIJ=L>gY*Y^6s_CwUjBd8Wp)l6WD_ddPre+owG$xbx5c}f9 z17h4Ghf*Rm1WO@L@=+&PrJy$iG5eV`srp~*J!OAUQxhR?_CdGR{i#owr^;5A6d}3| z0OBr%YKfKti{it%10Pag@2gIHsF`jjRCfv05REx=^mLHH_o)2*OE-#y>wA!1KL=-8 z)ju#xmM#IGGDQK;)Ly!ocz``AQ9v-&SCn~0ChylEH>6l+v)Kg9bu3r?C_i%!vWKH6 zCb_|svI{{+Jw0K*(UlsJ(Z>LOQ{|-G1}k#_Xc0v#gaXqhO;Z+dmp}HN7mp~I9I(Gd)%V-$Ti_Xas$yJKfYD}{8XKk4O)q>?NMq+5X-nB*T)xhQSRmIXrlymF!wC$*EGDzY`?xYMi+7hCnc#6 zSxO_Wq@%l(XDtTz;4?D`($%CVfryOK$ywhen96dzwLd|^JhNN_CdNL!capP?LLM?C>uEuA6N6v~Z zr6>zXy_#Nu%tE5&Ry;*V|KgHFrTy&g^dw&=rx)QPi>+|(5{$GX$qF%%sHRelq|upL z)n3`^5etZAML`zN)`5MsBBhJ_U*zJ-_wme{qxCYj7Y?#y+I)Jw+>h^C-FXAyQryD- zWAFb(f&1zW1kV@}cc8I{UdH&pbOzyt6s&+0Y)JkOaPc20*o*1e53*FzuaT6J8k)G4 zJ-rvZ4U>S<0;sxbTc{e?Phqq+IX?5ufqXi~ zOqcU029N6#*cU@Dx++$oLQ$WL_W>A1Ix>lc;&Ex|k*D7JyW(K^5~y)~vedKbN;Miq z*(3L&x?vc6=|cIW>P3YYUugDlC$R!3%awDUV>Iz@Z;gVS3^GQS<18s zx!A)c#vFKsd9I9Cr|Pc0ex71Om}3t4c7NE>Qy|p&=fT3mCZ@n;DOlSV=HYg_=dgY_ zoV8VC)ah~PeGGJ55zlR>^%=>A;5eVU)x&Nt+xiV~RH(lY+~>P!FEylChh-h{=Zm8d z6AiW_-WM(ed|S#e>$c~K;O}k8?C2e_uWjk}#Sx@`jJw}o9$}>ar3sG9c_Dh$zI4w= z;~RI$(CT_Px5S~=Y`I^|(SH~XOLu-wV4XQTFhaoWvQ4MLuLt^}qFooL&Yf7hLP}(o zg9%$4-bZ)tKJ^}9<{@X}Nbc}HPX1G%AHn4TB1LQS``wtc+Tjh4$4?@@u}w-E~)L2Y-*~fgaHufWH{I?t8sFj|m7cNu3draSr**?=bAi zywIUOKO z9Hw+FXaW3wi=qH(o5)(qs~#vBYIaHOW`P8aG=G3KMhCao|Dbj&^1@aE32R0_K8OHTBs zudX^CFrL;Ml+6+}u+ADc81V1TTJMsF#>pglbnKl#Is|idmoFk-pN4JdEz;Ygk>iks_d{*UqpCQZX9ZU2-y)#dx_>6f>8pFZ-0ruQA`Zcjj;g=d@Av7b z-=sVl#K-{O4&z*c_oFHnLQ27dJWC#)c>hRs@9+)bV3$k!q^k+0KbjTv^|^B#Z4Zz3 zZ!9?{tVy4>-m9OXou!nk8RlZ9v21yo!@KN7jtP-R)!TQ1r!=;#65XYY2vP8Rn$r8_e=H^vMgVwC_mbu6PJrjyG>|K3<-OUaBqf8YZq+9(9Cb^`&Ha2&a7( zLBZ*^(Yw##rsD*iDWbU)hxmDf3_o?h9<}^uZ2fo8RYijV_!45M(bX!c7{i&3PtT-~ z)nu4cz^DxgqFOdnoaMGFj?ff%EUoaiSxZT$bnD@@V@RmA5CLMJL>$vUpq>t@Y%qu2 zTM>G6FGvYEA)!W~49{r?kL;U6EDhADjN=dbc);8MD@X!E4xWEOs0pZ|Dxw@ z?`+n>(l~CImN5wGly9V?4WaCN(N}&F@?xChV}YVW))_m1Ou(eIF!9Hoy~!z06I_PO zZ>gE;I|Ze$iuuIYcTm&#F8m!Bqazn5E!@zuIrWbnQ)dqjlG^>CqOViBX`WKFZhtM# z-b;(RE=8W-^t3kI7A+-U5TZ!p;ik36-0PTSJ?xj^Bw6yw8D$L%P@}`_%B6; z?^@g%jeCoCQj_+grU(VQCUDn~R4fInSGy+6^DK^WTt|^lIV~bsYwhwL^DV%AvzGK~ z7N#w2y59KVTd3~OgD9ukAg9drTAJ?vv8QMpHtM#2zKzU~-r&LFakHZwzMag@pCOYq)yOx2AVD6VFBi&++POyzf5pjw!#b zu9BFEW6k_YUY{2qs6KUZn*O@ste9=!)gEw;zGc(!bBqWVPaf6DAt|Elch1SrPpPfR zzqw_}50NOlev|Y4hWCbQVWuWArd0Mmz2aa^nw5b#qx5DKG2g6$n8FJts>wM|{aq)$ z?|U?5QQ$-p@Gd{XK~A#LU9EvRVIoWUn{#thTy3#^tt%##yhtHAhJfOpDcc)02Ez}7 zk8c7ei2lI>e!}BIlOz9`H{Q%+Vg;3pTnqS9Bi}!!4bVj?W|QVhZr%Z5Tt={n^oZ_q znMMvO@zL?xaqc$g1bN~drC&X9R*HE{a6c zL~mLTj|@;A$zz$AoF#kOwUmKhq2*NW#7hqaU+26NWnBy;@@ZesdJHrtD z`aDLh00(0j)vc!~*Fr^t2M0TKn8A1CA~=vRSP0QatNI}ORL?vsEa8X12E6}u(EfEY zTW?ZYzmk*^OjPLo{E3J~Ir3e7)=ruJ52srOf70r{Hx{T*W!t#u$q-Y#<@(1|;xm16tn=?AR%kq)ijyn*XCqOw`+yo1Pj;L{cC9 z42^r(fdHN9oEZzL#fOVOZ(Z3Y)f5PME2544(KLRJp&`~6=JXRu_}(N{0xhUSqDxsSSZ!sCSm8iu@!m*bl_B;)0l_MAdBMAiH&#RArZIb143B7E&_b3%$e5@7 zH3XhoGFA$*9<9L}=iJeCi31V#!9;dx)q#6_9 z#}^j2n05TSJJws7Pza|<>$6VpN7KfIrzfOHA9S0*XPUTNkCAvTh19?@@$wKq%zJ3r*?;@ zZkfMjr^4@z1hS9@ z?TX4miKIV7@#)NJOhBRLNV2M?5nRGA>YcEa;zf=^wR$Y~eMW)8?@~TjPqS+PA&VC8 z&$PJ%->F+!3=%y1q*^w~<&>P4b(PI?YlV9}C`$bgAIrZno{ zsyv^+KRhQ&DC1!aY6IFJYy5}Rc>bF)3a{(6fC%1uzktVmz3XGhwztf`Wxq)R(8w5g zPbod#66;pzo^U>q_KK3)rv_Ci;-uzwN_BRn(P2w&cAlctDh>eB-e$#vSqD_FvM z6OUwulVxt!Fov7jmgj&=jaerJVWO7Xut6QNrCvJPA>C|8cwHYy5V+s&P^5K!db;IT zZ1#QM0th_U2X*cTq*V;(YnR|}cGm`aBTM~2AK(AlJof>DDnE&xE~!Hg3UD57*p!3-7)&+fqAh5pzP@1TIHx*9ZO&Fe0snHeZvwNso)7LFo&7TcL-n>iHt#|6|uE2<% zVt5}_pZjF|byyDkGUS!O^$)QS<>8;+v(vi!UReG~-D#;{^FM0`FD?p!iWy$cUdZ7fzu%(gJl=}efO(@^N={$Lv` zjX7E7#`&i5&7ZMC*0L#QP3dj8RXm+uZN(dhJ_j%Qb9}OP)!(`pB~4 z##*SL031>HEpMaWkm68JqE@)dMl@ZmsS$0n;b$S+>`}74;v?dlJtQMPkZ!#z@fv7@ zzGpx#Y<7~Gj+J8EGpmiBoe%G)%19$X8mh{q>Oa>gFsv4VP3Tvi>_1dg#*+fy;=M;K zx{_fP`s48w?ft}pIVpeH+jF#sjGc_WYSGw0uZ>w~kcuqmf9n>4{_51idwt@u$es&eI@udF0knoZd=GqG=%3t{)`-gY&n9S!iQ+7X z#|bDHzGP8*vJU)wE4JgE$_*lsR9Qd_btj04qimtuzlQJ_SMJ!jLrUCHUN_~{-Cv~) z`S`~Y%uzL&z7xPEGB5(6q!Epx5!Xxgg^jqy|Dp#-o6dgYFY~14U_NRv?H29pj@DIQ zC=z-R+^ia_ax2AiiPBeeXwbNI8L{EIeEt-eY4=|4*^+VY*`5hMlLLmZX z#>n<${jl$B2&n1hn3xubOoX^vPe(MKQ*_NX)4`YETikNV;pwlq-19N1$YjT2|3=B3 z9u?dpo*$vNXudoVBSTYR_UCA}B9cwLhCP4C202NoAn-i7*|wcQg&u9(Z2!1FEs?{g zBDm=M>jwJ3PKe>BRC6k6h@UfeX;jtL{<@~+RzgbZ^um&XMw!nJ`9o^eML;;5OU8-t zfS?uE6hJ(vZrq-gNFwaOsm13YILwFF=W**!&t!PxMZNbAqSKsuXEx&=-bC!{f22+g*f_%(wVSMe zL>u_|W@;+hk<~*BErAua6Jyr-lkjFbNs+`rRXG9Gukk!UE_}3Ffr#tKn_^%VE%}q4 z`L%p7FfeN|SSkeNiR~3&NlcG)F$8(8!aCr1qj-K7Z9F zH`hK?M4&pdu1kJ!OwL*Tc84j|X{k0#gk^3FR3&TY{T=q~aZBPa+|i%%cQo)F!WXQX zyAoQ|GleH3wgBk8lZ>HJrDz${Fc(L24)-@)UggffJMo!!!*WA~4YAAtok6Fv*ffY& zxC>OxYPGFiYRu_;$6~a{4NUeY$9fcpdvd0`bnTvX)KKyVHaKYM`V8F~O@C~KP#8VV zlV42PtS1h{J$`Fq>7RoQ*tPX_5y3+1jc)2AM#jmT^lWXnl~W5Xi6k@6o+U+jt*SNA zbrMV)-A%z2==yba(tMpoLogguL#4urK{^L(4S+*$6tS7cs~Lo_ZQ{bFoi?N8Ls|=a zDlkIEVsU#|VE)uKJ|8n0L-|?bRgvn`u&|^(b*WhDH*A(eU(R3imG^aYdxOANeF?c6 zveT~p>3_v{vWdJi0KLenwlz1;yeH}+77^?>vCp4%PtgJFu7tttHlV1+pHxDK7HP~R zY%u--c8KQbWT|2BQq^M%OVQZP;V=5-Wkj=c)|pUBO0{;1xZe9t_g#nbVl|=0t$uPx zFDYzoW@sEWO(6fs0e1Kv^POF2sO5wAj#^NNIHSo09=EVlN$BoYA;(LMkzv+K!;OuK z*i>w`ug=)+f|2gVo&W`W6P~&eQwLk-ay9YgpOS_C9P?7pR-C9}zAfW(iGx@)Q9qgQ zZ1&H-i-2Ik?n^XV^sDa3p{Wrjq^M9YFTtRg3!mOWXSJ?i0qKD>c^(Ot0sSL!m#sZC1Hi%UQA@w z3R+4*Hj2}Cra4)q9k@juA(t8P9@sN+R(J;#(h*U zCS@W?-9d3(Fj=?X2BlrUV+ZVet?V>y%6(^~@>1N+kJe9drHSDmm>>t3$?O$%X!Y7g z(%Y(sHI=HODEseki+|Bamdv`Lb?S4T((X#m%q-}+YPqi^TwhQ1JyYUkr=A^lohYAy z&uWlGE$dX(Ih}W;v0?zs7D+MHzQvz?;KBsh?h@!-;i?yKR!J~{2Ktyk+|eFwrCE^v ztUcDZzs=C>aKoY~5Ogrzwu3~1=ilO+5eeym@Y@v|_6Lnmuzjaj*N3n#NfJ|EBcq|M zQp%K#2aQHUD2hhNOt__RQP;8cmazayefn!YduvUN#Eol}-p3&vO3wJ;I#$JGg?@|A zoBbI`y6Fd{6n393@I{q#G4uB%5IkM*EHMI4B;_P$K1!`IhpOuAQsu{NgueAMXTb`0 zzaJ3F{JTMzgoW9gZpd{G=cy`>hK1V3OoU0#*j$= zgCgY+8ykB&dmfRq#2!{^%w_nf@tVv=#p|W^338ol8qJVEkq2*-Gcx1-X^#%YN>YFM z@-Nf^<4BS?KsHi|_V2K!?cupSm+e5J_BvST3iI^K3@gXy8AHUpyW0Qx+~f03Fm0zd|Fka|NSU!FmJXa!rQB6v#>9ctj4r>5yj6#u!dC@?$tz2iVhn*y$r$vVMYc46}{PhC(DVAcnaCPxpS zQX|Vv2QAg99A2Tj$1HU7S^!*B)1D2#Y&x6TbGHhRp>+b2>cU;Pd$PSemEO>Mgm*aE zS^iTcc_LNXpGcQ-5DO7L3p;RWWV64`m#}l&{?Gh;8Vnh@%K?nQr0Qt2Uz$R&ITQlge^lNafu#i&xI#w0G_oPu%P=qlN1-^@LGjLELNv*In_S$cs8gjF#{` z;iQRo(9alg{*XlyAd*oSD^iAHJdZ`1Y=9C0&S3`9pg41Vl;kaz$B4EA^d< zmenp${5Q~yc;uB8xXJ)7#br6tFI7>xuG|@;eWj4k+Tq8= zp^Ulz@N;yYBFev&$dIpf)A$DLGV93|5+st2kfSTRc4X{QrpE>HsEWeJ;?PF5Z)B!( z%P!PwBYhC!{zd}Nr7XR3>GizWTROz|Jy|ODq7fvIgI2SVYR4-g)J6T!w-_U(*_Al6 zT#S3ZM*Q~i7e$z)L6Ryku)f5ni-2M@|8u6`2e2_DO^QO)!3{hHdb}A=i0KBFz@byG zcoS(gIEju>h6x`exgnlt*WUfhB{AT<*Mw<7SsbEhbs>4Oax-dUePhRgsk zYJ>!_{l6p^{YCU7U>v=Kc!YQyhyflDl})Wd0=3OIlS#SW^no`)G2iUTg4qf_jw@>N zO<)M-Gb)^!i50~(#gX~V=&cZOO~h-?Ak4JW`UWI~FbnRR6zM&aG%SEas;-g@A-hmv zy`;3FT_YLS$0?vncO8&CNsN#nk@#VV9kx}IY=eZ|Q91Osqd*r35ITLO1hB)2h?GxI zhv^kT#L$d$5JP}xk0&cf0^s#bfFRV7spA~OpZio$=7-&7`5(q|3Ea;K((ZSnOFeFw zo|7kE#|B6aTLDb`3M8U%*Yux}Pc*UtAogzGry?>EAZzU;IF!*5aR_wnlx{<8O07Vw zey{w<`lHl8Mk`1pgh(1|nhPYtx$EtxQ=n7f(<|=*d#gT^MI86mG`}_sV6NVuI~`%@ z$}F4(STeJ=EA~3|md>Z+y345$!0MVoxM$8|D$f}C0K3tCzLiV?@K^pA_niUN3D1Ai=L;butpKn4<*0yGD3PaX ziiBYtTFqFet{-xhm!BEFX-9!Y zT?Rtqx4i`ZNt2Hjd6Er6F?RE^ZV?sq1vWaX{~^#g!BlVOjO0N>4Qao9_-BpW#*Se@45QBUfGBl*@p7VX=@)|bN{myYq0HAawh>QA=VW=H;u+D5R1QsZOgAz~|kP5#~72Z4Kb zEP_YJlTU~Q0(a4mr+rnd*pNTwd2?@nv=z){zX`FhQ-bC-f`V(?a*cVgwAQ(Od^nHo zQNeh&w%mEC^oVln^EsqN81h?;G>mTO&)e_%!gw%phMYxbbzblRgF(fy17v zNN+z9`e%ZMHt$gMN2f#e)8w_s1CFKJ+$QUC=&HH($@;Xzn=Ph>lZ9e|rDH7_CabN~ z&h;Z2Ty)2K0dUg6D=meS=%c-#t8N|cNo|_yn?4I-1i%F6~uLihQAf*VZ%|hfZ z;RQt!q4V9w#FVE)K6|3&Qc=H4%eSU+mAek!8}kB_`^fwAVm7&`{(yqlGBorDvMP@9 zE_uH9f=YK;jMg#-ZP=9zF7dGA!^6rB_~N8%dM96{Qr;6371KX_wp^m9Csx{qk>Rpi z$bELM{_Y-RIRwDR>`tjWQAsJj!V*ek^;u^Ziqx24PTOt!P&vp@+~mivOwKpMe5xOh zQ(D2LURNB6D{@#5VK_XG!-i7=58nm}1bD!E)_ddVbXEn)_jS8zo6PVz+tb!Y*`gs^ z^QDUq?Gc3u!o;!)!>975oAcH!)|hhVmuQM7X9bL0o7-up&I$q#0rpFa7vJ;{5{ZRs zz)ityu}7~_iASbguCBUwaEN>HTF&phY!}6sjyvL?K)XZOE+y0~EP<+c^vK|!hM#-y zF;8cak#Tr;n{n3R2Fq;R^LO0Oy zYDWo8ptF~%A51!@Suw@e-|!bY%_Y1ra3&=&K1*&HBjMW0PK$4{B{}hPTOeY*fV9Lx zjpuz25s&xB0{qu)*Nwl>G;q;tn1H$K^=4_Kmw~$Y^l2-GnN)&7_v+Ha<*(&{RvN7bbkrv-H z8LwfL%ZvIohQF1mqdV_CgEcEs;MW@+Yc_%URcnGLnNeNZUuw}*9E;a3O1H0?ojQX@ zfqa29Oi`9{%Gm7tG!(=x!L+WQ3kTrK{SvNNR3m&3gI}zkO*ObRx#wX?xY^G**yhY4 zQe!nZ^XRCjmy>|$?FarbWx9r=v6m{DvoPuyg-N@dlE)ZR4pkmk#^B(ieml`sgbI`_ ztZ7}E(JGk(a3Cuo*n0$>Ao3!|WvR0FwWdL)!`iCL>FA(7HtD8ygc<*msU0bikIXTW6mER<+NqAKw_VY?TrEvFjkm31jw zOW@7g7IXF0TF>h5{uMuBXJAO$gRXZhvU)gG96Xx-y-t>~|BJP-JB#F}fF;fk z4}+oZ{m)35*05%py(7;FuzH2qTI-Z;Q6xguAJo&T9`ol>sNRxuv1##XQh($4t8#&Z zbye#Q7$%gv@F%{Fs<|87NDFSMoRr*3vIn+}5Yd{gTCd9?ISFkK$UlUoHJMER^O* zuv8b9eN@f+xTdv~4m0$SOmIJu<0G(}(-?REftN2a?)c{0a9YJY_jzWRbUeIA%_>Dm zm+Ym^=e0=W^5o7}=5CAO+~}RkPRcGSD~#8+x*Zd6XoJx#cbBeZ&hN61Ec$llT#lQE9`}af zQ^$eD{sk*6zH{)!Y4@+OC?j&hezMrDcyQLHTQycv$}DLqQ!;!sof*x?ws3XL)oC7Aa>aTaIl9v4dh7vlTSL=--AO1%t&m(Ax0I z%3i4UC{9S4vp*!YlSsBIg39u9nY^OUlu0Gdl}7rGUJ|QZ=Tqswe|-|LZ=yuAql%1d zDE<)Vm^K|6G0trLL#~$N44pV+=-~~pgqKm8h%@oI#2$`n5WVEQjSedX@@I>dE>h+T zyg;?xs(N0Hh29Td;g^gqxef0_Tqn1oN-awpS~5K=oMUw-1=VK-9*UPo0-5!+ zVE~eGnmKbTSe|QNaqC&s;X135&FhLbS;Aqs3EFTvlg28y)M+ckOWs3fl8F*DjGkx1 zm1mhKmF#72vk1U@GILFlLX!Vr0rF=o`HjuPMqsKCKhc#9&sCY#!{%Xm;DGL6$3I4bWr&x&Q}l& z{K(nd6UIm(>1NvS9F|pKc}!|NPt^_fXA_DUS)7#?mrrG;UEdP_Eb~)eg=b7g+0YOC?uph9Bx*GWOig5l_MG_qTJRF+gKYR+Fb&#y3_XuJHyo_8CH!~tKZ4C0V6P^kTH4Pm2YLooe>t*2uG8! zY3a7>D_q7;AQbmWEPFx&7w7HC5y{yR!F_A%qNDoF(Y{ilehN(I$+ zb1s*5e-a(Rf8FWT&?}06C)tqs1MYn^@}6jvdSDN zFf_NgfQK)ZYU9aM_06#q;VeZH>3xu(GY^`)24v-2?gJ zkJ+_8!>jR@TnbVMU9jERl#q67v@0gN6nW9YXv6djxG=xo=YX)7+P=2}gol?&*{EE+ zhx_^SiZX)?CoeGx^5Nyi*kc39zU{aEMLG7+_pKK-S3k%Zikv{sjVJN-JtcFR`|U~V zgXew-AbvUN6-@7jtP-f}uLUgU?K=?!Ucv<5@LjGEj00ri=>HGS-a0I*E_@fp7XzgP z6r?2t2|>EMTj?I8yE|37JER-wZWy||o00Awa)9|Z`uhIPb)9p*KhFAxxtKkBuf5_~ z&vV~Ttby-jdsT>4c>UZz#0tl2e?Myit=v*MLz}VLN$_^svdBSG%aj?Bg(B*0OB^euC zYVA!4+yicl4wHKQ14rWBs~@n}&Onu^jwQGLD=&&GKDc8vuM&_)`w!~^E*m|9H=V_Y z!=waUFYg&ErylaU+&+|#dIP^tgI{#fbHDgw;psGuQog?-5J}4g45+U*0E))_{>mo$ zdSA}#=FT5}f2HRP`EA;L#Mrib<&k(Z=swi{j3)q4BBcRKjL3eQto@nS?Fe8Mk3@?7 zHX3zwN4MVJFpH$+zXbzAU%UGs}&%$e=BpW&TYAMfOO-2%%J^Y>*RKZCD&adwzqf4c&Hx3==-A0X(+P4^c)pp{Ofy=?sL zm}<>7nwKvpBKw15pK9+gUL#TK_CAa2VfT;l!b1RJ`ZfBH%l4QU7&@Cp^4nAT(CfJ2 zPj@M-TY^{=Bd?Fw(2)>yum!6XZQ!!;bO8Q({5A8Bd5Z8=!f@F8TzNlB?w%h=zw-mI zeOLa=WP* zZ#KYU=;z|hN7j;wZHGm5n<=OnK!%6t(V;(L!V6&hCGc48k7J_3koR@xl!xQqCfd}C zi<+R))P#CbMhpCjC)r+}(sycK``9N2GMYIr!H*09a9N@~EcZL1TE?y{t4oMJ9%s*b zyWVd!mifc<17SU4NpksJjr%MQ|35(PBL@(Ri>kG!6fy$vEAuVMrn}Arsj1D> z=ltGzH4jIBXLx`MUcJjPeDThg=qO|`#LQ$JmOos<7QNFwF15&1d zLsLn|%yytIb50nIU2vd+2ZVjqSXt*6!OG~Ir*UXakrm^|KE1J@{#|J+5OFS6cBme_ zwkaWHtx`#*W)gS@6u6I?8pw9+i7Q(ZkIH6xA3IJ)g9Ck)z{cMUB`=B5(q+Xnhx*mj zXv&6CCxA`Wi$u7uNl6clbyMJ6<%exiRgLkqt#%Pv z=D-{^Dt%-_=?@jmOP$S!R7!pFUZw6{hqyq*EjqdQpBiT$r?}JQG=Ikn2hOGe@l_VV zL78jnu=>glRjUCYUr9+)6~;<*1sM=ov>`w|=}8yqNF=!vuY_V)J`*8**jxb=${ObS z9GnMAsf_7S{eEvXP566~Ip(eTul6PozN}-@=mR6)C5s|8qbh70Uvt z@WPVB43306_~ltl6duuvE>J`(65yIM{K;q65N8ZnUoEE?FmU~_8Qetwbu=*{Xlrb8 z4sc7BdxiZM9Pj!?sjffmU=8nAHm@f3TUbYwsAFh!R)qtJdscg&tnY22>Zq)E__J3~ zMaPr{01+Jzaa$9qA%da0l~{`olBzH5q64J@Sr5Z)NhE*g+3A6RDs_3NPLB;gu86Xg zGA~a2@Ztlg`^AyRSbZV4e=3PZKS>2w1qnHM$(h>IKChnjEBP30JIJK3(K+QmOWHT^ zm2i;l0af;lT6cZf!pNp%t_7gVVl^N4f%=_5MAj}-h-%cdL@E`K$;Q-ii;r>;Hv0-6 z7YM}u06{i%2u@}ULN$*Z*4+S7$i&TJ+OD(Cp1rQroVZufcZ#|qf3|`_7q`v603#}S z^4mkeSn5}Uw10bTr2uAaDq9GPXro6D(=Ja)(Z;xlW?hN6g*SVMg8<;8%ZKLqdYx>} z8iS>^2W6god;>FHh)f4JT{FPhHHf)sZMRgdJX4glY2G3&BlYG-3lMwlu|**S1c4Co z&)OjX2+%{zCAwK;F&aj^{MCiBWH(9f`KJO_d-nbp3Im`5)+Aln=Pa#+F&dx(*3PG_ z)kF45nw0)LMJgg;0W1RHReASeG;}3%)#%getM2R&bbx!05GeJKQ&ptloW!*87pog( zAgNB#yPzFk5M0%MKKoJNYsRRfV@1XX%2n4sAcedXFYE}29BP;UzW@D@dUWNq>KfXf zX9{y&n64?@Tg8#TBQ<|0)O8-kUtAq&@@skc7WV9agpLbR*9vvy##5=)e}K}!R`akp z=EKyt1~=~mUN4kN<}rZ2I)8Bta}kXhzkU9WG^aO%l!FrK#9!h%I#8^lyZU21Dn|!R zFR#hZ;e$HV_TazruDX3v)^x`lbe0pvG}&y&>S(HXWY0hB;cX0 zyPX5%5Lco%kdi6EjqU7+24K0YFK@%`&Jk<&iEpbd<@Q2L^T5zr_!LalX)h2Ne(_C4~if1KRdmBlnMFe{WG*jt15EH}4`C~d+Ky~-xdBcB{9-u1(G#3RnSYnRPpCH!_Wkht@WZ)mjOeZRKNjH47dY)`Gy%IUtC)$Jpd zx-E~rU-OoDOhfmg^T&NKQ8`@CF3dqb>XEFROZNfk%>=r8^>Cw zK3#YBY7Cb&LUA3$V0d$wn*||xpqYtBFC~|wiBafj#f|wf8G{cY_mBaR8+YgK$B_fztN&)s&UE?*Y2 zFZXxQixz7Z$r+Rib&sIsuMcSZ(K@eYY&)e*SgkFe%MTB)ac-uO;qay25V#c+V(Y>q zCTmMOyJIl4Z55C)#Eiunj-);4BAW@~2&_(8x%CBZ_usYmQRmX=%B^`Qzl)kf4)EUB zet}#Y9!RZiZgug_zfP>)Gha@tOWV{4e>Sp_{tZ0merc;>_7Ms)Mt4lc%*D`pcq-P- z{AgSaI{(=CJ=ObgecREpqAVvl%dUGabDSHCk=K@bc@afCWzo!H_#S6)&1BcTf1dM; zeF7;8CyG0~;Rt3h*j`BVGc8s*yEl`G4oL36>2%^b#M{uW-ot0Sdc-?crbe$pv)fI{ znG@>a$<&J6IQm&y;}Xl2`TZXfk@4=AA)!&PMr4 z$Aq8Gi)>wOY6!eXOi`B_?7dRja3QwX*!^H+Btw8R;7d!A^$L}CS>B;%&&z&CK$5Qz ze{oS4*R$!((CF+Nml7enA3Y}W(WxZ9Z~bYcQ6UCRD%-Ok={8SsEn#$7=Aj;XXf{Qp z_?wMdM>$8B8tUfBKu22B)VGptc6JY7+x5abpb{UTOUk#8MGrsA**El9l}-NuU+=!` zN6?JD89__7Bi<7&nDvs-e@Cv1PDocdN`)+88m0`Y>w?=r`Y#jJXgefr#l;8LPMg+R zhXm{UwXld=f<>uukCZ}OHJvWDo%Y3h{c#~FbJZj%dnU2bC!uADfufX}O{cSpHACq5 zmXY#rEV=RGj;v7!(H1KHVgZKbaAr>RxB)bNmpbC#x__BjXNMJ@c#jZz9 zmC(G-U)O)o{di*Cc)$PF zk(t#nxy!mqvq><%>2l@0e_pWew@BNPcc{&@?U9#`St@`{@w<6{ditvXWwU{HYU0)A zrW%%8rTtpRB{*|{g>6AKBe$$-1siTd8(`Dol@Oe%>KFQnDX1!;1CnC5v%=3+8o^Mj zlqW!Hl!&_t49L?;71zDPLW3C|*d(!YUsgTVY$p(5qO+3(@uFxB7C) zNii9ye5lc35ybV6!skA=GEZk{a5;V3=O7>rYx-kwjL!WYPp`9j#$EoT>!nK7HXL{Vp;?4F#5UUx`S;7JtMS`LyRt))SzNqo0Pa zHWvJh%aMcA@Z<02x2EAz8z899o*eA><9nmS%Pf&}H z-g19BPb(gF3Uih&UVF*c$eP5Y!2vq|rC~Yu4vjerSCWrfaS80A52}p_gdVssUX_S# zQ&kRC97-iy4Jz<5fIL|Gr4q9PL$k^DF?f=IXQxNArx4#Aerw@9S=CqBIMqAa3tzTy zuAADr!`LL59=6Q#z$hdvr}+dIzSWaSYwAnASqdiR3W66)K+$weCd`0PLbM~0SUe~9UP z(zO+6)gJd|!k#*GcLdO(_t=Uxlh3`cN@j#)kdfvoVoXx_eT3xrlBBC%BsoL(gO+@e z$AfwhMhX6k);}W|H~P9uqAgouF)Ot!Qc{!4wH_jgkbVa&tl!^%(km!{63(V+5*kQ| z-yL?rDI$M35k!69t|E7MJ@eUJ_0;*ywTN?mF|2FbD(6=Ft`sNhJTfkUlb_pB;jnPx ziyhWOj;T-Z@*2kim+^-S0Cpt5xSq>YaQ0RO>$kcIi2BJ zeIHsv3xUD^=?l|SmkzBgFOKU?&^LL zrMGH%&3aNPM6I%;W=Yxf^H@47>r}h`{HM3W{LLJW-2{PcvJpXt5c?p55aH09BpJuP1_H?;1hJc=5>93no(uC_3892z8~jHu>VS0S zqdq~}qnQ13CCUjD{GPtoAIP|YEw(J(l{<|l#Sxm^)D=1wQ!QR-uCSiS&|kV{?!RB( zJ(N*J`3(7#0aMS`fVQepFu&gTa*k~M0&97awDzt9NLFf2V`px}H|MGod zxRry0)h;k_NPh<|^rMnWw4oj0sY;Xu_km}o?d-OHG4IBAbY8{u=g1^ZcJxZ3$Tdna z?Nej#O0rv4oZYWCnay6k?#jKQQ*^2l_z|KBxgWpc>RFase?~x&Cwu#AIc+3QmiPwx zOsr-iLFZ8WEa$RGX8M@}OmYAgIu}jc?z9;$g3jA}|c8K1ugr zKX?48Le38>{NKf!irqc>vxh#D$FlS^EjH~SQ&LizWMJ7;SW;R2N>B7F&>n8iscm-U zWEGZ7`pKsvgplrS=X64P@M!73jE?h<1b>5d2wyk%R(Iaq{SxBUj+qwa9-Lhl(%nSoN9^ zA%eZ;-^Gf6Fp35oY_vH0*UZf#NnM;w9k`%Nb4xUfoz=4UnW5>@7(CO@ZL zmw+rzfa*=HI8AV7dzMR|oSdBPE8E%Ekkjt}x&f)BOyf5U?{Wmod+A-*SnLjW2O%#Y z2`O_EEVuHB`@;G$UDQ*$}0;!%}GW=K^?ivNyQRZ!--b47Ob8HRGx$M|gJ zSNPW;t}Ka-w0!RM#9tg-NsUb$44$+DI;Ev2!hU}A_4A>(D!L*qA!CA%6eTo;J)P0u zJo%~k^~3swIXAmLT0tDkRC8W&wgqMgf2UtrM?KyW33X=9>?4Wz*1#R3({l!TY3hn< z;oSEwl9t=uLBe*=kcL9Y!9i_Rtm7&M3KE5N8#bZ__=nd`@71_5=gLn$5O#9%s}Wg9 zB=7f{Al3-XUF0+u5|kAet|J}l2m2rfXd1k#CEGwY-7@C&>u$6e8&W5DQ=3&Sv5L9( z^x~+r9@mLX(sGQ)$xa(CPnt*$~$oM@u4T z;%!b=@AqisvM|nk5|6rPf1QR9`owMsLr8#5O&BM4M-_pgf!`dfKy?l}XJeC>;exE# z{#MD}+fJ_g7+;Y~9d`&?ENCKU`UW>5f?F%gv$a4aAGcpYIm+Pn5amSht${~aH~G%@ zUOPGmQA9P;n30G<-_=j_8&t8b8DXM1-*)em_}VH4X`{?NV0C19GipVlw%Iy8idqLa zKsAk)QP<$$=4aP&#QDlrCQc+;<#3{H-M5_f~cRFuTisfAe^-R({Y|% z(V%=s(ERKsU-JvTy+r#r6H#ASzvC-h)q58UD^uWbUgSOnCLgvTvtqk91 zH9Ar;r;fM9mu_eH|?j~10A>lbLBrMxiYDg3?gqb}Hpf4W&%;d3$Iepz*-WIiy z1xFLN{`oEM{7#8Spv!PuFc@_&%28P?46XkYT3g|F)ae0JQy2!j8x5%}1{Opli#HTI`CYqSFUyCAKpU<)D z;H&KH&asPW$**~ozQL{|_(5jRC3)=7^ljtLV)?nLp?+nXtI(O)0ujuw%Z?=Ou1vo% zD?i!VTT!EcZe6apoC{gz>!;sr4?_BDXLM*K0s0j8diUWP$qYc+ubIqKikP zyXal@@ZijaKfo;h(gC9LVbwi+-(Ulg zws~$Lq%!$IG9B5wE_82-#^5_vC{2_@5e`ZVHVI{?1zm6$0bwUk z09e3o1dmlQ=;$9UqT2Y^H~&mG2FV$raFU>Y37tYyy`vrru8$zsc!m1;iJf=nRiNd^ zAG2AmVVm~~h?uq&-Ur3R2{oN>7jYn?A3@LXe=dPyvCJRiXK+N**8{G9gWcja;J38v z@{aCVM$8&yeAKQ(h!)k8g=9)^0o3GA?!eOFW_<{8 z)!T{~kksprrRQd-?>TTioX1}aKdIRb4urmb%Fe+dft(T$?I7iX_-u+<@!ao74fD|Z=ESQ60|K{g!L#8e+L;fj3dj6;Xc)Cpz@`_!{nsQS2lmll6@cH!Ul_QrmX7ov4pCMnwfEuk*BT$mKmQpX zAdM7gMnT}$5d5Plj7pFIRI)MX-!p)d%H@Uk!#xhZ{JF=&7&-pk0Kjtz|2`K1!T8zV zg98EKKTYWW;c{yp6To{={Q$(e*N2|{spuR~MfTB)`CM8`%LlfkNj&eVM?8J7Y4WMLK z21i~+QG~82Yk%E?`OS>aKKW4)dJ*qSgPco;9r!f2_-0jZ_16=p6z0-e_V21}M?oe@ zZN6sEMM@@|y$i(wTP!t)5GSiGjjq=p#;!z_L-f)ju2c(3{?*nI5Q@{M1aaxy^DDzd zLUvX}pEb}M49q-NZF;Xf-6*b1m~{qaw>s>%fBip8#iAfq@Q29&Ty!~^W5DTiw{FVN zBha7{69M>_FY%TLM=!FJH1G3X)%UL}llg7~p({V;3uX0@{;KE*>7mu4@5xY>1S_ND za7xr}WxI7VqXw?Rv{Izb;^7gCHq|SuIt@jj@l>8$v+}dgt*cIkm;+;9gn8LG+@zY5 zUnPz^1p-*;pFu{LSEcb`3}0T+@PJCJ45i9Z8}j13@9=A-ZtMUOT-$zaN{|c^{|^URBb6 zVD{&lHujbuSNyZ#fWCfZo#NE%A6?I(W_23cZ6K^{$3GC#m-dPd#j$hswyiKV$Uz}d zuB7)Jtg04CT)VZc31Sz1;iO1oP=}kIIE(e-;e9!4>~K6eu7(1!99XvK?{9jDy(0J; z>9iWyomw(l{AWfNZW^I=k-*y1FsYu4uTzU7_-JvB*L-x{5qvL{s!{2xnO|SjT_7(? zn&cso0xXP)9JB+iU4$+|zbXZi{LeILzoimibq7Sr!?kWQFap9QK+uuZhBcG{E-1LI zC^#+kzIz5)6$196OqKe3JuG6c+X|;fX3jxcPlQCd(Dl+YrWZ4V*`LK(YJI?3+j3bq zR%~xB6(0{Org;SBCHL;Qn@mS5BfEeX3FJue8ut(HoLsp#J9K=q{Wb;c!y)Q1#}BXC zn)X^o-!YbO7iEfJ`s!s+D(@X(Js>z1tapo%$q@(zJU(NXWYVaV{G=sUp~ahIRy1q2 zq$Hg71$o_Hx7QEE(+|j@me|O4=9|rwc z`NuF+l9X$(WTyq&NSN@OT^O*u!{868K1`RPTBUKoI7 z!#z!g)C8kup3tY2@Kl=|R_@8pYGuGfYL&V7B|w_o3PuHFd3Ch8s3(U8cCOW9BQeTzdF9@TdZ#no^~teYcW|#O8QD7w`_-M7&#|GKNEYs+(KQE-jf`$`36(w9W7r7fiKh4~Tirc{#nrHZ`-hc;B^n)R zUn8|*3uk&Rgsgp2-~>t(TiVV-C+6$D{h}pt_y{k>*e@I%`V!mZ#=&a#O#8;DC*0aa zY{x$sRDg7`#rGo6MM=9sP}SI0->6>EdHmQ0yRd(Z8|<7Yb-!T37v_3>Hh9}2(xM{a zp2-AB>?@G%XG#k&vhYL0&*mOxp!Z0~T?0**1SyuLl%Gz7yCa>#uKgNBS0fbS%@2ly z82X$9zD2j``1QSFz1q z2|U`{vxku>FdN|wOOov{x**%TWCsqX@($hQqZ05LR-u~&1@le+@FbT}@JRm|X2_@q zt;HgVVIZ8H&E{1{NJ04*7}#!-^{BSU`pAETaDYPR%i*A-XZh{A7_atg4+!>D zn(rrJg_yxOGkDEyi-IZ}Eyv-evvw~U z^$)9cNTipEmVn*PVt96WUnpHF7ewZaU$JWEs}y8jiN=X#bdu~O=cAFJCnqHeqPy`@ zV$ikep6jq?BK|$>$9Q{3-u5F1?mk52D%~xdcOAvXX9<&tLi%!5dLv59ijSlEB;(*a zXz2}^^6L0mfzxH=?l(N{4vTM1G^4k7dwZ8n# zv-bSF4lEU)Mt@IToed-8*)F+@?b#7Us(RdVi~~A|o-}^n@iYuWyW&M+tEG|j#yhdP zNV_xakm0jXiC^6@`L{>Wm-i|)^TS?CjSB8VLx_Z25WT7B@Nr?kMfmCJv*furnJL}i z2A29Iv{(2ONK_j4y*}`1jIA~y7UnaK5t%B*Y6lZ_UK$++e?}a7mbfP=oOWX!9oqS! zp)N52SxfPb8Xp^VC70F|pgLL8@;!eKtm zwbZ;I>sF=xEQbxuz!e?HBcyj<%h0Wjc51zy#TP;i#3@d=c)?bHG*t+x87_)=rub$I<_hSX^p;anr+e88K~te1Kz0+042u zb>2a*EJ}MfZzfq6eu9%^(HVDUMr7Jf=|5Dkeuz?x^K1crP}$*BU;$mO7IWex%H}zPd)LS5Y!b;(dWRU0yI(G?C_TD1}|d zIw(ADl&>SsoS^f=D2lWDW~j(DF`>9uEjfE@nO|t{D)$+Q<&tFy9y)q{c30O>$A+uz zNnXR#MkY;A5pUU^KAD})-J8?F1cSz%|>a#jjQmQntt1A7-;c&uq>Eo z%KLRKz!>z-P^^8!hsw!HdFe2I(qbtr-e>esF@wTxr6D`r4K|5*Pxz0^d9nVn$lQ2=GZI0L(kDN zr2eW;%8BBDRA05dpf(puA=1ZbkRO)wRIhvD$Hv7-JAjQVujoHB?y3Yk-%}pSrQO?0 zoZ~8Jr6{i39!f89BqHDG6pl>h*cpu|Dl3(j9vxL^%RKtRl!82+42h{&boa{O*`^hT_#tjTj^9hWgAi4uwd+=Vs7vZ=?>dG$ByCkw7KENF&y_hygK8 zr*|)1aSXK>M(5l{mPzea`?~Si5iO1NdX!E|uS=Hu1Z6kWg4o50#X@+YNP(yK{TDRm z7sO;28tz}ry?9hzyU?a?Qy^l!TOiozYo4y~Y1$zY)~dD#y?rGj^E*|_phtC83F z()woalJEYiAd#n(3u28(Q-0D={cE%>1a9F${tG0YQlnt>=`7z?}HB#Y5sxVUd~Kbya>b|l#xGneR^ahoLu-&IeoV=b7S zPvtce&o-A9CRKhi6wHc?-zJ`U5{4)2m6i(aozEvuZSECB58pPTOhRVt%h76dEa&4n zB$!NM&lV$A2I;9lh)yvq9vgtpK<&JiT0FpqQT zqS)rk{Fj@-u~uFJsTt@E_Dc|G>*>RjJ(*eobbu4#L2>R7F{ zt(MI?`0z(`rQh*u0$6}p=opXX5TDPROywqaXuc;E4|O986%-?dw|rvD@qTBix)vY4 zn0h}_D-nEhA`iD2SfvP?wQ>+J<@+L)S^u} zSGv9{KRb8cOg(`%_I&U%03A5Heq>R)9UI^G)2bEicbs1z{-?G2@Z@ub`#WU0-9-EQ z8Du!@6#3?RX$%K^uQWr*Py6QJ)XM92Y>$2ze5egcv9vDJ^evI{nt?ht59ARO@6|Xv`IJM-*phhLi7?*A+JWI#EVnu`t1Bor zc#GbV(jf;X6}C^$_t_=A3f6oBsr$KcKlW_~<8*CC1eEr=##u}56y}1r@!TrBxiJBs zEs0;DaJ<}+EWJK46Epb4L?bQ4wx_yR4Jb&SBwa znV-jC?rOrJr?jS~viNMnSQI5qGiSIpcXH#VL<50w4cBF+Nar?=*^Dn-Ijf1#N9@x0 z?6ebAWl1#&60I@zyof7Efy1*!eWkv+AU8}~Xm10XZEWD?yEE^{z$FZHdSZFK>ITJJ z;iRdu0~&f>+<9?ys)176Lf`Sw2vSU&m&tDa1w>6hwwB0LTQ~+_0~^Bse5u5Sq-}FJ zf{E5sIc*Zi8#rq}u?+_E?YsH&3K5LnJm3-zM+cbQqDlu~VH`{br4_9v_dp6psBaT? zH?OC62Mm1?ldLtL#F@GJwq;4C#mktwTU z&{d?bFoH+ejfaXOziEgT>&hQ+c) z6tI-Lc8u@qmQ8g)bZD0Kw+hK$A!*2Czf3~^S-3qL#P<(ahT_xJ8lL=;H_}9 zSf)i9>py8C6J4xN^<-XL+FQ=3un|H#R5C9+B(pVIVQ6~Ar zqkBT1S|fAT{D5^sd)si&tj;IE-DY&VgNFn8^B4`W%YBEG3pR^}mc!Y)_cLOBLM2gx zkX~|Hjgwa|-c3$=RQYxmtM$ho7phvKZ-%HN1#cFOUq6;9bUSN%$E28sz{rDufh|ZJj*`~<}3)2=asA!SvJ59%z4Mpt2 z6jmFz{QP_sy34Y<7E`yXEA@Gp>;Ay4GdT^J?#ZZjaL4`aFYtB-8mjd~0kEPt8??P_ z?hnHGj0;wKLZ97sjO@?d(H(g<_M@F(X?c~w6u=t^BpsTTO`jb`A!7d=?oCW=49aa8 zlXkFWnJw+k$MZ`eCu471epbHR-iHq#Q%T51Z+_#Q`9yjCNq!LhuH)UrP4Af1+4F!i znW<+e`)Cl3jYo1BN8=d|a>VLJ%~|)$h{n@GC3cQ`dux1)Ss8WdP+AgjG>c!^81dO7 zIW{=uvNo=(v#h@8PL5P8aWj-2BICt(ZAzrVzjo1k8xQkv-jY1imuM5(c`DJT(Vw5r zeN%^_K^NBqmzVV2kjH8Ee8o0en!ozdslPeTr^3c{$(~_=dl)O6t7mceoRu?<<)%*t z=aRUNO@Y9$F0WEodkl=&eGCV=o2VT2EFw?Lyt}0n#J*$WG&0@K?T1Kov^7OF{G>Ca4_f`R52f(7M^&V z=A!w|xA0I~#WT>h65C2``%{yBDmzfN!j1>|F6OX(m6l>~y6GeRcjQ-t-YttvTcLOj zL*9;g?@y0O-2?p#<0+rZqjaqbfZqbOMM4u$H}Q*W*p6BtLN z-F0{_BjfXE#?+E?Pw&;e@@c_*SY`+AJfhC!JeZbZyg{Yyqeh#7{uV~kxDsZ|Y6#o2 z+*XHY9V2`vSd?RmA`=Ut#BC}acQ|XS1{nn3>9W%#Mc6PCo4B1Xd2r}KxnEE{+9(;f zJWa+{wgqIF?po@<&^4>Ah&}))JIkuD1(>n>)5ZwfI1qH&el6Bb#9~}${TV42o`10JW0~EMctW_?+}EQ|2c-&` z=?#4v8*lF&+h@XaBNE`ec1D*C9iG`=LkDeBDnVCLrM$frQLU|?JIkcF>y{PTy)Dwd zZH+c2OZb{AQ&@7*Y&|(GG|n}EA6@o7ccU00>RfibBx3E>sHr*PheJlTrUDB=Gj5wG z%{OplM`RSQR*n&CFigfN%=P1yfYz^{T-;i&opFb?99EXJ0_+M@Q{}_Sd zFmwaMjatq33T_2A6!A$CQf9kFBNo|+!((gd#3LR@b2A#}S%#2RGbn{4qvM0^oHSlz zVVjMcmN5GRDKM9Ei(dQc))^N94-lnBNen zWMM^9IAr^CoHx<=c{v9PCMGTd27pnP*^eZ%9g@rth5Lh&gjxk=wq^b}mptHY7P)z> zI*a(MV97?e|OAsL7nPM;he3ljm1ivfK2 z_Z2UJ+YqYHlxWez9e?nPi5|TL7y#a`4~KR85H*B4R)?MMX`xT*fFgkwi16ws+pYKG z9UJ2t!9TON>$aP-oFbORG*GSo;|r1-_14)Q(LDiJDB~>#gH-jZ`4;o(XyURNuaq=~ zYF0fAR?7>-0>2 z#^9tcaoyd~n87nV6GK@cWt^~$d#EA3D%$BJ5#BVn*tAcJ0Cr1<0YmrKu1hs5($>b2 z9n-7>tv3@=J;mDh+e9R)`K&&)nsRmI+Z<{%B6hd7MWXQG0cyqfOY(QBRcx@ra7%#q*UB(@~;H5qTxX3A5ou^%lD*M@0UssphvZYm>@cy{C4sBj>foQV0svzLokI`z^0KV!0w%g6rzg~l zaKEK+e>7(x#fiJrQmZlS6#wrSab^svtrEVnrDEHQT+G^fzH`?)Va4ZaHpgp_gdQcx zn^5B(np)OLu5-dwSZ^u0DfvV5vgr8cS=S4^#1Lz*dk=>d2)J0s?bEo6>!|)#K2+|2 zaQcQ4r{eyTH+4mOSLfEm%>;5!#jG!~Mtwua6_C>`D9;S|KK*@IR{Hiz)73eYeq(s3UH6z`=D9r znsH57+mJ#e|FEG(PsYUC0ka<9DwBT}a(JE0@No(-Pvl79Y#p*eFOTtDHM}N!0hf>* zfEVu;?t)Ae-eh$^H|nl&NAA0hf3=)}i`j(^r|46)FIO9~$_9Gn+uqrzo)s>M#-etR@YfY19z8)?Ki7weDIP}Zp?%0xYM{56 zbuiwY%OHNOf50TZxh+EMQk>0GfaS`~VE z>ZA)dm4-t@-`I@>9k>+HI1W-*Yhsr@>;x;7aP3ckqAe|{R(#3Ld@O3PYgY1~ z%R~B;>S6vC;QZgW=~0lH=<)RU)%k=C-dH_6qg4WS78vVJxF_9{@6?uzr6;hXCyxtP zJn3jl=<9JQ$4i+J1RN?nIW*oiH`}a3j1KS6k?na?I4rIcr+l$Ja@qeEhlNpKFW{(! zdkTmjY4q@5!}dS8VSjKnx$wM}m=4oa#S z9j(b@+Q^zUSX1LiUB)+qQixn;iZ3p8eq?OFie>rjZWAF>+j;IFPq)sLXn24;ul#>> z1$dYs>(|s~VJ`LMX=^F}H!onosRFEw23X~2zd17iAOrq>&D|Az`29mmANm{p@qckS z`iiX(QCTrkHi~PG1!W9T-%>B)1;=1SbFy#GBywqK(gdWhr z!M}c?>S>1Ena90FnYa>d)WilV;#T$&G%p-Xx_3-CVGCK|;1idEw5#r)rC$K2<+lb; zQ_PDWJ?EIKtf$=h)fpPgidHIO<9~o($>ipmHFAAWB_K8yqF3^*iWCo8L{L|$%2}!| z^v%hc)vesrKtN68n^kT_GjBC1Lj=*(_Mq)qJCfE5nBLbkD;G7s`POJ=840?(;RvcY z7^XNREvicTXja#9O;jkAmpYU#86yW(y>8cY1$py|C>CUXrlQF2UGU_|Y7!6!w8|Do zM?X11!_WB&P$N|ItZqy(xy;s6r>r8?MxI85Wjn$&-28aC`n23XUn+CDuuiAh+q)_E zOovJ3Z5f<2IK+a_1(()mx?KR_jey|BZDd4QPI(Xz!gwZlwr2VNHFcFiaRhBTh6Hyf zxH~Km+}+)s#a)93g1ap4!5tO}p5Pu_g9LYXg4`tEeRa2gmYM=;x~He*eQW`Pf~5By z4Z#<}HCPi}8#XA7^> z%IRgIY|fFF#8?uXo9=eRsYV1xJ*z(PTFW$6U|jYj)sHy`TWc}nHv_dii=v57RGGan z_}x)VVj!HeF|=>Or;h9jAnj2V{MI^CNi|IT2kgk2=#(lehC5@CXu+7Z{o#XP^G*?C zeYd&2UiSy;T`;sX&X&P!0O>{scL8OeO{YkC7d0A}q z(?bF^{fK%Bw$nADs9?9D(%P_4hwDBPdM+2w6`i-NEDVGD`hnn#jft<>hPibVQ^6PZ z<`_H%jJ`0{hdWtSPfe-*1iKLeB8mOiEUQ{Aestcy&*9LT%s2CrmtWUkUTA*c_l(>r zN3LGY*IibTfE5sJQXv1;XeV+04vtdXr-FoLZNaKhB>V9zva*jpE;H0g(pS>LudAhv z+OL%y6yQKDGpHkuvEOP}zhuXDrkiKx&O5Yaed$rDs9*5W|N9;ulQoip;b2-e^b5;y zv5p~<96pXZ&DO+BPpe$&-BSW4Js3WrGv%?2r@Mjq$$S*rA7#5oHaVj-KBwKrT4bai z@@WA?rmIp$n0++;_yz~y-1C@=zSHUf>)wLo%+23NFO9m;!<}KQA z5k?X!y2fp&`<}a;C^3iDMoXjAmE4wrF0>)>ADVeR>chP6LrVti8DGoH&dfzwM}@h> z(UomQAV;C}ch(wE1QVYm6(=lQO~DJIkopD*@3k%y_}xbfry$ls2D17l?#xuOC5lE1 zCB&P0#g-ukuj{$q!Jo&c;sL@n@y&LtsRreg3-NwLmS)6jyQY`=?y?Byh@Q%t{?#kg z!KAYNMmsNWpdx=0{R^$x;6p)SqVxV*bxv#ixOX$HY*8Bco?F~(A8my^m`HuQq`_ndPa zqh!8^_`v6vopet!CGH=YCGi6`{JqQt69lK07rx40*l-8(UfgCJ+jLOY0E_Cayo|ni za&4pd{SGlL+ys?It$Lr2a-oB0;|2WU8AxV|4~JG=awz9(OAw-CvVvx{e%gT4tI*2# zG$SK-^Vs;lYYbqaj0;FO=0gMkxq|Oj8en`_qZ9v7IsJ zVd6C%_O@3Dm36D|wB`MkrI-*GUL^Y)j$Wk+hh=Y}7iytw;{I0~c*&S77J=RG*i`68 zMrm!`C}wB<^Hrei_;c6McNYaijIx-5v}uHtMb`Qc6bxJF>;zMfu?7;0_v=GZNC~{) z2{ls0qlMcf$^oqhl;5Zq`$24c_1O3DPu11N1x6K*MNSY4^=3!OYPPZfcmMQN7fRx> zC$mR(WQmBtF0T1hBPuFA;-@7vpvbxN74^(-*OI1|0J%aHK1rKRq;eB>TxZ{ddjvP` zQ;2|fuBq444z8*v+!;1#b86Q!^l#%?+NpYJo zdK}#_N~b4YDmNPvuNb@)Bq!f1o9>o)Vy`^&v;8;j*1BNgN8@o1wcnL8T#3s0Xb*8U62@>}J8ve# z*T{g~nI<(#oJC-bXu6qL2C4cZvjU;RSQ&5;e)e}6kR%Oi(Pr!rSsbq)CMRhWoQm?$ zWm0s+gkWgGfh^{G%^bR0Jfok-Bbhmj0)uCN1Qcv>p(Lc+FzhgG^1ifcBHgahdYsE# zeSo?>@N8RDv@hmPCSo{(FiDyWw|DTHKS4bB{M* z`d*vx;MCapLHOM?;!#B%og=eFzX+~y9blid*Ilj=G0^ygFxs~1jIF0PG*A?VlV@{$$4uKs6cGN_1W>DaQ>P4e_DPA)~k>AXD3cz9dhx#ZB*a z3s;-iiGi>!ffO~9V= zKN^ASPauIxHD9d;FLs#SV-e;QMUbJ9C=ZpKAAO6riKcQe4F!m zytBVGQ|8x%yxf`Y_yuQK#SAq;m;gRBGdU)u1AXXOi8Cx&;eIR*Jb%XI+y^An_01NI z!@515djnG!Zv?OaO6wr`Dd>JcM0643o|3Y~tav=j$+n-NKJ=x?)SUdm8%jGrtk=S< z$er!<#es5bK__{KJxWvJfI7I}ilM!S$z#ZStoP{3keiHFNdFU7jiBjDbdS>3j1k}G zc$a7E^Z+JX)f{s7Toto65>2a*xB^z~9Yx4oOV&|*{)g4tn$5#e&`94eL%Z;Yj+~M# z+1XvtH5>}Z-?Z15xW%2OJa4QUv?Yt15_@}BlyV%lMuSaBgw|fu-$$t!md*z8Thb_& z5YqHodT_<`Bu-)I2xt+#G!}MAHH~Ya{?OO+a`}-b{h&YzPQE&gX151vXgr`CDP<^f z_}Ow$3P_Q&^O-Juy*=>A$ba>BBc;GTzhEpzqG(O z&uGJ4Cz)TN=|~xll!aUqB=x%pd=Ac6>QBGj=3y4d?8r9P31@e}sF?qanbe71Lp2DO zv2M6f3R$dQ*%H{|I?dnD`BlRF4bQFks%q@yyk=6&CHJVq+qFtaE7~<92e-~lX@%Rt z|I+AkP0lq-;$+Qfa|gbFbeaN*18X;Vd*a1s5HqO!>S$&sF2eTKMms*4F1b?zGpSa} z_C}ktoZ?mh-_$he+Ic>JK#v*;L9Bq8?7=w(o|MAuX)RK|Os82E-_kXKM3WR!q%vUWZ`G5{fCs-jJoGlzf~0XeV6y(p)g> zdpqSeoOR6ccOFuUq5S9M%xLR2;rzt-S!w-xqGl(bbJJN2n4Nn%KgxT& z#(u39XgWf_A4gV`Yls)r`tS+=oJ>oG$WW@W1mp1Y97}miOB@J?-I@h>@R#{y@*|I> zKO$xC@P0u5sQM`oAFhhp9Cng@*wVap#3@dsQKRAPl*w2klJ(1vf7e^$lQ_1s%>~=L z&3h+b{6eOio9J44>g&9xQ+AkCw$%Cs+lX#ch&|8Y^aJ#pc=|ZW!|y7AG715~@E9zy ztM*SmFN5Uo@o-hhL($jfFi-O@Q11~n>eETYwBVzUP|4K_c*WTmq)m#ri$e~y%oddA z-@;YReJv3-%aakmG>0#{P6M&@Qob{pBK@e(t2yFre0)UJFdf=k&5{j@S*G?d z+89oF%pJS2oR80*X-?P=Yp!!SusIlZ*-B=A^q1Q^CcVt15`TRh4w_vM)G&I_Co8ViT1eLN&Hr%y-*UoSvBDOQb*;?li4ec61lB; zuQ5>pNoweu5s|!OqWo{3i^B%R_%~4{SOrD2umCw$7Qb=7gDB0T_20ocq5%tsRfA@W zvhFZ1{X@n47&5u4C#5rcu|?d4Z9PKe*k6##s}auA=T54y-htiF?iU+dOH$*J*LCx-rK`ta$s2kPly1RV45!px+X&rr2oa!g`*dBTp*KlNR?&DJW z>LsDk-KRw)gxg7v;`Iq57)`JU5x-(NIA$wRYzvcq$l2giXs}gt2I&auZDnlLoMU`; zZP^1>od7@{+B^bpQn_+U`L0rq5rY5NIN^%x%XuLr-EH-`E}JJ}wB1LzjZ_#7XmKRy zo@DPf!zV)TXVAQgqCbx>rA_mUAVy>!I>bqFx9`;HXwvnxGNM0)O1Re{9`4o?4QzJe zuC5Tpvkpx^a{{%>&q5pyp#-WwLgTPU%&pK01m|F5@G#7@XA;vL*(ebRUy|LiHsIs| zjjh>Mzwb89#yOU^D9rmWR<+N9l#Qak=ChOp$t`h}hGJlP+9(08bkITM8=vZ*Xt=Qb z5~zclQpP&$)SiK?qq0t>Hfe!1lkf(0!K2ubrZa>L+|of5W{*fm6hgo-YTs5D-|6}7 z!#dPT{l5Ou98{#GqNb>y+8}E5gQd_KOUJk(Qp%AgYLvPF}^sK+ioM3 zwd6iH>lO}Qtrg#KSH&%m$m%d|?i|ei7~nq2o|xa!(?sEDN#SGFBvj2E4h>}+-{yx> zuC|Lb*`ki+pt=y;u|?b;Pk*8L6*V&+71IHordVxLZ|n|uF=W2_wK`k75arcvw>XOgTKnbmjz!!;N8X>6?GY#n7S zDJndB22yWzi7gBe#8rQbqC(vYeOnzedKWuFfw!4h??0j#iQ6KEgO2o!$-c7x=f1L) zu|6S8^Du)=DahTMR;-rUpgk8oOF9^dKEw5M+_OM#w}-flBJL}D(BUUjGj*+d+S+b$ z6;ZaJcFeH$)3{@r04og~FkMQ1=rP?l^+O#9Vjn>g^{S3uGW)X21C?b&v?-;T1b4N? z$<+aEm_U;`+q};&G1|Ap{hT<%ZnC8p&ECzxU7qdET8rT~&*><-n{$qiaaM+(I?o|c zF^PlU)HF>n^L3<|_U{*z(y^nk*k_gBk z2MYb-K%0^PM<$eid-P zw&Iy}x0E?75W>`NnYx2S16b7DK+!3xPxaYEfAZhj^>1q|xdNUU&%eb3 zg1!n#@jww0fm{$o1w`=D4mv@eJ-kWGk>wWvdTCoZ@;})nDL-WJS^YF^fsXOX>o|%MkF1HE&`D~7?%d7!WVFaV3*5edI{%>t%+W3 zb`Q10{3glGNmUfIfCK1TqK?UH61bXT73vPZny=`;eQ^;MRlKo7zMV?jYWrNNEVPzF z_NxX#$VY3O%VS}7r(W5XHJ?{@f!$uev_=|<@)c8+HQ>;fi!=g;bfoQJq<+6TSnS!} zjS_lsdj2!=-Qe-*cI{v1I#suwv{ui1mb^G^FYe-$FdI}};zD{r5!UvaIf5WYjtZ+# zESpkuTA;EA_4G~L;NkoA@mKqVB8GUl!>gE?2>TcQa>~ThmL4i9CUo}&h2)0>(Tiy8YU<`vkw;8 zs_uzM`(IBAT7YPa9lwhUlGI74jN-UZx?mbrI#j5J z87GYFuq^(4C($(EqqVVM6|8Ot$qli#-T2kz)(^PQ9!C`&bx{utrLE*bD2>gY2Kvf+7uo#_WAOqGV|T zQ6z}uQg`2WMQmZdPH~$MCoM9v35lVn%T%)5In)6OsK+C1W~sd zIAS_dW*1`Eg2}1GGLvZwsg@;NQMTiKM6(+oS~B};n1AKrg?wzUj)o#)1W0#x4L%H? zU+Zs_{8jB)BnD?mP$pFK3HyrtO0E|)YH$u0S*4KKRvt51`y9Mi!Qk+q22A=t7KXs^ zkw!p=y!yyhkBxM4hzC@mz`;JeoYZ4f`6jUUu7Z9L1p$l%b@i+U4MP^Q-*4a_;0>}U zk&iIsGX@jbz|o=ns`0qu1-YdZ=tm+3jN>Nb3*D`=10zWIk8Hc>{+ZL3zm_XXnvA?0 zILAZ=XD3DntJCs7?o{lL79CkhDvfF9uX9&qGB8<)h$Xfo1yi9Ut3U;huvqY%) zp?mO>m`T#6o#-_6Ib3JQBWf7)`@3j}!^l&#lrjw2pUe*Fy$;0KPM&Q4TMK|IREO+h zrxfnLX<;EdM@afHh;nsKrM9g!XWG>og) zf~4~N82I_$Xix5+PU*);S4^DWVp4`U(hfi-T>~J6*q_4gaYn&iQe1>BTx@h>rVM=> zX5V8`VVVOKo8Q1?Q1e&WpXAlY#L=m>mB$C)hlW}ny6PNsq1yD&GscdZXrP*+QnQsC z`$2sNmj>Uv<$I>EUNDc50)7NK57S0`dC=CBy`v8td}qKleETgnwE|Za zXYG$vgdE!131d7(L<0k&Rrz`RcCkoqe;a{T!*~v5l^$rGemU=4EXq#X+7XY_7QYQG$A{o`2Dd)8HG1(hcA1paDyF5%X|c>1kWVEr0_WZ+}tGAtidasa|sHCB!kgTRs(*YjZC&_WjSSUvK$F#iX8ub=Kr700 z7{-Z8F69Y3_^<~DFB0SJpMVr4QQXE=+WJR%B@@@gUZBqycfLZUs7T|ftmp;uqjl&g zElO?yBc%Iv(SfW33hdQ&`Vs;H2hO>;k8c$Joq&epqzk6^uDh;0W8zZc*-hnIwjY%h zw6!ET=$BLbxj`5gJl#8r1O=%mtHz5=Q3>2mkb`^#T=C6g1wu4s!2J;r__RcS4Kx!H z&C|vO`hb`C*|MnLAE7%=#)W1u4b0Jw46|HI9RYHkBsQskIg1GO1izM%PQk!0Gm`1j zPr*%t+y5;nOCGz!zVnd4e+^7EUkA7?oP|!cPw+nJ+lgSfMEk}dr4CNWhuJf6yO~MU z(TpWtAhamwbvp&gqs|ZP5){Brw$tgLTV|Cq&WAQE_)dX%5@Wh4rh8?e$xr#gvgyW| zV{#ifSiwv(Q&xj>8!A`T3h*n<*U#m$Yk^N11g?FI!xUJyA+jzllNzv-ZeQOQz>YWy z3`l24>~%ph)2MC?$f=((qsqGE52ZPDh-_qt{&ciXi3f4obE>)w1OvT5A?1Sq9P(8D zchB)zaUk#j?*j%tVi!m8G`z6yE)O4%#hc75s~OU@1KG{$Wq%dDG1LZN$t$CzwMFw8 z-VS9U+i{|-T9~}bm${~}b09uJHBqE$!et}8AT~3z4b&$}f}~iP8(3qN{IEaU`Ye(V zbCfJW&j80Im{8AV4ayW;K6iB#S8*{17xIY5gOyy45Yz#r&E`W~dfS`+P4E+=tjwFu zuW(!76uFKC?QM1Q=v=Y_M%PHk;ssffz8fo{JT$n&Z3f%XZJJnl&nm$Q7K$t5aUnr5TZxAu85cXO$79R&R}k$8{?vikEl{8 zd%+P%egwD38v&D~1;!5XcXKxWwt*H8nm5r3xtF*J$@l9CJI#Tx?>Z|F<2MqLXFW%G z1T!31U9uxU=nC=+UE<^LD5v<9o+c5H&4%CtAK|L8EielQwYZN(WOc&)4PmrY~d+d8jKN)_nnDgn220k(wKN^#U>SVG9)w#EX{CYuVzu$k$jC{?-Ui-e4&}o!Bl20 zxaN?u?4WJm)f*>w;h`IP>fm8waFz*YL}Cvo?iA8;6N_W32L)k=cGeet%G`=l9g07% zP?OnZflYjmRg7||ahl=Z(Tg)H%gH|fiS{jydJ>iuVG0F4-_+^*B~)Nr!KzCKAsYk( zzx7I2`tVx!ui7?17H zp4*7Hf+xNMBL0&QI0ohoRp%>P+%=q=KQ%oq+m0=S$4~Q|p3>M`HaqBlsWYJz2z5M3 zlUx<@`c+^3XLZRrQrL2xQ_v*a(gQXkI)7aPYlJR%zTKN{S|1@k@5`Z*Wd&yJ@Vd*|CJaX;~f5KWP0cDvvPUj%u2Zmyle$E89ZT{c%6&9|Z( zdt|{f)06llGJG4R6o9uMz;?46zra?>7)u9XArO29XUi79#IQxl*~1D* zazL(;QwXmn1^ApN#x!mH0(_&qXP_@|^VVMo;GGG79qxG-SRC2Y#AakV2TlrpD~;%J zY-psLUvQBZrmkx)8ggN~605(qMN+dKZep?`Cn2QuR_`gOto z$8RgzUFG>oX%o7e48!7Ir{X*0;79yk#(;C~Yj=@_9zqOHamU8G`gaP!aRJUqCag%` zurUCYM)dnotq?Kp+tscU7mt23{5d9|7_bQ?R>{qZB#g zS4D}a>;mT1D-9;h;R$hziHTqL^N)zL`oP#Ob%siHtY5;uuQ`Z*(GBQ*46JaN5>Cy8 zlkBM6OiHzX!FYq1{9BB}gOw*;j3Vv%! zMKgbE7gj9b8lm%cQxV}orqd(5`x)&r-kr>`Q-@X>k%MrovzbFj?_%FQ05d!5h}0hV zI=}yNV?Lg770||wd$Ky(nB+Le5&W!QMi$HNcRp+1GhoAi@O_Ahs^@Wg#9C1tczYIg zsUhpO=39;$V(CwZ^@U6EsZZIrsXKP5Qz@mint+=T1NB{qosj=YEHM2foyVd7fek_8 z4{(Dsc-l!UlAJr1M)34?TmFE!pF+Y=H1soX;KA>5&O37@VXtmBW;UdGdFrtEl1fKt z8QtA{RuxzT{0CLc{FBdp>QpVVAXR`$2jSrls%S?v59`!K<6Y8)r=ZHgpUM}&Q_+DV zg66sMFh7P*Fo7Q2bgHa5d}Ns_&B0}QBaD05MV4TV>njmK!SrONc|S0STU)JU(_2|$ zp*MUL_jBU+&iaxavQ|nrc~0pddLi2yPX9R0bZ%UZ(fHH;b*)etg@{sEns$cjcd81v zk6PbGHKW9c$Ue4w%b^p>KK6~V!ABlxuywu!c`Eyx6 zo1YVpNKvm`>p1Lhl@vM^UiFMwdL%*g3>6lxsvC5lic>wV-YYF&8Mafg4T4aqorx473{L~_z{n4mR~u`08uemKs;y-gtfdCjk9 zAaR+PT1NzV&3_-$*4i7I$&z$_R8Yp0)jq(wIU7tJeK+#JJhpIEF7&MiYwn8-(cg5h zYR!>g_H83A*_G70Z%w~$h;iM2O;-9jR90_g#?u;P#}LFDKBKctE1MzK^?dRD_Wfih zlo+tA9c&Z1zl*H0(sn)hW0bYx3tyI4Bd}NJ^^F+2mEzjHEn@kIEun}KQ$Q!T40grL z%v%0p?9b6>8kFw|7Q9Dkh>MdK1q)7m7n3k3S$7x0CQ&;la#D+R_Co&kgzJ2~8dw)T zSfOd?+@(Z?k{`?2zQq&5o$n|9PNjIKON?6+Z(Y+aI9ULqsPG?wc4xE->UCeKzMVah zV>HBf>?k|ZC4=05zs_7y%cbr+5D1dJ_4Anz|Lh&Z8j48xCRESx`!)$5zmapEeS!7t ze`^6w{i-V|W9A18C&8#X{QZhlbb1GmGzht^N>|J1-^VC4cz@?7uw99Fo<$j8ThIDM zeCQ!Olrbaw`-~choZaxEipFcO;W8F4f9kT|yA!loXw;B=)88pI*hyu>#v zq0o#AoVOp**nZb(30=8Rn9W%i6sW%`vSaBRZ5YT<(xkk>B(8SeUpXB z()XUhhu1Q_viK=KkG65ohY(jN{LtAPqI~z3DNZvhR21!5s@%69(CW}V2(5h8oV`cI z=T$Yg$<@!%xaTt+P$D8*W1>{{V(+|(^j1>$>LzhP5PZRllxd%sV2x)Yx<_Vm^X79u zlP@JryCh@|l5=5Ehz_f3&XGAkCo6kAN2gch^I{^-5s)e72o4|Q&3d+>#@*Vx+~mUn z!7;Me^z)aw8m8$ON^{jhEqmDqq2O3) zx4y9bQ}?3c&vlp@aZ{SNRDlD%TKr9qoA&_YqZ?i2{g&~~6&TFx@Jy4@-8)w-pX(QT zBE4NgS6$>}=w8(K?Wgqhql?fA7VHzSI9p-9R_f^7r<0(e@G3%zv77Vy8K73Gbe;>? z++E1=t@^s#3EyeEd?q~j>3WoTDofEWy^;!Fk?K|UoT7Kw5t<~RklUtPujbzFQBTk;J^{ zdIyhniod-l<~en(XihjqMGvE+2V3!O{#j_x?9)x7ICb2=J{lA$9G09NdbXF+VWFu$ zRKt$3phltD?Yy10zKIIqx0_jYRBZM&8kCZ@bHkhZ$ZbH%BhOvB-fEWaK}4-$m)TvS z(I1+CzDdw4OTyJK#-+JbfOn6#lDd@gr&r4`xAbBOAhQRdtJKt}^@9vIDLUZOtu$oC z6do(iz82rSDAboi&hZEfnh;zoH1-kJ{A8L^9@EnE6YL_XF3EIKZlc2@)vfo}Nv@6w z&(}f=mw5lJ;#B%)Cc_F|4wv{3aa7~=Xu%!eh-kYhDuNI@a#7|UPaPtYRX_x>zpS{!sQ)RB15=}~2gfJ{~coszjY7cEcAfy%+9NWhoXg!jRq zCSv&&L)K~BDY=SDJ=o6|ON|kXe|%IkFU-X z7|CT3!a5(}M(BanL))n+@1gyE9Cx@+VfmB28ii!pX*7vy@wXt`uuafgzV|;hoWCJ? z0#jlhvJMin*jlIcO85>t6pEWaAxT;TS?BM2+WTttmPtBQT_tNqnsDq24AeGlJIwYE z58N>|FIt4F*3GRxeH}~g{V3cmL8G9SXbT_1#N}8>%O}P+V6}U%&q2{<8d`Ts3JloUK4bf&a2e@kyeaguthaPGn+iUl-A=Bf`#9O0 z)DLGX#j}vJhE5_kd1#ctRE3?EqQ*()oV`lE!b)&)^%hRzV5gjaYKU12FuUsuyE^Js zqZsCDGNn!)Tl$W8be1S-nHWq2hlx-4ItifCa{jr|H4=NU&rhA_P6v@)D?j_8S2yXh zO9*AiT~a>@Clc-ZByP*%*w!I#cQAYlf-r@fd+9VeH{FYGTvo3$N^Z>Pc~+5_{>nQa zAf~BbU32`N%qX`(vmkgd;>nlDjnRKqMLq!q!0&%g`C>lx`81<Bse*Ni*@vB5b5+zcUuX3}na8Eksbtx!SaW z%9>c?rpj(B7zxuo?8HnEfnoLP@%?f>@ywF7+-_HDZrY2Nk?+wwGn>O~1nO2qAc1M6 zG|1Da9IK5u0`-S@B}7Fq-Sr}feblkgPQsfRJWJ)qr;|KM$Pip&)OpgMRKZd^%RLbE zRq_j(!~ohHhn$VJUsjrDp^bMR(z?90;0D7xG&Nt2dmt+dS(S*Cl9WD~z1!qnF*f0J zrH8WlEP!GMe`gx9~$GZ6&3GE7c@8SE$Z1-mo~xD z1ph>+MiG)v1=IsPH%&9ZIHEa1{1>yy!9|xzi5@b@(_HT+W+nUU-rS8~RQBcj86}IO zLwR+R|7cSWzeCg~^2qQKod;VuZN~SCAWiWL7`g(0QM?ATthyh47cQbw)5~N+CwvoW zZjhCLT{{T7S7y+dSUnPFxn71!r4*parzbBQiGbKGnmGC=B@FRk*TDf3NA$?%k=cnj zpxF^V#yIi%V$gzFrfM+dKuR4U;UdVqkz-giIkIT>NN774Wz`lo1!{hV{Q@g0DvJ#6vstzE98dq`%ay8FIae zV#Kc_xDx+_$6P!q|5~Rz=h*Mda8F91jn)bKSel~X2+fkjP7E~ca+4t?9xCrj71Tl> zcvvfzAwt;5S;xyvJJgExwKOC69)BnZnavC1I3lyQ7yrp%L0 z^(LOTj*ISF;)h?=&3Q#Qw>}Aaqe)U&?@A0XVL>30|1jQX zs%1y#4}j9QV!Cif?nv4kGuCBZ&{d;Kp-bd;_{^}OStT9~d;fU6$oHvrZ+jaC#;h zfktgAdU~;O*y5U?>!RxFa&k|Ssxk}15(M|IxKYRJp|qj+%GqYh04??qu!c{^cSTgI z0<26|3F=BAWOMW4cd`@4b?m&H?Z%Og;QCMpwkn^Dbluvw#dLcGM5 zd8gMhnm?_6H}O=~zaSigyflTuAXGC|LlX*XtU5mt7ctcyMa{lRsguS?IdP?6!N_{+ za&_MMLAkI|nhL>>b+^Lb0jfDhX~Hq&Sd@Tm-c+JM8_Be~&7`ef!xEj}jJHD*3bWj( z8xIOEMxrtEpdRkdJJnl{elv069e>(e79swPej31Ta> zAVzk&LqElan}m_`ZJzkpS@|;yImM~3f|CcgLZQZ!y`74ySA>8q(!FFq$a(lU@uMb; z*kvl^lVt*3_c_{$hiiz+GV1~asy)h2#ifEWKI8mfDIx10&&SIPHZYOVr1>O_Om|F0 zD~FF+w@m()_#S1qfV_=`xb@-$p3EF-0}cI1;}TnP=UZ@Go85xe`DJk&9YuZ%5@CiU zE!#(t{@(p?lcMG*ag(2R`8%Q|0MV_Hp53V_l*Elo_DHFho?wr{4+w)4%~`)+zuzw9n6pnnHJ^^<8j(&3N`f0H)b_4r;8TgJcV zuILnHG#y3lhh2%9R1^(@Iqq-XEAK8p^7%^U@xtNEYtd$e!hmd4h|JC<`CRn4Lcc_0#1}^PQa*!nnMs#;7NL7x32mgY7l(Ypjk@9)rs8C-_i*1X?e~@_#_wOs`rN_ z*~!TtO9)nOQ67~PO;;Yv$vuvp?bg^{=noa%F}#ytNWt~<&_85QhrO?3<~TnhC1e>h z{&E`olQ~_X=gn+Oyq(6d_8Cs=D2VqxNGn~m0L{Bp94CF2Ao#?ZNU&KaUSt!_e2VSW ztGDbj;v(vaRXA|wY!fwSMUmyv^7K_VVi_SQ&c@dW3)Md#IeN_!bKOsOaanl#=WnoGKI8TH4(!2x!#`Eu^ z6%v4@q^v0b(Lg9j$HJbX6;T2Ob``ttFMnct|HQ6Jp4f0?;)8&Q+ibZ%nIO*d$i%$_ zUncvlatGc(B%|9!9{=81&+`*qgr0JCJVo*`5?*hrr<2%%zsSYq)heYag3s3-Lb$&A z_f0MSycyyYGO_w#s8AVK49#$(>x>w3l!;YjVn|1QoCW2ik+pA#gayA=L!wCmsE41P zwW2K@9UBJ=U_!7pL4PO_f*0o$i=vNOIk|8#;`q2UJG&gn|H!`E5B|xyV_o0(Ebi?; zXbFUyK^-BClpDI4Ne{$NC5jXX?#2S0uj zPxGV8cKd5F{qK{ZM6asM9|C0-n(8_G986>)c!b^Oo8NV{7;&ZJjo_1#L&h(6Nh};Z zly=Sb1wmSrU4f_^GD~!EA{Sdnpe8B!Gd#D?xIy$a%351+Eu6vgdivpsIwUI>Egb{| zc*Z)2WS|T|;{zGw66({$2DE5<=rAmG6e%uH7@=`@9Sp@9rnnRQ=V(u~w1#Y!9BniG z2r(DYE~eDt^64XU*AS2L1eAWwqUiQN7}iL9(gYUxMPxoLKr9wrn4`%7f@*erR<8^9 z+qU1>raaiW3bj@ifggXlH60<)cB)@>+F^Z@%<#ZH0@&aKxfbPdsTx?(id%08)`>>N z4yHEADj6^jGmF`a<4mO*lWE|Br3}aT7jeq_P@L_Sj*Ky6k*2I<-(rxzZ6Rmj;!>!+ zQb?)}#ge|~av#iW0&<)@tKuY(e*BHd`rDTibpJp50th1YHTZA-!K((I|Bl=Li<|)d sM40dH1^9Y-aaF&%0Dge|`}Vwj#VB|$r}@_TALCL+LQ%Y0)HvvW0H?o&8UO$Q literal 0 HcmV?d00001 diff --git a/browser-tests/workspace.spec.ts b/browser-tests/workspace.spec.ts new file mode 100644 index 0000000..bcf063c --- /dev/null +++ b/browser-tests/workspace.spec.ts @@ -0,0 +1,87 @@ +import { test, expect } from '@playwright/test'; +import { readFileSync } from 'node:fs'; +import type { Page } from '@playwright/test'; +import AxeBuilder from '@axe-core/playwright'; +import { writeFileSync } from 'node:fs'; + +async function launch(page: Page) { + const { launchUrl } = JSON.parse(readFileSync('.cache/browser-launch.json', 'utf8')); + await page.goto(launchUrl); + await expect(page.getByRole('button', { name: '新建项目', exact: false })).toBeVisible(); +} +async function project(page: Page, name: string) { + await page.getByRole('button', { name: '新建项目', exact: false }).click(); + await page.getByLabel('项目名称', { exact: true }).fill(name); + await page.getByRole('button', { name: '保存项目', exact: true }).click(); + await expect(page.getByLabel('研究问题', { exact: true })).toBeEnabled(); +} +test('desktop research, structured input, denied permission, revisions, citations and replay', async ({ page }) => { + const errors: string[] = []; + page.on('pageerror', error => errors.push(error.message)); + await launch(page); await project(page, '合成经济学与人文研究'); + await page.getByLabel('研究问题', { exact: true }).fill('fixture economics input'); + await page.getByRole('button', { name: '生成计划', exact: false }).click(); + await expect(page.getByRole('button', { name: '确认计划并开始' })).toBeVisible(); + await expect(page.locator('.user-message')).toContainText('fixture economics input'); + await expect(page.locator('.chat-message')).toContainText(['fixture economics input', 'Synthetic fixture plan']); + await page.getByRole('button', { name: '确认计划并开始' }).click(); + await expect(page.getByText('Choose a fixture discipline / 选择测试学科', { exact: true })).toBeVisible(); + await expect(page.locator('.artifact-pane')).toContainText('Synthetic saved draft'); + await page.getByLabel('研究问题', { exact: true }).fill('next unsent draft'); + await expect(page.getByRole('button', { name: '生成计划', exact: false })).toBeDisabled(); + await page.reload(); + await expect(page.getByText('Choose a fixture discipline / 选择测试学科', { exact: true })).toBeVisible(); + await page.getByRole('radio', { name: 'economics', exact: true }).check(); + await page.getByRole('button', { name: '提交回答' }).click(); + await expect(page.getByRole('button', { name: '允许这项操作' })).toBeVisible(); + await page.getByRole('button', { name: '拒绝', exact: true }).click(); + await expect(page.getByText('已完成 · 请审阅证据', { exact: true }).first()).toBeVisible(); + await expect(page.getByRole('button', { name: '有更新的已保存版本 →' })).toBeVisible(); + await page.getByRole('button', { name: '有更新的已保存版本 →' }).click(); + await expect(page.locator('.artifact-pane')).toContainText('Synthetic final report'); + await page.locator('.artifact-pane').getByRole('button', { name: 'Evidence', exact: true }).click(); + await expect(page.locator('.source-detail')).toContainText('Synthetic economics evidence A'); + await expect(page.locator('.source-detail')).toContainText('模型解读未经核实'); + await page.getByRole('button', { name: '查看步骤', exact: false }).first().click(); + await expect(page.locator('.highlighted-step').first()).toBeVisible(); + await page.getByRole('button', { name: '重载对话' }).click(); + await expect(page.locator('.user-message')).toHaveCount(1); + await expect(page.locator('.user-message')).toContainText('fixture economics input'); + const separator = page.getByRole('separator', { name: '调整产物栏宽度' }); + await separator.focus(); await page.keyboard.press('ArrowLeft'); await expect(separator).toHaveAttribute('aria-valuenow', '430'); + const download = page.waitForEvent('download'); await page.getByRole('link', { name: '导出项目' }).click(); + expect((await download).suggestedFilename()).toMatch(/^openlab-.*\.json$/); + await page.screenshot({ path: '.cache/workspace-desktop.png', fullPage: true }); + const accessibility = await new AxeBuilder({ page }).withTags(['wcag2a', 'wcag2aa', 'wcag21aa']).analyze(); + writeFileSync('.cache/accessibility-desktop.json', JSON.stringify(accessibility, null, 2)); + expect(accessibility.violations.map(value => ({ id: value.id, targets: value.nodes.slice(0, 5).map(node => node.target) }))).toEqual([]); + expect(errors).toEqual([]); +}); +test('mobile drawers, language, attachment preview and confirmed cancellation', async ({ page }) => { + await page.setViewportSize({ width: 390, height: 844 }); + await page.goto(JSON.parse(readFileSync('.cache/browser-launch.json', 'utf8')).launchUrl); + await page.getByRole('button', { name: '项目', exact: true }).click(); + await page.getByRole('button', { name: '新建项目', exact: false }).click(); + await page.getByLabel('项目名称', { exact: true }).fill('移动端合成研究'); + await page.getByRole('button', { name: '保存项目', exact: true }).click(); + await expect(page.getByLabel('研究问题', { exact: true })).toBeEnabled(); + await page.locator('input[type=file]').first().setInputFiles({ name: '中文附件.md', mimeType: 'text/markdown', buffer: Buffer.from('# 合成附件\n\nUTF-8 text evidence.') }); + await page.getByRole('button', { name: '产物', exact: true }).click(); + await page.getByRole('tab', { name: '附件', exact: false }).click(); + await page.getByRole('button', { name: '中文附件.md', exact: false }).click(); + await expect(page.locator('.drawer-artifacts')).toContainText('UTF-8 text evidence.'); + await page.keyboard.press('Escape'); + await expect(page.getByRole('dialog')).toHaveCount(0); + await page.getByLabel('研究问题', { exact: true }).fill('cancellation fixture'); + await page.getByRole('button', { name: '生成计划', exact: false }).click(); + await expect(page.getByRole('button', { name: '确认计划并开始' })).toBeVisible(); + await page.getByRole('button', { name: '停止运行', exact: true }).click(); + await expect(page.getByText('已取消 · 进程已停止', { exact: true }).first()).toBeVisible(); + await page.getByRole('button', { name: 'EN', exact: true }).click(); + await expect(page.getByRole('button', { name: 'Create plan', exact: false })).toBeVisible(); + await page.screenshot({ path: '.cache/workspace-mobile.png', fullPage: true }); + const accessibility = await new AxeBuilder({ page }).withTags(['wcag2a', 'wcag2aa', 'wcag21aa']).analyze(); + writeFileSync('.cache/accessibility-mobile.json', JSON.stringify(accessibility, null, 2)); + expect(accessibility.violations.map(value => ({ id: value.id, targets: value.nodes.slice(0, 5).map(node => node.target) }))).toEqual([]); + expect(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth)).toBe(true); +}); diff --git a/docs/architecture.md b/docs/architecture.md index d510e52..d567cb8 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -2,6 +2,8 @@ Status: accepted design for implementation, updated 2026-10-24. No application service, database, runtime adapter, or sandbox ships in v0.0.1. See [product behavior](product.md), [workspace](workspace.md), [Harness](harness.md), and the current [decision record](decisions/0002-workspace-and-harness.md). +Current development implements the local service, archive, supervised Harness and browser workspace. See [development](development.md), [OpenAPI](openapi.json) and [verification](verification.md) for exact scope; descriptions below retain the target contracts and do not establish untested guarantees. + ## Deployment and component boundaries Use a modular local service that serves the browser UI and platform API from one loopback origin. Keep persistence and execution behind the service. Start with one process for the control plane and a separately supervised agent runtime; do not introduce a distributed scheduler or message broker. @@ -137,6 +139,6 @@ Initial retrieval adapters target public scholarly metadata APIs (Crossref and O - Prevent global user skills, hooks and configuration from leaking into a managed execution. Validate this behavior against the pinned runtime before declaring the integration executable. - Confine project file operations through platform tools; disable arbitrary shell access in the literature release. Inspect resolved paths and links before reading, writing, or exporting files. - Treat retrieved text as evidence, not authority to change policy. Tool permissions come from the platform/user, not a downloaded Skill or paper. -- Project files and research records must remain usable if the runtime or UI library is removed. Export/import tests, a second runtime test double, a substitute model and an independent AG-UI consumer are required evidence for that guarantee; none has been implemented yet. +- Project files and research records must remain usable if the runtime or UI library is removed. Export/import tests, a second runtime test double, a substitute model and an independent AG-UI consumer provide current development evidence for that boundary; detailed coverage and limits are in [verification](verification.md). Runtime log and research-record retention follow the separate [logging and provenance specification](logging.md). These application guarantees require implementation tests before being presented as shipped behavior. diff --git a/docs/development.md b/docs/development.md index d4df258..04df47c 100644 --- a/docs/development.md +++ b/docs/development.md @@ -1,6 +1,6 @@ # Application development -The v0.0.1 release remains foundation-only. Current source includes a platform archive, generic Harness, supervised worker and local research API. There is no browser workspace build or installer yet. Follow the [implementation plan](implementation-plan.md) for remaining gates. +The v0.0.1 release remains foundation-only. Current source includes the React/assistant-ui research workspace, platform archive, generic Harness, supervised worker and local research API. There is no installer yet. Follow the [implementation plan](implementation-plan.md) for remaining gates. ## Run checks @@ -9,10 +9,14 @@ Use Node.js 24 and the committed npm lockfile: ```sh npm ci npm run check +npm run build +npm run test:browser ``` The check includes strict TypeScript validation, documentation validation and Node's test runner. Tests use temporary directories, controlled model responses and a substitute runtime; they require no provider credential or network service. Native Windows tests cover Unicode/space-containing paths and a separately spawned worker. GitHub CI runs the same checks on Windows and Linux. +Browser acceptance uses an installed Chrome through Playwright. Set `OPENLAB_BROWSER_CHANNEL` to another installed Chromium channel if necessary. The synthetic server uses a separate temporary data root and no provider credential. Tests cover desktop and 390-pixel drawers, plan confirmation, structured questions, denied access, report revisions, source/step links, conversation replay, cancellation, UTF-8 attachments and English/Chinese interaction. Axe checks the exercised screens for WCAG A/AA issues; this is automated evidence, not a complete accessibility certification. + ## Implemented core - `app/contracts.ts` defines platform and replaceable model/tool/runtime boundaries. @@ -28,12 +32,14 @@ SQLite schema version 1 is the first development schema; no migration from a shi `npm run test:live-model` uses `OPENLAB_SMOKE_API_KEY` to test DeepSeek Flash Chinese text streaming and a structured tool call. Run it explicitly; ordinary tests and CI do not use network credentials. The script prints only test results, public endpoint/model information and usage. It writes the same sanitized record to the ignored `.cache/live-model-smoke.json`. -On Windows, keep credentials encrypted with user-scoped Windows protection and pass the decrypted value only in the launching process environment. Do not put credentials in commands committed to the repository, project metadata, exports or test fixtures. The application credential/settings interface remains a later implementation item. +On Windows, keep credentials encrypted with user-scoped Windows protection and pass the decrypted value only in the launching process environment. Do not put credentials in commands committed to the repository, project metadata, exports or test fixtures. The settings UI supplies protected provider creation, removal and capability checks. The Windows helper loads the system PowerShell module path explicitly, independently of a PowerShell Core host. The current DeepSeek configuration uses non-thinking mode; replay of private reasoning state is not implemented. ## Local research API `npm start` starts the service on an available loopback port and prints a per-launch browser link. `OPENLAB_DATA_DIR` selects the local data/configuration root (default `.openlab`); `OPENLAB_PORT` selects a fixed port when needed. Projects are under the root's `projects` directory; protected provider settings and inert skill installations remain outside exported projects. Stop with Ctrl+C to cancel active workers before closing storage. +Build the browser bundle first with `npm run build`. A data-root ownership file prevents two control planes from modifying the same archive concurrently. A stale owner is reclaimed only after its process is absent. Saved records remain available after restart without executing previous decisions or model calls. + The launch link's fragment is exchanged for an HttpOnly, SameSite session cookie. API mutations require the matching origin and current `x-openlab-csrf` token. Host and origin checks reject remote origins. The bootstrap/session tokens rotate on every start. The service has no externally accessible listener or hosted account. The initial API is described in [OpenAPI JSON](openapi.json), also served at authenticated `/api/v1/openapi`. Add providers through `/api/v1/providers`, then use their test operation to demonstrate streaming and structured calls before creating runs. Credentials use Windows user-scoped protection; persistent credential configuration is currently Windows-only. No plaintext persistence fallback is provided on Linux. Provider probes and plan generation consume model tokens. Crossref metadata retrieval requires internet access; it requires no provider key. The selected model and skill revision are frozen into each run's public configuration. @@ -46,6 +52,10 @@ Install a public Git skill repository by exact 40-character commit through `/api The AG-UI endpoint attaches to an already created run. It validates the route's run/thread identities and ignores client-supplied tools/state for execution purposes. SSE replay is read-only. Domain event cursors use canonical sequence numbers; AG-UI cursors use `sequence:projection-index` so reconnecting during a projected multi-event record does not lose its remaining events. Stream closure never starts, retries or cancels work. +The official assistant-ui adapter receives a platform-bound `HttpAgent`; its transport can only attach to the selected existing run. Remounting the conversation replays a fresh view rather than duplicating model work. Its local subscription abort is not a platform cancel. Decisions and explicit cancellation use separate platform commands. Sources and exact artifact revisions stay outside the UI library. Report Markdown ignores active HTML and external image embedding. + +Attachments are copied, hashed and exported with the project. Preview UTF-8 text/Markdown and PNG/JPEG, while other formats remain inert downloads. A run snapshots the selected material identities; saved attachment bytes are checked again before use. The current upload limit is 10 MB per material and the JSON import request limit is 32 MB. The model receives bounded text excerpts; images are preview/download material in this version, not vision model input. Public source content and model interpretations retain separate evidence labels. + ## Remaining application gates -The React/assistant-ui workspace, attachments, a second scholarly provider and full application acceptance remain pending. Browser compatibility and successful public full-text access have not been established. Fixture tests and the live metadata probes do not establish completion of those gates. See [verification](verification.md) for recorded outcomes. +A second scholarly provider, successful public full-text access on this machine and final release acceptance remain pending. Current native Chrome evidence and live metadata tasks do not establish compatibility with every browser, model or scholarly provider. See [verification](verification.md) for recorded outcomes. diff --git a/docs/implementation-plan.md b/docs/implementation-plan.md index d797d9c..abcfb46 100644 --- a/docs/implementation-plan.md +++ b/docs/implementation-plan.md @@ -18,6 +18,6 @@ Each iteration uses a short-lived branch, local checks, a PR, passing required C | --- | --- | | Design baseline | PR #6 merged; Windows/Linux required CI passed | | Platform and Harness | PR #7 merged; 28 tests, type checking, Gitleaks and Windows/Linux CI passed; live DeepSeek streaming/tool calls passed | -| Services and adapters | Implemented local API, settings, Crossref retrieval, skill installation and AG-UI projection; 38 tests passed on native Windows; two live metadata research/export/import tasks passed; PR checks pending | -| Research workspace | Pending | +| Services and adapters | PR #8 merged; 38 native Windows tests and required Windows/Linux CI passed; two live metadata research/export/import tasks passed | +| Research workspace | Implemented React/assistant-ui and official AG-UI adapter, project/history navigation, decisions, reports, sources, attachments and bilingual settings; 42 unit/integration tests and two desktop/mobile browser scenarios passed; exercised axe A/AA checks report no violations; a real browser/DeepSeek/Crossref loop passed; PR checks pending | | Application acceptance | Pending | diff --git a/docs/openapi.json b/docs/openapi.json index 80895f1..0503936 100644 --- a/docs/openapi.json +++ b/docs/openapi.json @@ -231,6 +231,78 @@ } } }, + "/retrieval/test": { + "post": { + "summary": "Check the fixed metadata provider without starting research", + "security": [ + { + "LocalSession": [] + } + ], + "responses": { + "200": { + "description": "Persisted platform result", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, + "201": { + "description": "Persisted platform result", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, + "400": { + "description": "Persisted platform result", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "401": { + "description": "Persisted platform result", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "403": { + "description": "Persisted platform result", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "409": { + "description": "Persisted platform result", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + } + }, "/projects": { "get": { "summary": "List local projects", @@ -671,6 +743,146 @@ } ] }, + "/projects/{projectId}/materials": { + "post": { + "summary": "Copy an inert attachment into project storage (10 MB maximum)", + "security": [ + { + "LocalSession": [] + } + ], + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string" + }, + "base64": { + "type": "string" + } + }, + "required": [ + "name", + "base64" + ], + "additionalProperties": false + } + } + } + }, + "responses": { + "200": { + "description": "Persisted platform result", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, + "201": { + "description": "Persisted platform result", + "content": { + "application/json": { + "schema": { + "type": "object" + } + } + } + }, + "400": { + "description": "Persisted platform result", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "401": { + "description": "Persisted platform result", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "403": { + "description": "Persisted platform result", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + }, + "409": { + "description": "Persisted platform result", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/Error" + } + } + } + } + } + }, + "parameters": [ + { + "in": "path", + "name": "projectId", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ] + }, + "/materials/{materialId}": { + "get": { + "summary": "Download checksum-verified original attachment bytes", + "security": [ + { + "LocalSession": [] + } + ], + "responses": { + "200": { + "description": "Original bytes; Content-Disposition attachment", + "content": { + "application/octet-stream": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + } + } + }, + "parameters": [ + { + "in": "path", + "name": "materialId", + "required": true, + "schema": { + "type": "string", + "format": "uuid" + } + } + ] + }, "/projects/{projectId}/export": { "get": { "summary": "Download a versioned portable research archive", diff --git a/docs/product.md b/docs/product.md index e35a56e..c297524 100644 --- a/docs/product.md +++ b/docs/product.md @@ -68,4 +68,4 @@ Supervisor-Skills is the initial reference case. Preserve its original directory The application acceptance suite must cover an economics question and a humanities/text-research question, an empty retrieval result, contradictory evidence, an inaccessible full text, invalid credentials, a cancelled run, restart after interruption, offline project viewing and export/import integrity. Check both Chinese and English interactions. Use controlled fixtures for repeatable automation and a separately recorded live retrieval smoke test. -Also cover live artifact updates, exact-revision and citation/step navigation, structured answers, denied permissions, stale/duplicate decisions, cancellation races, connection replay without duplicate work, and keyboard-accessible desktop/drawer layouts. Prove the adapter boundary with a second runtime test double, a substitute model provider and an independent AG-UI consumer. Archives must remain readable/exportable without the original runtime or assistant-ui. Detailed outcomes are in the [workspace acceptance matrix](workspace.md#application-acceptance-scenarios); all remain unimplemented application gates. +Also cover live artifact updates, exact-revision and citation/step navigation, structured answers, denied permissions, stale/duplicate decisions, cancellation races, connection replay without duplicate work, and keyboard-accessible desktop/drawer layouts. Prove the adapter boundary with a second runtime test double, a substitute model provider and an independent AG-UI consumer. Archives must remain readable/exportable without the original runtime or assistant-ui. Detailed requirements are in the [workspace acceptance matrix](workspace.md#application-acceptance-scenarios); current implementation evidence and remaining gates are in [verification](verification.md) and the [implementation plan](implementation-plan.md). diff --git a/docs/verification.md b/docs/verification.md index f85b031..947622f 100644 --- a/docs/verification.md +++ b/docs/verification.md @@ -30,6 +30,14 @@ Two live DeepSeek Flash/Crossref tasks completed: minimum wage/employment in Eng The local network resolves external names to reserved VPN fake DNS ranges. General source reads correctly reject those ranges. The fixed Crossref metadata adapter uses HTTPS with redirects disabled and successfully retrieved its records. This is an environment-specific full-text limitation, not a reason to expand arbitrary network access. +### Browser workspace, 2026-10-24 + +On native Windows x64 with Node.js 24.19.0 and Chrome 155.0.8059.85, 42 unit/integration tests, strict server/browser TypeScript and the production build passed. Two synthetic Playwright scenarios passed desktop and 390-pixel flows: planning, a structured answer, denied permission, live draft/final revisions, citation and producing-step navigation, conversation reload without duplicate text, cancellation, UTF-8 material preview and Chinese/English controls. Axe's exercised WCAG A/AA checks reported zero violations after fixing contrast and upload-control labels. Screenshots were inspected locally. This automated check is not a complete accessibility certification. + +A separate real browser session tested the protected DeepSeek provider and Crossref diagnostics through settings, created a Chinese economics project, confirmed its plan, retrieved five metadata records, saved an immutable report and downloaded the portable archive. No frontend exceptions were recorded. The README screenshot is from that task. The sanitized record is `.cache/live-browser-record.json`, with a local research export; it contains no provider credential. Full-text and claim verification were explicitly excluded from that task. + +The native credential helper initially failed because the PowerShell Core host's module path reached Windows PowerShell. The helper now selects the Windows system executable and system module path explicitly; a real DPAPI round-trip test passes. The local service then started and the browser/provider task completed. Data-root ownership, imported reference checks, attachment integrity and private-configuration rejection have separate regression tests. + ### Initial application core, 2026-10-24 On native Windows x64 with Node.js 24.19.0, strict TypeScript validation and 28 tests passed (19 application-core tests and nine governance tests). Core cases include confirmation before dispatch, substitute model/tool/context/sink and RuntimeAdapter implementations, validated questions and denied permission, duplicate/stale decisions, usage/turn limits, worker cancellation, restart reconciliation, SQLite transaction rollback, Unicode/space-containing paths, junction rejection, immutable revisions, corrupted archive rejection and offline export/import. These do not establish browser UI or full literature research acceptance. @@ -53,4 +61,4 @@ The implementation is accepted when the foundation PR passes required checks and Static validation checks local document links, YAML structure and Issue Form fields. GitHub's rendering and form behavior should also be inspected on the published default branch. Badges and remote resources depend on their services and are not fetched during PR lint. -The foundation release and historical documentation revision above supplied no application execution. Current development core verification is recorded separately above. A complete research application, scientific result, sandbox, diagnostic bundle, assistant-ui/AG-UI compatibility, full-text verification, retrieval reliability and Supervisor-Skills licensing resolution remain explicit application gates in the [product](product.md), [architecture](architecture.md), [Harness](harness.md), and [third-party record](../THIRD_PARTY_NOTICES.md). +The foundation release and historical documentation revision above supplied no application execution. Current development's native core/browser and live metadata evidence is recorded separately above. This does not establish a verified scientific result, a sandbox, full-text access on this network, compatibility with every browser/provider, or Supervisor-Skills redistribution permission. Remaining release gates are tracked in the [implementation plan](implementation-plan.md); deferred capabilities and third-party terms remain in the [product](product.md) and [third-party record](../THIRD_PARTY_NOTICES.md). diff --git a/docs/workspace.md b/docs/workspace.md index fae38c2..d421d80 100644 --- a/docs/workspace.md +++ b/docs/workspace.md @@ -1,6 +1,6 @@ # Research workspace specification -Status: accepted implementation design, updated 2026-10-24. No workspace application ships in v0.0.1. This document defines the first UI against the [product specification](product.md), [platform contracts](architecture.md), and [Harness specification](harness.md). +Status: accepted implementation design, updated 2026-10-24. No workspace application ships in historical v0.0.1; current development implementation and native browser evidence are described in [development](development.md) and [verification](verification.md). This document defines the first UI against the [product specification](product.md), [platform contracts](architecture.md), and [Harness specification](harness.md). ## Research work stays visible @@ -76,7 +76,7 @@ All core controls require keyboard operation and accessible labels. Restore focu ## Application acceptance scenarios -These are future implementation gates, not tests passed by this documentation change. +These are application acceptance requirements. The [verification record](verification.md) distinguishes implemented and tested cases from remaining gates; a design table alone is not evidence of completion. | Scenario | Required outcome | | --- | --- | diff --git a/package-lock.json b/package-lock.json index 1a785ea..5f3bffb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,54 +8,2169 @@ "name": "openlab-repository", "version": "0.0.1", "license": "Apache-2.0", + "dependencies": { + "@ag-ui/client": "1.0.3", + "@ag-ui/core": "1.0.3", + "@assistant-ui/react": "0.15.31", + "@assistant-ui/react-ag-ui": "0.1.0", + "react": "19.3.1", + "react-dom": "19.3.1", + "react-markdown": "10.1.0", + "yaml": "2.9.1" + }, "devDependencies": { + "@axe-core/playwright": "4.14.0", + "@playwright/test": "1.64.1", "@types/node": "24.19.2", + "@types/react": "19.3.0", + "@types/react-dom": "19.3.0", + "@vitejs/plugin-react": "6.1.2", "markdownlint-cli2": "0.24.0", "typescript": "7.0.2", - "yaml": "2.9.1" + "vite": "8.3.5" + }, + "engines": { + "node": ">=24 <25" + } + }, + "node_modules/@ag-ui/client": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@ag-ui/client/-/client-1.0.3.tgz", + "integrity": "sha512-pqp6hEiO+gjAKIBSA/g8SzPy17XHZhbKZFK3Q4DhZ7bNLR34xNpPfXogP3bSQrzsKh2hmSc9ZYUutTpe/BQBpw==", + "license": "MIT", + "dependencies": { + "@ag-ui/core": "1.0.3", + "@ag-ui/encoder": "1.0.3", + "@ag-ui/proto": "1.0.3", + "@types/uuid": "^10.0.0", + "compare-versions": "^6.1.1", + "fast-json-patch": "^3.1.1", + "rxjs": "7.8.1", + "untruncate-json": "^0.0.1", + "uuid": "^11.1.0", + "zod": "^3.25.76" + } + }, + "node_modules/@ag-ui/core": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@ag-ui/core/-/core-1.0.3.tgz", + "integrity": "sha512-HdMdZPOqworKlnzLaNktnbNYgIzOPbzt5ow6J4bPhbmHCdpWpCSZQFgkifQkn1zVHgCK1V+pmKc8xtW6FsQZKA==", + "license": "MIT", + "peerDependencies": { + "zod": "^3.25.18 || ^4.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/@ag-ui/encoder": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@ag-ui/encoder/-/encoder-1.0.3.tgz", + "integrity": "sha512-Uj/znRLejAQ7PWcY/Qio2ucymbJNuiDMKU34aTQV+v0EeB6Up9vj6nHNtH+eAAVSVZADs4/U+8gXVnN0on51ew==", + "license": "MIT", + "dependencies": { + "@ag-ui/core": "1.0.3", + "@ag-ui/proto": "1.0.3" + } + }, + "node_modules/@ag-ui/proto": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@ag-ui/proto/-/proto-1.0.3.tgz", + "integrity": "sha512-L3YVEJN1hCYx3P7Zfrf4e1obA1pwzykkkPZkNHAdcXyUZ05/ezvWCkErnfNoUlER8QCasBwRjKiDjicu3Tt7+g==", + "license": "MIT", + "dependencies": { + "@ag-ui/core": "1.0.3", + "@bufbuild/protobuf": "^2.2.5", + "@protobuf-ts/protoc": "^2.11.1", + "zod": "^3.25.76" + } + }, + "node_modules/@assistant-ui/core": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@assistant-ui/core/-/core-0.3.31.tgz", + "integrity": "sha512-NrTSgdUTM0SVmos3YMAAvPqDDNlxFmwVTjnA06T+4A7DcXswsKrBw65BsfSEn4m1DFSwaD3rfMlIFTEK3ei4uQ==", + "license": "MIT", + "dependencies": { + "assistant-cloud": "^0.2.8", + "assistant-stream": "^0.3.54", + "nanoid": "^6.0.2" + }, + "peerDependencies": { + "@assistant-ui/store": "^0.3.22", + "@assistant-ui/tap": "^0.9.24", + "@types/react": "*", + "react": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/@assistant-ui/react": { + "version": "0.15.31", + "resolved": "https://registry.npmjs.org/@assistant-ui/react/-/react-0.15.31.tgz", + "integrity": "sha512-vTTcI2963DO5CYrKh8CYkJ0stQcu0JXxUQM0nepsP2Zy4QfrovVuiYkAhKM1U4jMnGHy3sQ9ovxH0VTZrScnZg==", + "license": "MIT", + "dependencies": { + "@assistant-ui/core": "^0.3.31", + "@assistant-ui/store": "^0.3.22", + "@assistant-ui/tap": "^0.9.24", + "assistant-cloud": "^0.2.8", + "assistant-stream": "^0.3.54", + "radix-ui": "^1.8.1", + "react-textarea-autosize": "^8.5.9", + "safe-content-frame": "^0.0.34", + "zod": "^4.6.7", + "zustand": "^5.0.15" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^18 || ^19", + "react-dom": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@assistant-ui/react-ag-ui": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@assistant-ui/react-ag-ui/-/react-ag-ui-0.1.0.tgz", + "integrity": "sha512-mV+rFFMKyjhNgN87q6E4XC6YbtbDqZDng3VqdyPAi02wxIWYMcP1ILANWLsEC6PP/Ac7UTfeHfF58W38BFBRrw==", + "license": "MIT", + "dependencies": { + "@ag-ui/client": "^1.0.3", + "@assistant-ui/core": "^0.3.31", + "@assistant-ui/generative-ui": "^0.0.7", + "@assistant-ui/store": "^0.3.22", + "assistant-stream": "^0.3.54", + "fast-json-patch": "^3.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@assistant-ui/react-ag-ui/node_modules/@assistant-ui/generative-ui": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@assistant-ui/generative-ui/-/generative-ui-0.0.7.tgz", + "integrity": "sha512-3+t5CjRN4NqfN8Oet1mQEJWKibBECvy+OylqCDJO7VqqMwvr12ThDNv7iD/J6Cvw8hUx8d/vWSQdTkLrzoTArw==", + "license": "MIT", + "dependencies": { + "@types/json-schema": "^7.0.15", + "assistant-stream": "^0.3.54" + }, + "peerDependencies": { + "@assistant-ui/react": "^0.15.0", + "@types/react": "*", + "generative-frame": "^0.0.8", + "react": "^18 || ^19", + "react-native": "*", + "vue": "^3.5.0", + "zod": "^4.0.0" + }, + "peerDependenciesMeta": { + "@assistant-ui/react": { + "optional": true + }, + "@types/react": { + "optional": true + }, + "generative-frame": { + "optional": true + }, + "react": { + "optional": true + }, + "react-native": { + "optional": true + }, + "vue": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, + "node_modules/@assistant-ui/store": { + "version": "0.3.22", + "resolved": "https://registry.npmjs.org/@assistant-ui/store/-/store-0.3.22.tgz", + "integrity": "sha512-aXp4m42mmtZOQUE0lzxJ8Z9EZKknOWBk+DDg1Pj2gNhhBGYXmfrScYD1N1e/9/8wTKSwL0g4z2fSZqr+5fWbxg==", + "license": "MIT", + "peerDependencies": { + "@assistant-ui/tap": "^0.9.24", + "@types/react": "*", + "react": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/@assistant-ui/tap": { + "version": "0.9.24", + "resolved": "https://registry.npmjs.org/@assistant-ui/tap/-/tap-0.9.24.tgz", + "integrity": "sha512-6O0hf2HkKbrylIwNlFUimHf2V/6Gq5UXF0y0r2Fbw5wSvvO8ZDmv0AEkMwwygSfJgbpnQgiC8Xs+2VFaeO0RBw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^18 || ^19" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "react": { + "optional": true + } + } + }, + "node_modules/@axe-core/playwright": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@axe-core/playwright/-/playwright-4.14.0.tgz", + "integrity": "sha512-SGUhG5PIHd2eMdpgBF2PyQLOhX+qyDR2PT4cmoS4Qk2ak+jX/rrSvst9l0PGv5WTHolKpUMvVmoHpr4cZgf1RQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "axe-core": "~4.14.0" + }, + "engines": { + "node": ">=22.12.0" + }, + "peerDependencies": { + "playwright-core": ">= 1.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.10.tgz", + "integrity": "sha512-NpugTzbKgGrNe45Ui32cN8vCFFMxVtP/9rtfwv0nP5FRDAjvYBxJcLVpYA9mr1jxP6cnIT7x9LUzuDTbWYYNRA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bufbuild/protobuf": { + "version": "2.16.0", + "resolved": "https://registry.npmjs.org/@bufbuild/protobuf/-/protobuf-2.16.0.tgz", + "integrity": "sha512-FWa0sPlqYGJgpTs6OxBRcL/AW4JT0OIO+W1ajerluoTVh8CV0B7XM1qsNCRgnkSCkRCgOICKHRQlY17jrsm0+Q==", + "license": "(Apache-2.0 AND BSD-3-Clause)" + }, + "node_modules/@floating-ui/core": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.8.0.tgz", + "integrity": "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.12" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.8.0.tgz", + "integrity": "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.8.0", + "@floating-ui/utils": "^0.2.12" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.9", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.9.tgz", + "integrity": "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.8.0" }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.12", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.12.tgz", + "integrity": "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==", + "license": "MIT" + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.155.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.155.0.tgz", + "integrity": "sha512-SeE8JPwW1xs1tHeNycjUtUBdhorRa8o4jlvjwk49R7/Z291i+4DQa7JJIgOm/TaYQZx+QrPQbVJ/9c8dMIZN8Q==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/oxc-project" + } + }, + "node_modules/@playwright/test": { + "version": "1.64.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.64.1.tgz", + "integrity": "sha512-IaYGml/nSOiZWROfPtuIL7neuu9qDOZ3TNvNljUIrhDMxiGTgPjzj/g9p9RxpS2tSepPDRY+UwAaEeiKtUvIAg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.64.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@protobuf-ts/protoc": { + "version": "2.11.1", + "resolved": "https://registry.npmjs.org/@protobuf-ts/protoc/-/protoc-2.11.1.tgz", + "integrity": "sha512-mUZJaV0daGO6HUX90o/atzQ6A7bbN2RSuHtdwo8SSF2Qoe3zHwa4IHyCN1evftTeHfLmdz+45qo47sL+5P8nyg==", + "license": "Apache-2.0", + "bin": { + "protoc": "protoc.js" + } + }, + "node_modules/@radix-ui/number": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.3.tgz", + "integrity": "sha512-Road2bidD0uu/1BGDOWNdPI06g0lIRy6IF9GZcIrDK2KGItfor8IQwQa+yM2ERgHM1MmHxaxpTzk0/Jp42lNfA==", + "license": "MIT" + }, + "node_modules/@radix-ui/primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.2.0.tgz", + "integrity": "sha512-KABY+VMIRPJL+Vyzv/335FEhXXD/xDv8BMb1kl48/aXJZMH0LtHCTx5ga5RnxaEP3nZHtmTLf1P9vanmFX46bw==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-accessible-icon": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accessible-icon/-/react-accessible-icon-1.2.0.tgz", + "integrity": "sha512-BB4+HHkFl3HipEAhm/mLhrpXV+2kRX7MvY4AaImUcQVeWQt5KhV1JEByNs8NGfJOcr95RcQGijt9+OrCo68xGA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-visually-hidden": "1.2.13" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-accordion": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.3.1.tgz", + "integrity": "sha512-lCZ6sbGS/7WO0j84yx4srIgOf0MRJ/sZ13RonADXxvKBym8xrxNCq5f6DibGcr38sIlwPk8/52uYdXeOPsbEDw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-collapsible": "1.2.1", + "@radix-ui/react-collection": "1.2.0", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-direction": "1.2.0", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-use-controllable-state": "1.3.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-alert-dialog": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.2.1.tgz", + "integrity": "sha512-MWC4N7RY+qsbNrFNo7yQ9ZbdoZEW+B5DtpWxJfUnVCqcQxxqqg3qis3RMPTDbCNaTIdHJSn5XDkl9ZdVWXknLg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-dialog": "1.3.1", + "@radix-ui/react-primitive": "2.2.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.17.tgz", + "integrity": "sha512-67jjcVvxjXCCStnTW3Z+M7jmaFMuMR7M2JgpDzH4w7JSRrikmzhjfYgP/09ECb8n7whnsgh1ifJ+Ie96qEj+pQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.2.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-aspect-ratio": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-aspect-ratio/-/react-aspect-ratio-1.2.0.tgz", + "integrity": "sha512-gHEU+Lx8DlT/FYIjZQvvBJrFSwUngXTjh7FeL9QvJaNALDLez6pgDj2axCUihBjtPBy82LN0gNb25+sTcSXNmA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.2.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-avatar": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.3.1.tgz", + "integrity": "sha512-ssZOkfQNkFpYVmCPQlr978xSaU7moSrGXdwg4j2Xs2WlGl/1KsSZtfpK+WnLnx0Rwll1BAh6oUrAy/uR31SBvQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-use-callback-ref": "1.1.5", + "@radix-ui/react-use-is-hydrated": "0.1.3", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.4.1.tgz", + "integrity": "sha512-KWtwhh+98ML3seZ9vO9yRY2OwaQG90dCiDAxjpSNY2AaZ3Hik3B0dsk6m0ILywo/no3A9/SozadyPMNweVT8yw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-presence": "1.1.11", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-use-controllable-state": "1.3.0", + "@radix-ui/react-use-size": "1.1.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collapsible": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.2.1.tgz", + "integrity": "sha512-fSzLb/qy1wuZSxQBcxpW0hNXYHDMWiIXDGyJGdA4+FSLknOWZsNRrTCUavxb4PI8b8lvHo/CysO6NVi5zLpo5g==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-presence": "1.1.11", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-use-controllable-state": "1.3.0", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.2.0.tgz", + "integrity": "sha512-+yiQzqcc7Sy7/5s0jrMCjpPRFB3538VNrXPWdTLZhE/SCfgEws4aI6lHWN/vvcnyhHndROWBzqmqB4QBrITZ9Q==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-slot": "1.5.0", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.5.tgz", + "integrity": "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.3.0.tgz", + "integrity": "sha512-v5AYEocMISkbJ+1fTXzZpiFv7NRr3D2WiYbLIKf+FkmJ8VkP6d503eV2n34MFussHXY1pcoq3yfJiQH77dc4Mg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context-menu": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context-menu/-/react-context-menu-2.4.1.tgz", + "integrity": "sha512-Pddn3Z8lio3/ChxyXaEP3jHTTo1LTHCWwF8+mG1chQlWijP+ttVHBKwezrQTiV6Mj670ojvumqvuGh+mdv33iQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-menu": "2.2.1", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-use-controllable-state": "1.3.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.3.1.tgz", + "integrity": "sha512-+0E4qvVTwjAWra9sEhJ+9BTsvHE00zkUuNw1VGh7S+Gc13dfYk+S1PVQdchWDj+XYHhAai7Z7IpzXVDCGsDplg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-dismissable-layer": "1.2.1", + "@radix-ui/react-focus-guards": "1.2.0", + "@radix-ui/react-focus-scope": "1.3.1", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-portal": "1.2.0", + "@radix-ui/react-presence": "1.1.11", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-slot": "1.5.0", + "@radix-ui/react-use-controllable-state": "1.3.0", + "@radix-ui/react-use-layout-effect": "1.1.4", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.7.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.2.0.tgz", + "integrity": "sha512-FtRA52a/r+LKStFT+neLUm+8vSe7wbzkbQGGeU8ixFiwC8NU14Evoq9ATECqL3eGbcUzJR4fyfA4KkK/q6dfqg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.2.1.tgz", + "integrity": "sha512-KIfDcAsx+oVylFZ4xryVKusJUrLLhTQmfnJW4kwk4Km5myvhHCrj04hnWWUC166+xD4DHdH8tQGylwYX1h6+Pg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-use-callback-ref": "1.1.5", + "@radix-ui/react-use-effect-event": "0.0.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.2.1.tgz", + "integrity": "sha512-XsHcmYG2AcqnvTqrOXXWcMrhquEIlqucizuDH1MV/qMRW+DndpBCMvNeTRXOxqmG1H5j89M/+I0/34Zdd+zDcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-menu": "2.2.1", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-use-controllable-state": "1.3.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.2.0.tgz", + "integrity": "sha512-MPm5R0iRGUDrDnPNxmLBkv+X7pN4XKzt+MbWJEuJQsH3NFH4omrbpXpucyYCCKTSt+na/ZxGhEWQrWTZ0285cA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.3.1.tgz", + "integrity": "sha512-ugeMhbXt4y5S2T1GnIqbWDKEN962PDwnX1fN0eKOOgf5f63Dh2mn8F1FUySlgW0iF6GiVsRF32DzuDIWTUyQJg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-use-callback-ref": "1.1.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-form": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-form/-/react-form-0.3.0.tgz", + "integrity": "sha512-KnmrCM27m7Jo1R8S6cFDPrEeq20Jfn5dqde0SZrJr2lVXlj6/QZSUCyFoYh1d8ti0uUrMNK5Lx6dUDuINT+f4Q==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-label": "2.1.17", + "@radix-ui/react-primitive": "2.2.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-hover-card": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.2.1.tgz", + "integrity": "sha512-4QooHxgmpCKRjj1Pft+hETKidYcrbPKcVwMGtk2Oa5DcWY2IH1fHuQt9LG6q5hUQBBV2MhQxSvtx6B/C1xfiTw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-dismissable-layer": "1.2.1", + "@radix-ui/react-focus-scope": "1.3.1", + "@radix-ui/react-popper": "1.4.0", + "@radix-ui/react-portal": "1.2.0", + "@radix-ui/react-presence": "1.1.11", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-use-controllable-state": "1.3.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.4.tgz", + "integrity": "sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.17", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.17.tgz", + "integrity": "sha512-xmj7MN/drx1oUAXkfiFIvyDmGNUIN/omGobvA5oAoNqu9zO2MkISYGZOkBcqSzs/UZXXwV+/3ACRE7q7mGcmkA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.2.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.2.1.tgz", + "integrity": "sha512-G34BsU90SBj5foYpz8pQqq/Q0Dlfyyl6KitWEfRwXC0QvfwhWNAwS49DWP2+rRJ0iwv20Z2Qx7Sk+T03bGo5JA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-collection": "1.2.0", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-direction": "1.2.0", + "@radix-ui/react-dismissable-layer": "1.2.1", + "@radix-ui/react-focus-guards": "1.2.0", + "@radix-ui/react-focus-scope": "1.3.1", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-popper": "1.4.0", + "@radix-ui/react-portal": "1.2.0", + "@radix-ui/react-presence": "1.1.11", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-roving-focus": "1.2.1", + "@radix-ui/react-slot": "1.5.0", + "@radix-ui/react-use-callback-ref": "1.1.5", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.7.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menubar/-/react-menubar-1.2.1.tgz", + "integrity": "sha512-bRTYtS9JITV2dUYqYZb7+N5SooQBkZrNRejfmyrSKe3Y57aXhKgx//kBUP0kY7pEpsWR/U1AHsAJRzjwCrK3PA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-collection": "1.2.0", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-direction": "1.2.0", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-menu": "2.2.1", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-roving-focus": "1.2.1", + "@radix-ui/react-use-controllable-state": "1.3.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-navigation-menu": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.4.1.tgz", + "integrity": "sha512-XPPZjkeyCSMNNvt0FyDv/PUauG7Ow4GwHQ/XZSwW36F/TS7rR4HFTsXH8ewLnXTOLn3x96/pmTPNQIVoTX/86g==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-collection": "1.2.0", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-direction": "1.2.0", + "@radix-ui/react-dismissable-layer": "1.2.1", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-presence": "1.1.11", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-slot": "1.5.0", + "@radix-ui/react-use-callback-ref": "1.1.5", + "@radix-ui/react-use-controllable-state": "1.3.0", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-use-previous": "1.1.4", + "@radix-ui/react-visually-hidden": "1.2.13" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-one-time-password-field": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-one-time-password-field/-/react-one-time-password-field-0.2.1.tgz", + "integrity": "sha512-KtHdcgnaAK6rohQ/MMJoGYcbgSRJsIuGBdHrFeTB1/9SaKlDs14oSOniW6BL6M43HqvgCO3u4WuVdFLkuc6sqw==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.3", + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-collection": "1.2.0", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-direction": "1.2.0", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-roving-focus": "1.2.1", + "@radix-ui/react-use-controllable-state": "1.3.0", + "@radix-ui/react-use-effect-event": "0.0.5", + "@radix-ui/react-use-is-hydrated": "0.1.3", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-password-toggle-field": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-password-toggle-field/-/react-password-toggle-field-0.2.1.tgz", + "integrity": "sha512-vu4+ZoJAaFjdI7l1lDSZRs+4dEv83Jmu7lyzajPrAeQzX74y84n0rUNWbWSgN9XB5NE8IY86Z02kGH6oRrzR+w==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-use-controllable-state": "1.3.0", + "@radix-ui/react-use-effect-event": "0.0.5", + "@radix-ui/react-use-is-hydrated": "0.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.3.1.tgz", + "integrity": "sha512-ZNxvXRx5W2Wos4fXCXb5lpdqN0Z5chf+mStOUOTr4ik7d+62MRvoe9rzCA/eGNg36lxXqSrKEKmwuZEcUrIstw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-dismissable-layer": "1.2.1", + "@radix-ui/react-focus-guards": "1.2.0", + "@radix-ui/react-focus-scope": "1.3.1", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-popper": "1.4.0", + "@radix-ui/react-portal": "1.2.0", + "@radix-ui/react-presence": "1.1.11", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-slot": "1.5.0", + "@radix-ui/react-use-controllable-state": "1.3.0", + "@radix-ui/react-use-layout-effect": "1.1.4", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.7.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.4.0.tgz", + "integrity": "sha512-yCWRvLeGv9Pz3+efwHqGPp70mZ56r3kfIgQVF56Oj2aAa4uP/Tb+IE59wNcc+758rctqbCKx4LbK8II/r7Bn0Q==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.17", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-use-callback-ref": "1.1.5", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-use-rect": "1.1.4", + "@radix-ui/react-use-size": "1.1.5", + "@radix-ui/rect": "1.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.2.0.tgz", + "integrity": "sha512-StX7xXnaRU41VvFtMm9zlhVGz8XF6GmWWTXftkQaY+tqVkbs/R9UmAp5mLzHHapk5hM9RMZQvO/3usyIP/g42g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.11.tgz", + "integrity": "sha512-Z7/atlkab36Z8hm+V8zYc79r0W+tiOB1o0tun1By94JyadkekLQmtEAnBSP9XKXBNQsStoStS4+gX10JpS5akA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.2.0.tgz", + "integrity": "sha512-HCTSPMNqv55Qk0IX4geJbpyXazmqulcimwuyjxTkKi29v+QZbG2Zho9BnbBq4YS2PF+KlNb2CXoLz66uUmblkg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.5.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-progress": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.2.1.tgz", + "integrity": "sha512-yQBiM6J3YM8Ums1orkEFpbznK47geKxa0igikzcaS1xHgdba3DdAQBsGbCTt5BVfR8zhEvgxQ0poGcINaD3lvw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-primitive": "2.2.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-radio-group": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.5.1.tgz", + "integrity": "sha512-QbyVT1ddnjqB9H+CUYKkNWdU9fIK3QHCYtjf7mS6ekSqiuXkWtWn5UZW9mhA8qtl3vrVyIsmR9gePstAtKrqkw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-direction": "1.2.0", + "@radix-ui/react-presence": "1.1.11", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-roving-focus": "1.2.1", + "@radix-ui/react-use-controllable-state": "1.3.0", + "@radix-ui/react-use-size": "1.1.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.2.1.tgz", + "integrity": "sha512-UwraORzVUeEPMclhO1p0vz0qI+TbdDLO9i+FiOqhJyHKwdpCRCck9yVEscUnAMIzCiuNoQa8D4A9bIpOPXGt6Q==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-collection": "1.2.0", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-direction": "1.2.0", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-use-callback-ref": "1.1.5", + "@radix-ui/react-use-controllable-state": "1.3.0", + "@radix-ui/react-use-is-hydrated": "0.1.3", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-scroll-area": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.4.0.tgz", + "integrity": "sha512-CFrLqHNejWq5FhKAcj9V/WPGRsol76HBQto81NdOBVFEOqIEkJkKIJt0Z824A/9hHLCtoiR/Kw3euRgzXP2pxg==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.3", + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-direction": "1.2.0", + "@radix-ui/react-presence": "1.1.11", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-slot": "1.5.0", + "@radix-ui/react-use-callback-ref": "1.1.5", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.4.1.tgz", + "integrity": "sha512-888ZwW0Md6GyptuqYABRX9nLKgoYfcooCyBBvVaX5Jp3e+8Jp2i4Zo/Uy50Hm+Ysn2JfloQXstW4U36w8teVtg==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.3", + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-collection": "1.2.0", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-direction": "1.2.0", + "@radix-ui/react-dismissable-layer": "1.2.1", + "@radix-ui/react-focus-guards": "1.2.0", + "@radix-ui/react-focus-scope": "1.3.1", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-popper": "1.4.0", + "@radix-ui/react-portal": "1.2.0", + "@radix-ui/react-presence": "1.1.11", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-slot": "1.5.0", + "@radix-ui/react-use-callback-ref": "1.1.5", + "@radix-ui/react-use-controllable-state": "1.3.0", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-visually-hidden": "1.2.13", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.7.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-separator": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.2.0.tgz", + "integrity": "sha512-1jUAap0wZEqVP05Ou7kJgRxe/W+Qftng+rUNWGa0qkKbv9r5pWTG3E7a3ELKo2eJoHKMECnzuYrZpSyO1YgcYQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.2.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slider": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.6.1.tgz", + "integrity": "sha512-IRNRR90moPqxfwCJ0XbMJIxo70W0/1ZExT/sNCxEHMaPx0SF9HoQrcPEC75+H28cFKa0b+KH63anfLYJmwKacw==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.3", + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-collection": "1.2.0", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-direction": "1.2.0", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-use-controllable-state": "1.3.0", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-use-size": "1.1.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.5.0.tgz", + "integrity": "sha512-tyYd9ddPXAysAbdbD9eYxgwpCSv6f3PncqVSNKQFSR5lcJVLz3cb/pANDoYrLiHBpXv7skortyUgGUWsYuPPFQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.5" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-switch": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.4.1.tgz", + "integrity": "sha512-NpBGwc40rY02ZmSvuyeKk0Bvc8ZsNeTMi4qHvJ+ZH4EiYCv0zIXEgdCW4bVdzgIUeP9xxbLJSxKOUfqITYNmkA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-use-controllable-state": "1.3.0", + "@radix-ui/react-use-size": "1.1.5" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.2.1.tgz", + "integrity": "sha512-hRoJlode+hg5ZNlR+3tkfkUliIybdTpD5lkAvHQ9yaXseGf7XVO9Ib2GPEIOuaJJbo7YWYbNpDDRfo8Dhf6/xA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-direction": "1.2.0", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-presence": "1.1.11", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-roving-focus": "1.2.1", + "@radix-ui/react-use-controllable-state": "1.3.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toast": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.3.1.tgz", + "integrity": "sha512-iQvL0J7XK4HtJNGkkWMyYrboNseI7vtotFeUkIno8YNxVA9oM7B6//fh0Mc6TSKI96+zioLWi98lQLso88yJmQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-collection": "1.2.0", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-dismissable-layer": "1.2.1", + "@radix-ui/react-portal": "1.2.0", + "@radix-ui/react-presence": "1.1.11", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-use-callback-ref": "1.1.5", + "@radix-ui/react-use-controllable-state": "1.3.0", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-visually-hidden": "1.2.13" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toggle": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.2.1.tgz", + "integrity": "sha512-+3MUq+WyiBplXYqQ0EIYlq8R4sLQ0T5NbOK7DMdDLCKO93pe2uE0DUJTWWfTBprpJJXxfoF3eMW0xpakVZrYsg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-use-controllable-state": "1.3.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toggle-group": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.2.1.tgz", + "integrity": "sha512-O8Ldx5ao4p4KoW+DO8/PmkOe5mdKNi3F2SseqjVj88wvDGDHe60cUmO0Zm/uHsKrvNgQGV8Efpuh95Tht/h4vw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-direction": "1.2.0", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-roving-focus": "1.2.1", + "@radix-ui/react-toggle": "1.2.1", + "@radix-ui/react-use-controllable-state": "1.3.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toolbar": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toolbar/-/react-toolbar-1.2.1.tgz", + "integrity": "sha512-Cm8m7JTEeUdbfA6T9xgyu8HfOpyvSx8pB/wj0DlEuWaJe9FilIs4Az9wci/lU9RbthV/UkltszMw5WnnD+m/Ug==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-direction": "1.2.0", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-roving-focus": "1.2.1", + "@radix-ui/react-separator": "1.2.0", + "@radix-ui/react-toggle-group": "1.2.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.4.1.tgz", + "integrity": "sha512-NxLFGSFtZ78zVfcoOhTwR1xwqo5qx1Jh8Pw6FMZ9McCaqfRzz7/gIRahs2Oy5XlPgQ+8QkUcKbN5B0U44J/kCg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-dismissable-layer": "1.2.1", + "@radix-ui/react-focus-scope": "1.3.1", + "@radix-ui/react-id": "1.1.4", + "@radix-ui/react-popper": "1.4.0", + "@radix-ui/react-portal": "1.2.0", + "@radix-ui/react-presence": "1.1.11", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-slot": "1.5.0", + "@radix-ui/react-use-controllable-state": "1.3.0", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-visually-hidden": "1.2.13" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.5.tgz", + "integrity": "sha512-Y5GBf4NKeADoXI1geiMlMuMoURT3k9MizzzKhGb/m55H94aGigZ01PIpkvsO6lML13BA6XYAXSJ6gza7vTn5nw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.3.0.tgz", + "integrity": "sha512-TAcBI7bw2ljYCbv4+zuTSd6zICu8pRt6SDN2vJ/Z06K2MXh6I9WYFe/A9ovlFDtR5HVHGm5iDxDoABm/++dbKg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-use-effect-event": "0.0.5", + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.5.tgz", + "integrity": "sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.6.tgz", + "integrity": "sha512-9cRullnQRv+kqnRmFlmh0WC3kAPuGmHiQ9VwsXdrYk//sBhgCKEttcpPEPgIQbyx6TGUmooVEHYTyohwz/lSJg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.5" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-is-hydrated": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.3.tgz", + "integrity": "sha512-umO/aJ+82CpOnhDZUTbILCQf7kU/g0iv+oGs/Q8jw7IkhWBzaEP4sA268PhFAJTFetbwp3ICc6ktpI4TqtxcIw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.4.tgz", + "integrity": "sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.4.tgz", + "integrity": "sha512-XoSLhbRbqxFtgJoi2fNHA3C6pDlY34x508vUpUGoFZfvePfHXHbE1lC4FYFMnJWgiCRroSTw6fOsXQoVS9RwZg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.4.tgz", + "integrity": "sha512-cSOCh6JlkmfjLyNcLiu2nB4v+nm+dkZ+Q5KHWk/soo4U7ZLiEQFKHK9/YmtBHjfCEaU43IBKQOc4/uJmCaiCTQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/rect": "1.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.5.tgz", + "integrity": "sha512-V1McDQkMGzgivwqdu7u96APs7oi0Jvj9LTzgq3WWrDZqxWdAqU9yZnZQqk5OizGFR8badUQwZsrlao7MOlWYOA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.13.tgz", + "integrity": "sha512-feijiN1oPk4rck4u9Dm9tsU4mT6Wp9oeEEhaz1WUEs1VuqLf8xzsFG+Qk+uWq6TnMH9O+WRECRg0NftyP9E4Fw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.2.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.3.tgz", + "integrity": "sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw==", + "license": "MIT" + }, + "node_modules/@rolldown/binding-android-arm-eabi": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm-eabi/-/binding-android-arm-eabi-1.2.15.tgz", + "integrity": "sha512-aLcgK9gIJ3YRvrrtfSBbRa27F5WrpiTaPulnFpsHPxpUHgFlSgnU4854MHwoRGEBhfRJDWCS2WQ7lIjskE9Olw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.2.15.tgz", + "integrity": "sha512-xD7DdZCrbThKuxNg7ooQmvp07ys4J/pnSEMTG3/OdOWl6daC67m/Q4fL78sRHMxTH/N4fGUbYhcPM0MF5WFHkg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.2.15.tgz", + "integrity": "sha512-m2WuQ6r0B4OTYxyPpNLtXaMRbRDUbG8YQV2Q3KTdSSK8Th6KHCnC8jVCJt8bvYeSB5WivkkNbc85mKadaSdOxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.2.15.tgz", + "integrity": "sha512-5RtnsHM36nodM9WbMy+zip0LGS/G+y5wro5RHrpOlr4ac24+1zSi9kiV2MP1myZeZ/ifEHfulQcWjnAXzc2C2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.2.15.tgz", + "integrity": "sha512-g3l09pyFqhu+135JKkbkwiKGQmwU5hUMUPJ4VdBADvuqR8d33L6H578rh8m7rtxbdp+VmX/+nzI7nvhSZlzpUQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], "engines": { - "node": ">=24 <25" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.2.15.tgz", + "integrity": "sha512-nIe3VugI97nGQtspPxDqA4sXMUpeiocKIP/CqpY/u/vgbT+alX+cVxu1C/oVBPKg4feNTmYovE91v4DgXvXNwQ==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 8" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.2.15.tgz", + "integrity": "sha512-iTBjXx+B5QSvgI89lJfmqh4wTh8NabR1YZIIN3hNx4zSG0EVfVqkOvLulNpG4251pbd6/IAmZ/LefVk1Y4rPhw==", + "cpu": [ + "arm64" + ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 8" + "node": "^20.19.0 || >=22.12.0" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.2.15.tgz", + "integrity": "sha512-uN26XOnVR7rqQupG3V9oAG2EGGatWjcJHOeDOHUFCh4TBWmP2tHTFoVN13aYbwfm0aHqJAEmcwhAu6sooIjfqQ==", + "cpu": [ + "arm64" + ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 8" + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.2.15.tgz", + "integrity": "sha512-9nWkcdd+H+XhaA3fQIwpHnKIrfx/PQ6rO0pivr78Og07tXlUlctBBJoEYLPfhTmCR/QxEVdn6uPjHV5SymPhdw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.2.15.tgz", + "integrity": "sha512-Ggh0ciFjLwIusLSLz7LZYEaGdxOAUJXI+Qu5JCbhPJJHEt8vP+IjRATTJheiJsdzzTYgUiHUiw3X1Ea3ZpMBdA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.2.15.tgz", + "integrity": "sha512-FHg60rS6DBePAYZ9X9F67x+JObFdmgn6tupA1C/a3B+CufFmTD4mX+e8M75tCiAO3foR/6xwVBI0KQiI7sn/rw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.2.15.tgz", + "integrity": "sha512-/cvV/7v5VGz/moiz0XKRj2dE8DzsKyhIu1KpOvJGPjRywg3yscON889UGvAe8zfiJLHup7VLjBduoGWi3HusWQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.2.15.tgz", + "integrity": "sha512-MyzC5Hrv/sm+Z7TSGKP/j8001RO2xQm1zOpCNVL4j0cY/VhBZviludA2YFQ7i4lH6ZAXxQm0lcMdBzM+99mE7w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.2.15.tgz", + "integrity": "sha512-+PgYiS+AH5omy6L45ZLxybasauJaj3M8buoT83D+nzPmCayXlxUV9hydhciCKVyIg0B/v0BtXPDE1uGqNwCPpg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.2.15.tgz", + "integrity": "sha512-HFZd2QNQYn1JbIi+Hpd7H+r/lkWxcfIu28nirZ6eCn2LafuvmExMlb/P96Sfx5tf7pPArw39IdhmCF8vmV+NhQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" } }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, "node_modules/@sindresorhus/merge-streams": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", @@ -69,16 +2184,51 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, "node_modules/@types/debug": { "version": "4.1.13", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz", "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==", - "dev": true, "license": "MIT", "dependencies": { "@types/ms": "*" } }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "license": "MIT" + }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "license": "MIT", + "dependencies": { + "@types/estree": "*" + } + }, + "node_modules/@types/hast": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.5.tgz", + "integrity": "sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "license": "MIT" + }, "node_modules/@types/katex": { "version": "0.16.8", "resolved": "https://registry.npmjs.org/@types/katex/-/katex-0.16.8.tgz", @@ -86,11 +2236,19 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/mdast": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz", + "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", + "license": "MIT", + "dependencies": { + "@types/unist": "*" + } + }, "node_modules/@types/ms": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "dev": true, "license": "MIT" }, "node_modules/@types/node": { @@ -103,11 +2261,35 @@ "undici-types": ">=7.24.0 <7.24.7" } }, + "node_modules/@types/react": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.3.0.tgz", + "integrity": "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==", + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.3.0.tgz", + "integrity": "sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==", + "devOptional": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.3.0" + } + }, "node_modules/@types/unist": { "version": "2.0.11", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz", "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "dev": true, + "license": "MIT" + }, + "node_modules/@types/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==", "license": "MIT" }, "node_modules/@typescript/typescript-aix-ppc64": { @@ -450,6 +2632,42 @@ "node": ">=16.20.0" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.4.0.tgz", + "integrity": "sha512-1mEZtMKPM09vDmQt5y7YvmN2+DFTP7Tg0EWXdic8/C6VRnpb33e4ghisCIE3WZjsE2N8mf+QV1Zqh7ZFYLWInQ==", + "license": "ISC" + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.1.2.tgz", + "integrity": "sha512-fhJNGUdscQa4HkI0xFxwonWyhnVReCPDbXI7FH7jmzpnVCrFQI7PRrhWecO263610vOrs9tr2Y4xcf8uAdFxbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "oxc-transform-react": "^0.152.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "oxc-transform-react": { + "optional": true + } + } + }, "node_modules/ansi-regex": { "version": "6.4.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", @@ -470,6 +2688,95 @@ "dev": true, "license": "Python-2.0" }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/assistant-cloud": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/assistant-cloud/-/assistant-cloud-0.2.8.tgz", + "integrity": "sha512-KCFwGcDgGUvKoQNw0zyEeFrS7F5B6QXfDaZ/QCNAjDWs7FxomFtLrXxlFGwlvueE+QK5vlOAdNnvKc9QfRzcqA==", + "license": "MIT", + "dependencies": { + "assistant-stream": "^0.3.54" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/exporter-trace-otlp-http": ">=0.200.0", + "@opentelemetry/sdk-trace-base": "^2.1.0", + "ai": "^6.0.0 || ^7.0.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@opentelemetry/exporter-trace-otlp-http": { + "optional": true + }, + "@opentelemetry/sdk-trace-base": { + "optional": true + }, + "ai": { + "optional": true + } + } + }, + "node_modules/assistant-stream": { + "version": "0.3.54", + "resolved": "https://registry.npmjs.org/assistant-stream/-/assistant-stream-0.3.54.tgz", + "integrity": "sha512-Jed48fZg12rzHHWDoTIRKLAO7SovX942f5VXCZ/GifGhaBH0fS1wZhhWhT8BVldJ4uDnCAMlszPGl82eZwO+vw==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/json-schema": "^7.0.15", + "nanoid": "^6.0.2", + "secure-json-parse": "^4.1.1" + }, + "peerDependencies": { + "ai": "^6.0.0 || ^7.0.0", + "ioredis": "^5.10.1 || ^6.0.0", + "redis": "^5.12.1" + }, + "peerDependenciesMeta": { + "ai": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "redis": { + "optional": true + } + } + }, + "node_modules/axe-core": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.14.0.tgz", + "integrity": "sha512-9WTZxEjsZ7b13TH8JPmbV2z8CHbl80/2hm3XPEG4JgNdQLK81IBRXmSxHfMAOkSqQeRxT/0dwNDz2GOm3zzpcQ==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/bail": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz", + "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", @@ -483,11 +2790,30 @@ "node": ">=8" } }, + "node_modules/ccount": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz", + "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/character-entities": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz", "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", - "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/character-entities-html4": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz", + "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", "license": "MIT", "funding": { "type": "github", @@ -498,7 +2824,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "dev": true, "license": "MIT", "funding": { "type": "github", @@ -509,7 +2834,16 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", - "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/comma-separated-tokens": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", + "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", "license": "MIT", "funding": { "type": "github", @@ -526,11 +2860,22 @@ "node": ">= 12" } }, + "node_modules/compare-versions": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", + "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -548,7 +2893,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.3.0.tgz", "integrity": "sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==", - "dev": true, "license": "MIT", "dependencies": { "character-entities": "^2.0.0" @@ -562,17 +2906,31 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, "node_modules/devlop": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz", "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "dev": true, "license": "MIT", "dependencies": { "dequal": "^2.0.0" @@ -595,6 +2953,22 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/estree-util-is-identifier-name": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", + "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -612,6 +2986,12 @@ "node": ">=8.6.0" } }, + "node_modules/fast-json-patch": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz", + "integrity": "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ==", + "license": "MIT" + }, "node_modules/fastq": { "version": "1.20.3", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz", @@ -635,6 +3015,21 @@ "node": ">=8" } }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, "node_modules/get-east-asian-width": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.7.0.tgz", @@ -648,6 +3043,15 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/glob-parent": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", @@ -683,6 +3087,62 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/hast-util-to-jsx-runtime": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", + "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/unist": "^3.0.0", + "comma-separated-tokens": "^2.0.0", + "devlop": "^1.0.0", + "estree-util-is-identifier-name": "^3.0.0", + "hast-util-whitespace": "^3.0.0", + "mdast-util-mdx-expression": "^2.0.0", + "mdast-util-mdx-jsx": "^3.0.0", + "mdast-util-mdxjs-esm": "^2.0.0", + "property-information": "^7.0.0", + "space-separated-tokens": "^2.0.0", + "style-to-js": "^1.0.0", + "unist-util-position": "^5.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/hast-util-to-jsx-runtime/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/hast-util-whitespace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", + "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/html-url-attributes": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", + "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/ignore": { "version": "7.0.14", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.14.tgz", @@ -693,11 +3153,16 @@ "node": ">= 4" } }, + "node_modules/inline-style-parser": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz", + "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==", + "license": "MIT" + }, "node_modules/is-alphabetical": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz", "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", - "dev": true, "license": "MIT", "funding": { "type": "github", @@ -708,7 +3173,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", - "dev": true, "license": "MIT", "dependencies": { "is-alphabetical": "^2.0.0", @@ -723,7 +3187,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz", "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", - "dev": true, "license": "MIT", "funding": { "type": "github", @@ -753,95 +3216,379 @@ "node": ">=0.10.0" } }, - "node_modules/is-hexadecimal": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "node_modules/is-hexadecimal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", + "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-path-inside": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz", + "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-plain-obj": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", + "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/js-yaml": { + "version": "5.4.3", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.4.3.tgz", + "integrity": "sha512-yzzNtgczwgoVHNigCyKTXcQJU4Y8t/ikU4UxXW0BvrLfHTGWZhsNcOT2s/a7vn81/4KqVnNmHjaUEPlvWnNwVw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.mjs" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonpointer": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", + "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/katex": { + "version": "0.18.2", + "resolved": "https://registry.npmjs.org/katex/-/katex-0.18.2.tgz", + "integrity": "sha512-3snve4y0SXTMequLim1FMiPvLGElySam0blN5xQZBqEY3lOJz1TnuaaiugnBZBqHzlSAGmFTYL7MCQkORuLKCA==", + "dev": true, + "funding": [ + "https://opencollective.com/katex", + "https://github.com/sponsors/katex" + ], + "license": "MIT", + "dependencies": { + "commander": "^8.3.0" + }, + "bin": { + "katex": "cli.js" + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.33.0.tgz", + "integrity": "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.33.0.tgz", + "integrity": "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.33.0.tgz", + "integrity": "sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.33.0.tgz", + "integrity": "sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.33.0.tgz", + "integrity": "sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.33.0.tgz", + "integrity": "sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.33.0.tgz", + "integrity": "sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.33.0.tgz", + "integrity": "sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=0.12.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/is-path-inside": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-4.0.0.tgz", - "integrity": "sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==", + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.33.0.tgz", + "integrity": "sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=12" + "node": ">= 12.0.0" }, "funding": { - "url": "https://github.com/sponsors/sindresorhus" + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/js-yaml": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-5.4.3.tgz", - "integrity": "sha512-yzzNtgczwgoVHNigCyKTXcQJU4Y8t/ikU4UxXW0BvrLfHTGWZhsNcOT2s/a7vn81/4KqVnNmHjaUEPlvWnNwVw==", + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.33.0.tgz", + "integrity": "sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==", + "cpu": [ + "x64" + ], "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/puzrin" - }, - { - "type": "github", - "url": "https://github.com/sponsors/nodeca" - } + "libc": [ + "musl" ], - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" }, - "bin": { - "js-yaml": "bin/js-yaml.mjs" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/jsonc-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", - "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/jsonpointer": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz", - "integrity": "sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==", + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.33.0.tgz", + "integrity": "sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=0.10.0" + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, - "node_modules/katex": { - "version": "0.18.2", - "resolved": "https://registry.npmjs.org/katex/-/katex-0.18.2.tgz", - "integrity": "sha512-3snve4y0SXTMequLim1FMiPvLGElySam0blN5xQZBqEY3lOJz1TnuaaiugnBZBqHzlSAGmFTYL7MCQkORuLKCA==", + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.33.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.33.0.tgz", + "integrity": "sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==", + "cpu": [ + "x64" + ], "dev": true, - "funding": [ - "https://opencollective.com/katex", - "https://github.com/sponsors/katex" + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" ], - "license": "MIT", - "dependencies": { - "commander": "^8.3.0" + "engines": { + "node": ">= 12.0.0" }, - "bin": { - "katex": "cli.js" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" } }, "node_modules/linkify-it": { @@ -864,6 +3611,16 @@ "uc.micro": "^3.0.0" } }, + "node_modules/longest-streak": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", + "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/markdown-it": { "version": "15.0.3", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-15.0.3.tgz", @@ -974,6 +3731,179 @@ "markdownlint-cli2": ">=0.0.4" } }, + "node_modules/mdast-util-from-markdown": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.1.0.tgz", + "integrity": "sha512-KRLgmPaP2H1ul2bpoJkOS+9pAcDAAj9Nu1mqLnh8NLeJG5tlvj42TH80PIGBpROWlRXLk0lDrWNuZCFy/tsB4A==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "decode-named-character-reference": "^1.0.0", + "devlop": "^1.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark": "^4.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-normalize-identifier": "^2.0.0", + "micromark-util-symbol": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-from-markdown/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/mdast-util-mdx-expression": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", + "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", + "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "ccount": "^2.0.0", + "devlop": "^1.1.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0", + "parse-entities": "^4.0.0", + "stringify-entities": "^4.0.0", + "unist-util-stringify-position": "^4.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-mdx-jsx/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/mdast-util-mdxjs-esm": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", + "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", + "license": "MIT", + "dependencies": { + "@types/estree-jsx": "^1.0.0", + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "mdast-util-from-markdown": "^2.0.0", + "mdast-util-to-markdown": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-phrasing": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", + "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-hast": { + "version": "13.2.1", + "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz", + "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "@ungap/structured-clone": "^1.0.0", + "devlop": "^1.0.0", + "micromark-util-sanitize-uri": "^2.0.0", + "trim-lines": "^3.0.0", + "unist-util-position": "^5.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.2.0.tgz", + "integrity": "sha512-2Jn/ADrHFFyyniBMi0bncTbJqU78K3/TEYvzvwrWWzD9MYPUa/Gm4r3nOeKFQg/PDM5BsbDYdc8ackW4NTY6NA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "@types/unist": "^3.0.0", + "longest-streak": "^3.0.0", + "mdast-util-phrasing": "^4.0.0", + "mdast-util-to-string": "^4.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-classify-character": "^2.0.0", + "micromark-util-decode-string": "^2.0.0", + "micromark-util-html-tag-name": "^2.0.0", + "unist-util-visit": "^5.0.0", + "zwitch": "^2.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/mdast-util-to-markdown/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/mdast-util-to-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", + "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/mdurl": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.1.0.tgz", @@ -995,7 +3925,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.3.tgz", "integrity": "sha512-oGYfQzHSG5dOMovQcJ3fyTmZlWAWpi0XA0sJwJs+i6OT88o1+Jtw/8z0CmdowSLxGhhFK89rQ/oXph/wN02PNw==", - "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -1032,7 +3961,6 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.4.tgz", "integrity": "sha512-wxEeE8v8XVvOrxn1TZj74qYhAtTQsSqufHVS3uNVyT1MupXxhyaHk8/9xDNPh9BmL77QNDgWTkZ3RsYHB2ry7w==", - "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -1164,7 +4092,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", - "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -1186,7 +4113,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", - "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -1209,7 +4135,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.1.0.tgz", "integrity": "sha512-fS8hnLIjnjvdQIj39Geug8wWsR0HrYZ43KShKxNfwT7t2LOHo/LbWZEzEaSOjwtjdCsjoE6syHhonEzW0zv0+Q==", - "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -1230,7 +4155,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", - "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -1253,7 +4177,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", - "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -1276,7 +4199,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz", "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -1297,7 +4219,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", - "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -1317,7 +4238,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", - "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -1339,7 +4259,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", - "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -1360,7 +4279,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", - "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -1376,11 +4294,32 @@ "micromark-util-symbol": "^2.0.0" } }, + "node_modules/micromark-util-decode-string": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", + "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", + "funding": [ + { + "type": "GitHub Sponsors", + "url": "https://github.com/sponsors/unifiedjs" + }, + { + "type": "OpenCollective", + "url": "https://opencollective.com/unified" + } + ], + "license": "MIT", + "dependencies": { + "decode-named-character-reference": "^1.0.0", + "micromark-util-character": "^2.0.0", + "micromark-util-decode-numeric-character-reference": "^2.0.0", + "micromark-util-symbol": "^2.0.0" + } + }, "node_modules/micromark-util-edit-map": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/micromark-util-edit-map/-/micromark-util-edit-map-1.0.0.tgz", "integrity": "sha512-Pa2ljlsEL6sVwFaYeyrOLSYbQt73JvGbPOYFq+9AElXiSnfI5Q4465RRZ1sHv7lDjDOnGRDDaqPXqClqWK/q1Q==", - "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -1400,7 +4339,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -1417,7 +4355,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", - "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -1434,7 +4371,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", - "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -1454,7 +4390,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", - "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -1474,7 +4409,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", - "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -1496,7 +4430,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", - "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -1519,7 +4452,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -1536,7 +4468,6 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.3.tgz", "integrity": "sha512-oxB2Ik03hI0gv+VNn9tnh1t1YEe9MDPptViAEgfdf3YQHsn0pzGTgCdlSCJXcwhqm8phaEuM7zeEu3QQzVBrPg==", - "dev": true, "funding": [ { "type": "GitHub Sponsors", @@ -1567,14 +4498,30 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, + "node_modules/nanoid": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-6.0.2.tgz", + "integrity": "sha512-hIvVOb9Qr1p/9CNNLOe0OlWvgx/ziTsVwKN/GDSsuArN2K0wDiafP3s0SWmsm7o2BdnyS8Z9Z64NFBUbn6ziBg==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.js" + }, + "engines": { + "node": "^22 || ^24 || >=26" + } + }, "node_modules/parse-entities": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz", "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", - "dev": true, "license": "MIT", "dependencies": { "@types/unist": "^2.0.0", @@ -1590,6 +4537,13 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, "node_modules/picomatch": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", @@ -1603,6 +4557,93 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/playwright": { + "version": "1.64.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.64.1.tgz", + "integrity": "sha512-HCrVhHr5QQuwYoswEUGTb7gGp522yflbv/ST8nF4sFWGuLjQCt17Q/EubpGGSNKYn1tsTCPZxqHhHCfVURu7UA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.64.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/playwright-core": { + "version": "1.64.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.64.1.tgz", + "integrity": "sha512-BC4s8y6SEVA6Q1rSxZTto8hI1KUI3xXlHGZgvGqP4IqPdI/a6FX9H29IDNz7vKo9l21b6LfYosScPL6M3xxyDQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/postcss": { + "version": "8.5.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.31.tgz", + "integrity": "sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.20", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss/node_modules/nanoid": { + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/property-information": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.2.0.tgz", + "integrity": "sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/punycode.js": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", @@ -1634,6 +4675,83 @@ ], "license": "MIT" }, + "node_modules/radix-ui": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/radix-ui/-/radix-ui-1.8.1.tgz", + "integrity": "sha512-HPfpVGizO9xANlsFSauruLhRe/3+iOt3vPX0MXYle/UQi3gorQrSYtBcdiv0BZYewcl2rvL59qrIuLFMGAmUrw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.2.0", + "@radix-ui/react-accessible-icon": "1.2.0", + "@radix-ui/react-accordion": "1.3.1", + "@radix-ui/react-alert-dialog": "1.2.1", + "@radix-ui/react-arrow": "1.1.17", + "@radix-ui/react-aspect-ratio": "1.2.0", + "@radix-ui/react-avatar": "1.3.1", + "@radix-ui/react-checkbox": "1.4.1", + "@radix-ui/react-collapsible": "1.2.1", + "@radix-ui/react-collection": "1.2.0", + "@radix-ui/react-compose-refs": "1.1.5", + "@radix-ui/react-context": "1.3.0", + "@radix-ui/react-context-menu": "2.4.1", + "@radix-ui/react-dialog": "1.3.1", + "@radix-ui/react-direction": "1.2.0", + "@radix-ui/react-dismissable-layer": "1.2.1", + "@radix-ui/react-dropdown-menu": "2.2.1", + "@radix-ui/react-focus-guards": "1.2.0", + "@radix-ui/react-focus-scope": "1.3.1", + "@radix-ui/react-form": "0.3.0", + "@radix-ui/react-hover-card": "1.2.1", + "@radix-ui/react-label": "2.1.17", + "@radix-ui/react-menu": "2.2.1", + "@radix-ui/react-menubar": "1.2.1", + "@radix-ui/react-navigation-menu": "1.4.1", + "@radix-ui/react-one-time-password-field": "0.2.1", + "@radix-ui/react-password-toggle-field": "0.2.1", + "@radix-ui/react-popover": "1.3.1", + "@radix-ui/react-popper": "1.4.0", + "@radix-ui/react-portal": "1.2.0", + "@radix-ui/react-presence": "1.1.11", + "@radix-ui/react-primitive": "2.2.0", + "@radix-ui/react-progress": "1.2.1", + "@radix-ui/react-radio-group": "1.5.1", + "@radix-ui/react-roving-focus": "1.2.1", + "@radix-ui/react-scroll-area": "1.4.0", + "@radix-ui/react-select": "2.4.1", + "@radix-ui/react-separator": "1.2.0", + "@radix-ui/react-slider": "1.6.1", + "@radix-ui/react-slot": "1.5.0", + "@radix-ui/react-switch": "1.4.1", + "@radix-ui/react-tabs": "1.2.1", + "@radix-ui/react-toast": "1.3.1", + "@radix-ui/react-toggle": "1.2.1", + "@radix-ui/react-toggle-group": "1.2.1", + "@radix-ui/react-toolbar": "1.2.1", + "@radix-ui/react-tooltip": "1.4.1", + "@radix-ui/react-use-callback-ref": "1.1.5", + "@radix-ui/react-use-controllable-state": "1.3.0", + "@radix-ui/react-use-effect-event": "0.0.5", + "@radix-ui/react-use-escape-keydown": "1.1.6", + "@radix-ui/react-use-is-hydrated": "0.1.3", + "@radix-ui/react-use-layout-effect": "1.1.4", + "@radix-ui/react-use-size": "1.1.5", + "@radix-ui/react-visually-hidden": "1.2.13" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/re2js": { "version": "2.8.6", "resolved": "https://registry.npmjs.org/re2js/-/re2js-2.8.6.tgz", @@ -1644,6 +4762,173 @@ "node": ">=18.0.0" } }, + "node_modules/react": { + "version": "19.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-19.3.1.tgz", + "integrity": "sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.1.tgz", + "integrity": "sha512-Y0bARz53lhEC1NMCASs0/j+faB/zqt3R8hpvg1e8+61ZVJ4UL0UdC4cBbXfZMcIuhrfgeILTLe+ZeBCavQPtKQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.28.0" + }, + "peerDependencies": { + "react": "^19.3.1" + } + }, + "node_modules/react-markdown": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz", + "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "devlop": "^1.0.0", + "hast-util-to-jsx-runtime": "^2.0.0", + "html-url-attributes": "^3.0.0", + "mdast-util-to-hast": "^13.0.0", + "remark-parse": "^11.0.0", + "remark-rehype": "^11.0.0", + "unified": "^11.0.0", + "unist-util-visit": "^5.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + }, + "peerDependencies": { + "@types/react": ">=18", + "react": ">=18" + } + }, + "node_modules/react-remove-scroll": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", + "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-textarea-autosize": { + "version": "8.5.9", + "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.9.tgz", + "integrity": "sha512-U1DGlIQN5AwgjTyOEnI1oCcMuEr1pv1qOtklB2l4nyMGbHzWrI0eFsYK0zos2YWqAolJyG0IWJaqWmWj5ETh0A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.20.13", + "use-composed-ref": "^1.3.0", + "use-latest": "^1.2.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/remark-parse": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz", + "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", + "license": "MIT", + "dependencies": { + "@types/mdast": "^4.0.0", + "mdast-util-from-markdown": "^2.0.0", + "micromark-util-types": "^2.0.0", + "unified": "^11.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/remark-rehype": { + "version": "11.1.2", + "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz", + "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", + "license": "MIT", + "dependencies": { + "@types/hast": "^3.0.0", + "@types/mdast": "^4.0.0", + "mdast-util-to-hast": "^13.0.0", + "unified": "^11.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -1655,6 +4940,40 @@ "node": ">=0.10.0" } }, + "node_modules/rolldown": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.2.15.tgz", + "integrity": "sha512-6odND+fel5zVw3I4Yc2e69gvguDnX0CAAGiNDZSq4ik5WUcaLrkD2GLuog0XcODbor31hZ5ayEMgVDno+KPV5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.155.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm-eabi": "1.2.15", + "@rolldown/binding-android-arm64": "1.2.15", + "@rolldown/binding-darwin-arm64": "1.2.15", + "@rolldown/binding-darwin-x64": "1.2.15", + "@rolldown/binding-freebsd-x64": "1.2.15", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.15", + "@rolldown/binding-linux-arm64-gnu": "1.2.15", + "@rolldown/binding-linux-arm64-musl": "1.2.15", + "@rolldown/binding-linux-ppc64-gnu": "1.2.15", + "@rolldown/binding-linux-s390x-gnu": "1.2.15", + "@rolldown/binding-linux-x64-gnu": "1.2.15", + "@rolldown/binding-linux-x64-musl": "1.2.15", + "@rolldown/binding-openharmony-arm64": "1.2.15", + "@rolldown/binding-win32-arm64-msvc": "1.2.15", + "@rolldown/binding-win32-x64-msvc": "1.2.15" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -1679,6 +4998,43 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-content-frame": { + "version": "0.0.34", + "resolved": "https://registry.npmjs.org/safe-content-frame/-/safe-content-frame-0.0.34.tgz", + "integrity": "sha512-js/Tt/SWUGdBi+pt8NuMlg5OkDLD+5Q8sTItzAFr4Xr+geHkTOc0Vl2LA+BUsPFZ3fdT4hbRjWaPb9DoPEvHDw==", + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz", + "integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==", + "license": "MIT" + }, + "node_modules/secure-json-parse": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.1.1.tgz", + "integrity": "sha512-lnd81MQEZKjhW/Gd2TQqdd4XP6QYNdDW/EP+I7nTrFHP9y2LmaGPIHk9xsnzcOiEr1c39c/e84xFgGviQKBTRw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/slash": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", @@ -1705,6 +5061,26 @@ "url": "https://github.com/sponsors/cyyynthia" } }, + "node_modules/source-map-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/space-separated-tokens": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", + "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/string-width": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.3.0.tgz", @@ -1722,20 +5098,100 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/strip-ansi": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", - "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "node_modules/stringify-entities": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz", + "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", + "license": "MIT", + "dependencies": { + "character-entities-html4": "^2.0.0", + "character-entities-legacy": "^3.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/style-to-js": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.21.tgz", + "integrity": "sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==", + "license": "MIT", + "dependencies": { + "style-to-object": "1.0.14" + } + }, + "node_modules/style-to-object": { + "version": "1.0.14", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.14.tgz", + "integrity": "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==", + "license": "MIT", + "dependencies": { + "inline-style-parser": "0.2.7" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", - "dependencies": { - "ansi-regex": "^6.2.2" - }, "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" + "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/to-regex-range": { @@ -1751,6 +5207,32 @@ "node": ">=8.0" } }, + "node_modules/trim-lines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz", + "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/trough": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz", + "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, "node_modules/typescript": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz", @@ -1813,11 +5295,371 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/unified": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz", + "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "bail": "^2.0.0", + "devlop": "^1.0.0", + "extend": "^3.0.0", + "is-plain-obj": "^4.0.0", + "trough": "^2.0.0", + "vfile": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unified/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/unist-util-is": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", + "integrity": "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-is/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/unist-util-position": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz", + "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-position/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/unist-util-stringify-position": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", + "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-stringify-position/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/unist-util-visit": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.1.0.tgz", + "integrity": "sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0", + "unist-util-visit-parents": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.2.tgz", + "integrity": "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-is": "^6.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/unist-util-visit-parents/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/unist-util-visit/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/untruncate-json": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/untruncate-json/-/untruncate-json-0.0.1.tgz", + "integrity": "sha512-4W9enDK4X1y1s2S/Rz7ysw6kDuMS3VmRjMFg7GZrNO+98OSe+x5Lh7PKYoVjy3lW/1wmhs6HW0lusnQRHgMarA==", + "license": "MIT" + }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-composed-ref": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.4.0.tgz", + "integrity": "sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-isomorphic-layout-effect": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz", + "integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-latest": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.3.0.tgz", + "integrity": "sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==", + "license": "MIT", + "dependencies": { + "use-isomorphic-layout-effect": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/uuid": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.1.tgz", + "integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/esm/bin/uuid" + } + }, + "node_modules/vfile": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz", + "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "vfile-message": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz", + "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", + "license": "MIT", + "dependencies": { + "@types/unist": "^3.0.0", + "unist-util-stringify-position": "^4.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/unified" + } + }, + "node_modules/vfile-message/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/vfile/node_modules/@types/unist": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", + "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", + "license": "MIT" + }, + "node_modules/vite": { + "version": "8.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.3.5.tgz", + "integrity": "sha512-z1YpjieJ9g0ebTmQpLBv/oq9E3F9EIlOe+7LahW8SrE3LTiG3udBRUXQOlYXP9b+IC2q2phHeVxj9xGlw34OEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.7", + "postcss": "^8.5.30", + "rolldown": "~1.2.14", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.7.1 || ^0.8.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/yaml": { "version": "2.9.1", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.1.tgz", "integrity": "sha512-3NxN8+78OdzbT7C/WjGsyfPAtJaN3FNDsWxv7Y7mcDsT/oOmgW8BpyQQFFBnvZE3j9Y2Sdz1ULFLezL7Eb2yFw==", - "dev": true, "license": "ISC", "bin": { "yaml": "bin.mjs" @@ -1828,6 +5670,54 @@ "funding": { "url": "https://github.com/sponsors/eemeli" } + }, + "node_modules/zod": { + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.7.tgz", + "integrity": "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zustand": { + "version": "5.0.16", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.16.tgz", + "integrity": "sha512-Uv7e327ihlCfayfja3hDc0M64Upev1LWBKpRobdK4NyovDDPeeX4imWXc/wzC1yc6fkYPoAY/ncKVPAs2jrtQw==", + "license": "MIT", + "engines": { + "node": ">=12.20.0" + }, + "peerDependencies": { + "@types/react": ">=18.0.0", + "immer": ">=9.0.6", + "react": ">=18.0.0", + "use-sync-external-store": ">=1.2.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "immer": { + "optional": true + }, + "react": { + "optional": true + }, + "use-sync-external-store": { + "optional": true + } + } + }, + "node_modules/zwitch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", + "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } } } } diff --git a/package.json b/package.json index c0c34f9..90aca43 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,8 @@ "description": "Repository governance and documentation tooling for OpenLab", "license": "Apache-2.0", "overrides": { - "katex": "0.18.2" + "katex": "0.18.2", + "zod": "4.6.7" }, "engines": { "node": ">=24 <25" @@ -14,7 +15,9 @@ "scripts": { "brand:generate": "node scripts/generate-brand.mjs", "check": "npm run lint:markdown && npm run check:repository && npm run typecheck && npm test", - "typecheck": "tsc --noEmit", + "typecheck": "tsc --noEmit && tsc -p tsconfig.web.json", + "build": "vite build", + "test:browser": "playwright test", "lint:markdown": "markdownlint-cli2", "check:repository": "node scripts/check-repository.mjs", "test": "node --test scripts/tests/*.test.mjs app/tests/*.test.ts", @@ -28,9 +31,24 @@ "release:check": "node scripts/release.mjs check" }, "devDependencies": { + "@axe-core/playwright": "4.14.0", + "@playwright/test": "1.64.1", "@types/node": "24.19.2", + "@types/react": "19.3.0", + "@types/react-dom": "19.3.0", + "@vitejs/plugin-react": "6.1.2", "markdownlint-cli2": "0.24.0", "typescript": "7.0.2", + "vite": "8.3.5" + }, + "dependencies": { + "@ag-ui/client": "1.0.3", + "@ag-ui/core": "1.0.3", + "@assistant-ui/react": "0.15.31", + "@assistant-ui/react-ag-ui": "0.1.0", + "react": "19.3.1", + "react-dom": "19.3.1", + "react-markdown": "10.1.0", "yaml": "2.9.1" } } diff --git a/playwright.config.ts b/playwright.config.ts new file mode 100644 index 0000000..6d059f6 --- /dev/null +++ b/playwright.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from '@playwright/test'; +export default defineConfig({ + testDir: './browser-tests', fullyParallel: false, workers: 1, timeout: 45_000, + use: { browserName: 'chromium', channel: process.env.OPENLAB_BROWSER_CHANNEL ?? 'chrome', headless: true, viewport: { width: 1440, height: 1000 }, trace: 'retain-on-failure', screenshot: 'only-on-failure' }, + webServer: { command: 'node scripts/browser-fixture.mjs', url: 'http://127.0.0.1:4321', reuseExistingServer: false, timeout: 30_000 }, +}); diff --git a/scripts/browser-fixture.mjs b/scripts/browser-fixture.mjs new file mode 100644 index 0000000..f9384cf --- /dev/null +++ b/scripts/browser-fixture.mjs @@ -0,0 +1,50 @@ +import { join, resolve } from 'node:path'; +import { mkdirSync, writeFileSync, rmSync } from 'node:fs'; +import { randomUUID } from 'node:crypto'; +import { setTimeout as delay } from 'node:timers/promises'; +import { Store } from '../app/store.ts'; +import { Platform } from '../app/platform.ts'; +import { SubstituteRuntime } from '../app/tests/helpers.ts'; +import { Research, researchTools } from '../app/research.ts'; +import { Settings } from '../app/settings.ts'; +import { Skills } from '../app/skills.ts'; +import { createService } from '../app/service.ts'; + +// Synthetic, network-free acceptance server. It never opens the user's data root. +const root = resolve('.cache', 'browser-fixture-' + randomUUID()); +mkdirSync(root, { recursive: true }); +const store = new Store(join(root, 'projects')); +const settings = new Settings(root, { protect: value => 'synthetic:' + value, unprotect: value => value.slice(10) }); +const entry = settings.add({ name: 'Synthetic fixture model', baseUrl: 'https://fixture.invalid', model: 'synthetic-model', apiKey: 'synthetic-test-value' }); +settings.save([{ ...entry, testedAt: new Date().toISOString(), streaming: true, tools: true }]); +const skills = new Skills(join(root, 'skills')); +const research = new Research(store, async url => ({ status: 200, contentType: 'application/json', url, body: JSON.stringify({ message: { items: [ + { DOI: '10.0000/synthetic-a', title: ['Synthetic economics evidence A'] }, + { DOI: '10.0000/synthetic-b', title: ['Synthetic contradictory humanities evidence B'] }, +] } }) })); +const runtime = new SubstituteRuntime(async (run, host, signal) => { + const message = async content => { const messageId = randomUUID(); const stepId = randomUUID(); await host.emit('step_started', { stepId, label: 'Synthetic fixture step' }); await host.emit('message_start', { messageId, stepId }); await host.emit('message_delta', { messageId, stepId, text: content }); await host.emit('message_end', { messageId, stepId, content }); await host.emit('step_finished', { stepId }); }; + await message('Synthetic fixture plan. 这是合成验收数据,先确认计划再检索。'); + if (await host.wait({ kind: 'confirmation', input: 'boolean', prompt: 'Synthetic fixture plan. Confirm controlled metadata retrieval.', scope: { tools: researchTools.map(tool => tool.name) } }, signal) !== true) return; + const tool = async (name, args) => { + const call = { id: randomUUID(), name, arguments: JSON.stringify(args) }; const stepId = randomUUID(); + await host.emit('tool_intent', { callId: call.id, name, arguments: call.arguments, stepId }); + const result = await research.execute(run, call, host, signal); + await host.emit('tool_result', { callId: call.id, stepId, result }); return result; + }; + const sources = await tool('search_metadata', { query: 'synthetic evidence' }); + const sourceId = sources.sources[0].id; + const draft = await tool('publish_report', { content: `# Synthetic saved draft\n\nIncomplete fixture report. [Evidence](source:${sourceId}).`, final: false, artifactId: '' }); + if (run.question.includes('input')) await host.wait({ kind: 'question', input: 'single_choice', prompt: 'Choose a fixture discipline / 选择测试学科', options: ['economics', 'humanities'] }, signal); + await tool('read_source', { sourceId }); + await delay(300, undefined, { signal }); + await tool('publish_report', { content: `# Synthetic final report\n\nThis is controlled acceptance data. [Evidence](source:${sourceId}) is metadata only. Contradictory fixture records are preserved.\n\n## 限制\n\n模型解读未经核实。全文访问被拒绝或不可用。`, final: true, artifactId: draft.artifactId }); + await message('Synthetic report saved. Review the metadata limitations. 合成报告已保存。'); +}); +const platform = new Platform(store, runtime); +const service = await createService({ platform, settings, skills, research, port: Number(process.env.OPENLAB_TEST_PORT ?? 4321) }); +writeFileSync('.cache/browser-launch.json', JSON.stringify({ launchUrl: service.launchUrl, origin: service.origin })); +console.log('Synthetic browser fixture ready.'); +let closing = false; +const close = async () => { if (closing) return; closing = true; await service.close(); if (resolve(root).startsWith(resolve('.cache') + (process.platform === 'win32' ? '\\' : '/'))) rmSync(root, { recursive: true, force: true }); }; +process.on('SIGTERM', () => { void close(); }); process.on('SIGINT', () => { void close(); }); diff --git a/scripts/check-repository.mjs b/scripts/check-repository.mjs index e7efe0f..ef1c4bf 100644 --- a/scripts/check-repository.mjs +++ b/scripts/check-repository.mjs @@ -3,10 +3,11 @@ import { resolve, dirname, relative, extname } from 'node:path'; import { parseDocument } from 'yaml'; import { readJson } from './lib.mjs'; import { mark } from './generate-brand.mjs'; +import { execFileSync } from 'node:child_process'; const root = process.cwd(); const errors = []; -const ignore = new Set(['.git', 'node_modules', '.cache', 'dist', 'coverage']); +const ignore = new Set(['.git', 'node_modules', '.cache', '.openlab', 'dist', 'coverage', 'test-results', 'playwright-report']); function walk(path) { return readdirSync(path, { withFileTypes: true }).flatMap((entry) => { if (ignore.has(entry.name)) return []; @@ -16,6 +17,8 @@ function walk(path) { } function check(condition, message) { if (!condition) errors.push(message); } const files = walk(root); +const trackedPrivate = execFileSync('git', ['ls-files', '--', '.openlab', '.cache', 'test-results', 'playwright-report'], { encoding: 'utf8', windowsHide: true }).trim(); +check(!trackedPrivate, 'Local application data, caches and test output must not be committed'); const required = ['LICENSE', 'NOTICE', 'VERSION', 'README.md', 'README.zh-CN.md', 'CONTRIBUTING.md', 'CONTRIBUTING.zh-CN.md', 'CODE_OF_CONDUCT.md', 'SECURITY.md', 'SUPPORT.md', 'CONTRIBUTORS.md', 'THIRD_PARTY_NOTICES.md', 'CHANGELOG.md', '.github/CODEOWNERS', 'docs/vision.zh-CN.md', 'docs/product.md', 'docs/architecture.md', 'docs/logging.md', 'docs/verification.md']; required.forEach((file) => check(existsSync(file), `Missing required file: ${file}`)); diff --git a/tsconfig.web.json b/tsconfig.web.json new file mode 100644 index 0000000..baa4d12 --- /dev/null +++ b/tsconfig.web.json @@ -0,0 +1,8 @@ +{ + "compilerOptions": { + "target": "ES2023", "module": "ESNext", "moduleResolution": "Bundler", "jsx": "react-jsx", + "strict": true, "noEmit": true, "skipLibCheck": true, "lib": ["ES2023", "DOM", "DOM.Iterable"], + "types": ["vite/client"], "allowImportingTsExtensions": true, "erasableSyntaxOnly": true + }, + "include": ["web/**/*.ts", "web/**/*.tsx", "vite.config.ts"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..0d8bab8 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,3 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +export default defineConfig({ root: 'web', publicDir: '../assets', plugins: [react()], build: { outDir: '../dist', emptyOutDir: true }, server: { host: '127.0.0.1' } }); diff --git a/web/api.ts b/web/api.ts new file mode 100644 index 0000000..4b2a15a --- /dev/null +++ b/web/api.ts @@ -0,0 +1,27 @@ +let csrf = ''; +export async function initializeSession(): Promise { + const token = new URLSearchParams(location.hash.slice(1)).get('token'); + const response = token + ? await fetch('/api/v1/session', { method: 'POST', headers: { 'x-openlab-bootstrap': token } }) + : await fetch('/api/v1/session'); + if (!response.ok) throw new Error('Open the launch link printed by npm start to connect this browser. / 请使用 npm start 输出的启动链接。'); + csrf = (await response.json() as { csrf: string }).csrf; + history.replaceState(null, '', location.pathname + location.search); +} +export const csrfToken = () => csrf; +export async function api(path: string, method = 'GET', body?: unknown): Promise { + const response = await fetch('/api/v1' + path, { method, headers: { 'content-type': 'application/json', 'x-openlab-csrf': csrf }, ...(body === undefined ? {} : { body: JSON.stringify(body) }) }); + if (!response.ok) { + let message = `Local service HTTP ${response.status}`; + try { message = (await response.json() as { error: string }).error; } catch { /* Keep an actionable HTTP status. */ } + throw new Error(message); + } + return response.json() as Promise; +} +export async function uploadMaterial(projectId: string, file: File): Promise { + if (file.size > 10_000_000) throw new Error('Material limit: 10 MB / 附件上限:10 MB'); + const base64 = await new Promise((resolve, reject) => { + const reader = new FileReader(); reader.onerror = reject; reader.onload = () => resolve(String(reader.result).split(',')[1]); reader.readAsDataURL(file); + }); + await api(`/projects/${projectId}/materials`, 'POST', { name: file.name, base64 }); +} diff --git a/web/conversation.tsx b/web/conversation.tsx new file mode 100644 index 0000000..ccaa16f --- /dev/null +++ b/web/conversation.tsx @@ -0,0 +1,43 @@ +import { createContext, memo, useContext, useEffect, useMemo, useState } from 'react'; +import { AssistantRuntimeProvider, MessagePrimitive, ThreadPrimitive } from '@assistant-ui/react'; +import { useAgUiRuntime } from '@assistant-ui/react-ag-ui'; +import { HttpAgent } from '@ag-ui/client'; +import type { AgentSubscriber } from '@ag-ui/client'; +import type { Run } from '../app/contracts'; +import { csrfToken } from './api'; +import { ReportText } from './ui'; + +class ExistingRunAgent extends HttpAgent { + platformRunId: string; + constructor(run: Run) { + super({ url: `/api/v1/runs/${run.id}/ag-ui`, threadId: run.conversationId, headers: { 'x-openlab-csrf': csrfToken() } }); + this.platformRunId = run.id; + } + override runAgent(parameters?: Parameters[0], subscriber?: AgentSubscriber) { + return super.runAgent({ ...parameters, runId: this.platformRunId }, subscriber); + } +} +const SourceContext = createContext<(id: string) => void>(() => {}); +function TextPart({ text }: { text: string }) { return {text}; } +function Message() { + return

{toolName} {result === undefined ? '· …' : '· ✓'}
{result === undefined ? 'Waiting for tool / 等待工具' : JSON.stringify(result, null, 2)}
}, + }} />; +} +function UserMessage() { return
; } +const messageComponents = { UserMessage, AssistantMessage: Message }; +export const Conversation = memo(function Conversation({ run, onSource, retryKey = 0 }: { run: Run; onSource: (id: string) => void; retryKey?: number }) { + const agent = useMemo(() => new ExistingRunAgent(run), [run.id, retryKey]); + const [streamError, setStreamError] = useState(''); + const runtime = useAgUiRuntime({ agent, showThinking: false, autoCancelPendingToolCalls: false, isSendDisabled: true, + onError: () => setStreamError('Conversation stream ended with an error. Check the recorded run status. / 对话流异常,请查看已记录的运行状态。'), + }); + useEffect(() => { runtime.thread.startRun({ parentId: null }); return () => agent.abortRun(); }, [runtime, agent]); + return + {streamError &&

{streamError}

} + + + +
; +}, (previous, next) => previous.run.id === next.run.id && previous.retryKey === next.retryKey && previous.onSource === next.onSource); diff --git a/web/diagnostics.tsx b/web/diagnostics.tsx new file mode 100644 index 0000000..a3fd1df --- /dev/null +++ b/web/diagnostics.tsx @@ -0,0 +1,20 @@ +import { useEffect, useState } from 'react'; +import type { ObjectData } from '../app/contracts'; +import { api } from './api'; +import { Tag } from './ui'; + +export function Diagnostics({ zh }: { zh: boolean }) { + const [setup, setSetup] = useState(null); + const [result, setResult] = useState(''); + const [busy, setBusy] = useState(false); + useEffect(() => { void api('/setup').then(setSetup).catch(() => setResult(zh ? '本地服务不可用' : 'Local service unavailable')); }, []); + return
{setup ? zh ? '本地服务和运行时可用' : 'Local service and runtime available' : zh ? '正在检查本地服务' : 'Checking local service'}
{zh ? '环境信息' : 'Environment details'}

Node.js {String(setup?.node ?? '…')} · {String(setup?.platform ?? '…')}

{zh ? '已保存项目支持离线查看;模型执行和检索需要网络。进程丢失后需要明确重试。' : 'Saved projects can be viewed offline; model execution and retrieval need network access. A lost worker requires an explicit retry.'}

{result && {result}}
; +} +export function ExecutionLimits({ zh, value, change }: { zh: boolean; value: { tokens: number; turns: number }; change: (limits: { tokens: number; turns: number }) => void }) { + return

{zh ? '后续运行的限制' : 'Limits for subsequent runs'}

{zh ? '达到已报告用量后停止新模型调用;单次调用可能跨过预算。未知用量仍会标记为未知,这不是供应商通用的账单硬上限。' : 'New model calls stop at the reported usage budget; one call can cross it. Unknown usage stays unknown. This is not a provider-independent hard billing cap.'}

; +} diff --git a/web/index.html b/web/index.html new file mode 100644 index 0000000..88af54d --- /dev/null +++ b/web/index.html @@ -0,0 +1,13 @@ + + + + + + + OpenLab · Research workspace + + +
+ + + diff --git a/web/main.tsx b/web/main.tsx new file mode 100644 index 0000000..a3eb556 --- /dev/null +++ b/web/main.tsx @@ -0,0 +1,5 @@ +import { createRoot } from 'react-dom/client'; +import { App } from './workspace'; +import './style.css'; + +createRoot(document.getElementById('root')!).render(); diff --git a/web/style.css b/web/style.css new file mode 100644 index 0000000..3dfba53 --- /dev/null +++ b/web/style.css @@ -0,0 +1,6 @@ +:root{font-family:Inter,"Segoe UI","Microsoft YaHei",system-ui,sans-serif;color:#243b3a;background:#f4f6f5;font-synthesis:none;font-size:14px;line-height:1.6} +*{box-sizing:border-box}body{margin:0}button,input,select,textarea{font:inherit}button,a,input,select,textarea{outline-offset:4px}button:focus-visible,a:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible,[tabindex]:focus-visible{outline:3px solid #31a694}button{cursor:pointer;background:#fff;border:1px solid #d9e1dd;color:#294541;border-radius:7px;padding:7px 12px;line-height:1.45}button:hover:not(:disabled){background:#ecf5f1;border-color:#a7c8bd}button:disabled{opacity:.5;cursor:not-allowed}a{color:#087c68;text-underline-offset:3px;word-break:break-word}h1,h2,h3,h4,p{margin-top:0}h1,h2,h3{line-height:1.3}h1{font-size:28px;letter-spacing:-.7px;font-weight:600}h2{font-size:19px;font-weight:600}h3{font-size:16px;font-weight:600}small,.muted{color:#53694f;font-size:12px}pre{white-space:pre-wrap;overflow-wrap:anywhere;font:12px/1.7 "Cascadia Code",Consolas,monospace;background:#f1f5f2;border-radius:6px;padding:12px;max-height:360px;overflow:auto}.mono{font-family:Consolas,monospace;font-size:12px;overflow-wrap:anywhere}dl{display:grid;grid-template-columns:auto 1fr;gap:6px 12px;font-size:12px}dt{color:#5a6e5c}dd{margin:0;overflow-wrap:anywhere}details>summary{cursor:pointer;color:#4b6455;font-size:12px}details[open]>summary{margin-bottom:10px}.sr-only{position:absolute!important;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.row{display:flex;align-items:center;gap:10px}.spacer{flex:1}.primary{background:#1c685a;color:#fff;border-color:#1c685a}.primary:hover:not(:disabled){background:#155548;color:#fff;border-color:#155548}.link{border:0;background:none;padding:2px 0;color:#247f67;font-size:12px}.link:hover:not(:disabled){background:none;text-decoration:underline}.block{display:block;text-align:left}.icon-button{border:0;background:transparent;font-size:22px;line-height:1;padding:7px;text-decoration:none}.subtle{background:none;font-size:12px}.eyebrow{color:#536c5b;font-size:10px;letter-spacing:1.7px;font-weight:600}.tag{display:inline-block;vertical-align:middle;background:#edf1ee;color:#4e6355;border:1px solid #dce5df;border-radius:4px;padding:3px 7px;font-size:11px;line-height:1.4}.tag.green,.tag.teal{background:#e4f3eb;border-color:#cfe6d9;color:#226b52}.tag.amber{background:#fcf3dc;color:#816226;border-color:#f1e2b9}.dot{display:inline-block;flex:0 0 auto;width:6px;height:6px;border-radius:50%;background:#77b49a}.dot.running,.dot.awaiting_confirmation,.dot.awaiting_permission,.dot.awaiting_input{background:#deb96b}.dot.failed,.dot.interrupted{background:#d68c7d}.dot.cancelled{background:#83968c}.notice{display:block;font-size:12px;color:#7e6b42;background:#fbf7eb;border:1px solid #eee4c7;border-radius:6px;padding:11px 14px;margin:10px 0}.notice button{margin-left:8px}.error-banner{display:flex;gap:16px;align-items:center;justify-content:space-between;background:#fbe9e5;color:#963f32;padding:10px 24px;font-size:13px}.error-banner button{background:transparent;border:0;color:inherit;font-size:20px}.workspace{display:grid;grid-template-columns:228px minmax(360px,1fr) 5px var(--artifact-width);height:100dvh;overflow:hidden}.sidebar{display:flex;flex-direction:column;background:#123b39;color:#d0dfd7;min-height:0;padding:27px 15px 12px}.nav-brand{display:flex;align-items:center;gap:8px;margin:0 8px 28px;white-space:nowrap}.nav-brand strong{font-size:21px;letter-spacing:-.7px;color:#fff}.nav-brand>span{font-size:8px;letter-spacing:1px;border-left:1px solid #46615a;padding-left:8px;color:#8aa79c}.sidebar button,.drawer-navigation button{color:inherit;background:transparent;border:0;text-align:left}.sidebar button:hover:not(:disabled),.drawer-navigation button:hover:not(:disabled){background:#29514a}.sidebar .new-project,.drawer-navigation .new-project{border:1px solid #638078;background:#28524a;padding:11px 13px;color:#f3faf5;margin-bottom:27px}.section-label{font-size:9px;letter-spacing:1.7px;color:#96b7a8;margin:17px 12px 10px}.section-label.row{justify-content:space-between;margin-top:22px;margin-bottom:5px}.section-label button{font-size:17px;line-height:1;padding:0 6px}.project-list,.conversation-list,.run-list{display:flex;flex-direction:column;gap:5px}.project-list{max-height:27vh;overflow:auto}.project-list button{display:flex;align-items:center;gap:8px;min-width:0}.project-list button>span:nth-child(2){overflow:hidden;text-overflow:ellipsis}.project-list small{font-size:9px;color:#94b2a7}.folder-icon{font-size:18px;color:#8aaea0}.sidebar .selected,.drawer-navigation .selected{background:#2a554c;color:#fff}.conversation-list button{font-size:12px;padding:8px 13px}.run-list{overflow:auto;flex:1}.run-list button{display:flex;align-items:center;gap:10px;font-size:12px}.run-list small{display:block;font-size:9px;color:#bad1c5;margin-top:2px}.nav-footer{margin-top:auto;padding-top:20px}.nav-footer>button{display:block;width:100%;padding:10px 12px;font-size:12px}.local-note{padding:15px 8px 0;font-size:9px;color:#91ab9f;border-top:1px solid #31544b;margin-top:13px;display:flex;align-items:center;gap:7px}.main-panel{display:flex;flex-direction:column;min-width:0;min-height:0;background:#fafbf9}.topbar{display:flex;gap:15px;align-items:center;border-bottom:1px solid #e6ebe5;min-height:65px;padding:0 24px}.topbar button{font-size:12px;border:0;background:transparent;padding:5px}.breadcrumb{display:flex;gap:10px;font-size:11px;color:#5c705f;overflow:hidden;white-space:nowrap}.breadcrumb>span:first-child{max-width:160px;overflow:hidden;text-overflow:ellipsis}.breadcrumb strong{font-weight:500;color:#52695b}.connection{font-size:10px;display:flex;gap:6px;align-items:center;margin-left:auto;color:#526d58;white-space:nowrap}.connection.offline{color:#ad714d}.connection.offline .dot{background:#ad714d}.research-header{display:flex;align-items:start;justify-content:space-between;gap:15px;padding:31px 35px 20px}.research-header h1{margin:8px 0}.research-header p{font-size:12px;color:#5b705d;margin-bottom:0}.research-header button{margin-top:7px}.conversation-panel{flex:1;min-height:0;overflow:auto;padding:0 35px 20px;scrollbar-gutter:stable}.welcome{max-width:620px;margin:12vh auto 50px}.research-symbol{font-size:43px;color:#518d73;margin-bottom:25px}.welcome h2{font-size:25px;letter-spacing:-.7px;font-weight:500}.welcome>p{font-size:13px;max-width:410px;color:#5d7054;line-height:1.9;margin:16px 0 28px}.suggestions{display:grid;gap:10px;max-width:460px}.suggestions button{display:flex;justify-content:space-between;text-align:left;gap:20px;padding:14px;font-size:12px;background:#f7f9f4;color:#617962;border-color:#e3e8dc}.suggestions span{color:#8a9c87}.composer{margin:0 30px;border:1px solid #dce4d7;background:#fff;border-radius:11px;padding:15px 17px 11px;box-shadow:0 4px 20px #324c2606}.composer textarea{border:0;resize:vertical;width:100%;max-height:200px;min-height:65px;background:transparent;font-size:13px;color:#334e3f}.composer textarea::placeholder{color:#93a08e}.composer textarea:focus{outline:0}.composer-controls{display:flex;align-items:center;gap:8px;margin-top:8px}.composer select{max-width:185px;background:#f4f7f1;border:1px solid #e5e9e0;border-radius:5px;padding:5px 6px;font-size:10px;color:#506749}.composer select:nth-of-type(2){max-width:150px}.composer .primary{font-size:11px;padding:8px 13px;white-space:nowrap}.composer .primary span{margin-left:10px}.composer small{display:block;font-size:9px;margin-top:9px;color:#607355}.workspace-footer{display:flex;gap:14px;font-size:9px;color:#5d7052;padding:11px 32px}.workspace-footer>span{margin-right:auto}.workspace-footer a,.workspace-footer button{font-size:9px;color:#5a704c}.run-status{display:flex;align-items:center;gap:10px;flex-wrap:wrap;font-size:11px;color:#5e7055;margin:0 0 20px}.run-status>button{font-size:10px;padding:5px 9px}.thread{width:100%}.thread-viewport{min-height:20px}.chat-message{padding:10px 0 18px}.user-message{background:#edf4e9;border:1px solid #e1eadb;padding:0 15px;margin:5px 0 14px;border-radius:8px}.user-message .chat-message{padding:10px 0}.user-message p:last-child{margin-bottom:0}.markdown{font-size:13px;line-height:1.9;overflow-wrap:anywhere}.markdown h1{font-size:22px;letter-spacing:0}.markdown h2{font-size:18px;margin:24px 0 10px}.markdown h3{font-size:15px;margin:22px 0 10px}.markdown p{margin-bottom:13px}.markdown ul,.markdown ol{padding-left:22px}.markdown table{border-collapse:collapse;display:block;overflow-x:auto;max-width:100%}.markdown th,.markdown td{border:1px solid #e0e7dc;padding:6px 10px;text-align:left}.markdown blockquote{border-left:3px solid #aac7ad;margin-left:0;padding-left:15px;color:#6d8069}.markdown code{background:#eff3eb;padding:2px 4px;border-radius:3px}.markdown pre code{background:none}.citation{display:inline;border:0;background:#e7f1e4;padding:0 4px;border-radius:3px;font-size:inherit;color:#2e7d53;text-align:left}.tool-message{background:#f0f4ed;border:1px solid #e3e9df;border-radius:5px;padding:8px 12px;margin:5px 0;font-size:11px}.tool-message pre{max-height:240px;font-size:10px}.interaction{background:#f0f6eb;border:1px solid #d5e4cd;border-radius:9px;padding:19px;margin:16px 0}.interaction .row{margin-top:15px}.interaction>details{margin:14px 0}.execution-record{border-top:1px solid #e3e9df;margin-top:20px;padding:13px 0}.execution-record ol{padding:0;list-style:none}.execution-record li{border-left:2px solid #e0e7db;padding:7px 13px;font-size:11px;margin:8px 0}.execution-record time{font-size:9px;color:#536a4e;margin-right:12px}.execution-record strong{font-weight:500}.execution-record li>details{margin-top:6px}.execution-record .highlighted-step{background:#e5f0db;border-color:#66985e}.retry{font-size:11px;margin-top:10px}.pane-divider{background:#eff2eb;cursor:col-resize;border-left:1px solid #dfe6d9;touch-action:none}.pane-divider:focus-visible,.pane-divider:hover{background:#9cbc96}.artifact-pane{display:flex;flex-direction:column;min-height:0;background:#fff;min-width:0}.pane-title{display:flex;align-items:center;justify-content:space-between;padding:29px 25px 20px;gap:14px;min-height:112px}.pane-title h2{margin:7px 0 0;font-size:19px;letter-spacing:-.4px}.pane-title .icon-button{background:#f4f7f0;border:1px solid #e4e9dd;border-radius:5px;color:#7c8e70}.tabs{display:flex;gap:0;border-bottom:1px solid #e7ebe1;padding:0 20px}.tabs button{border:0;border-radius:0;padding:12px 9px;font-size:11px;color:#617455;background:none}.tabs button[aria-selected=true]{color:#3d7446;border-bottom:2px solid #619968}.tabs span{font-size:9px;padding:1px 4px;background:#f1f4ec;margin-left:6px;border-radius:3px}.pane-content{padding:20px 25px;overflow:auto;flex:1;scrollbar-gutter:stable;min-height:0}.empty-pane{text-align:center;margin:20vh auto 0;max-width:260px;color:#83907a}.empty-pane h3{font-size:15px;color:#708363;font-weight:500;margin:20px 0 12px}.empty-pane p{font-size:11px;line-height:1.9;color:#637554}.empty-icon{display:inline-block;font-size:34px;color:#adc3a1;border:1px solid #e7eddf;border-radius:12px;width:64px;padding:4px;background:#f8faf4}.revision-meta{display:flex;align-items:center;gap:10px;flex-wrap:wrap;margin:10px 0 22px}.revision-meta small{font:9px Consolas,monospace;width:100%}.source-card{display:block;width:100%;background:#f9fbf6;border:1px solid #e3e9db;padding:12px;text-align:left;margin:9px 0;font-size:11px;line-height:1.7}.source-card small{display:block;margin-top:6px;font-size:9px}.source-card.active{border-color:#6e9d69;background:#edf5e8}.source-detail{border-top:1px solid #dfe8d8;margin-top:24px;padding-top:21px}.source-detail h3{font-size:14px;line-height:1.65}.source-detail h4{font-size:12px;margin:22px 0 10px}.source-detail a{font-size:11px}.source-text{font-size:11px;max-height:400px}.material-preview{max-width:100%;height:auto}.field{display:flex;flex-direction:column;gap:6px;font-size:11px;color:#677b5d;margin-bottom:16px}.field input,.field select,.field textarea{width:100%;padding:9px 10px;border:1px solid #d9e2d1;border-radius:5px;background:#fff;color:#345131;min-width:0}.form-grid{display:grid;grid-template-columns:1fr 1fr;gap:15px}.check{display:flex;align-items:center;gap:9px;font-size:12px;margin:10px 0 18px}.modal{width:min(520px,calc(100vw - 32px));max-height:calc(100dvh - 48px);border:1px solid #dce5d4;padding:25px;border-radius:12px;box-shadow:0 22px 70px #15251029;color:#31492e;overflow:auto}.modal.wide{width:min(690px,calc(100vw - 28px))}.modal::backdrop{background:#10231870;backdrop-filter:blur(3px)}.modal>header{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px}.modal>header h2{margin:0;font-size:19px}.modal>header button{font-size:24px;line-height:1;background:none;border:0}.modal p{font-size:12px;line-height:1.85}.modal h3{margin-top:24px}.modal .primary{font-size:12px}.archive-button{margin-top:22px;border-color:#dfcfab;color:#86682e;font-size:12px}.provider-card{display:flex;align-items:center;gap:8px;border:1px solid #e2e8dc;background:#f8faf4;border-radius:7px;padding:13px;margin:12px 0}.provider-card>div{flex:1;min-width:0}.provider-card p{font-size:10px;color:#7d8d72;overflow-wrap:anywhere;margin:4px 0 7px}.provider-card button{font-size:10px;padding:5px 8px}.skill-card{border:1px solid #e0e8d8;border-radius:7px;padding:16px;margin:18px 0}.skill-card h3{margin-top:0}.skill-card button{font-size:11px;margin-top:14px}.drawer-navigation{display:flex;flex-direction:column;min-height:60vh;background:#123b39;color:#d0dfd7;padding:20px;border-radius:8px}.drawer-navigation .run-list{max-height:30vh}.drawer-artifacts .pane-title{padding:10px 0 20px;min-height:90px}.drawer-artifacts .tabs{padding:0}.drawer-artifacts .pane-content{padding:20px 0}.drawer-artifacts .empty-pane{margin:15vh auto}.mobile-nav,.mobile-artifacts{display:none}.launch{min-height:100dvh;display:grid;align-content:center;justify-items:center;padding:30px;text-align:center}.launch h1{margin-top:20px}.launch p{max-width:550px;color:#809173}.nav-collapsed{grid-template-columns:0 minmax(360px,1fr) 5px var(--artifact-width)}.nav-collapsed .sidebar{visibility:hidden;padding:0;overflow:hidden} +@media(min-width:1001px) and (max-width:1250px){.workspace{grid-template-columns:195px minmax(350px,1fr) 5px 330px}.research-header{padding:25px}.conversation-panel{padding:0 24px 20px}.topbar{padding:0 19px;gap:10px}.breadcrumb strong{display:none}.connection{font-size:9px}.composer{margin:0 21px}.composer select{max-width:125px}.composer select:nth-of-type(2){max-width:100px}.nav-brand>span{display:none}.nav-collapsed{grid-template-columns:0 minmax(350px,1fr) 5px 330px}.pane-title{padding-left:20px;padding-right:20px}.pane-content{padding:18px}} +@media(max-width:1000px){.workspace,.nav-collapsed{display:block;height:100dvh}.sidebar,.artifact-pane,.pane-divider,.desktop-toggle{display:none!important}.main-panel{height:100dvh}.mobile-nav,.mobile-artifacts{display:inline-block}.topbar{padding:0 18px;min-height:57px;gap:10px}.breadcrumb>strong,.breadcrumb>span:nth-child(2){display:none}.breadcrumb>span:first-child{max-width:140px}.connection{margin-left:auto}.research-header{padding:25px 25px 20px}.research-header h1{font-size:25px}.conversation-panel{padding:0 24px 20px}.composer{margin:0 22px}.welcome{margin-top:9vh}.composer select{max-width:160px}.composer select:nth-of-type(2){max-width:150px}.workspace-footer{padding:10px 25px}.research-header p{font-size:11px}} +@media(max-width:550px){.topbar{padding:0 11px;gap:5px}.topbar button{font-size:11px}.breadcrumb{display:none}.connection{font-size:9px}.research-header{padding:24px 18px 16px}.research-header .eyebrow{font-size:8px}.research-header h1{font-size:23px}.research-header button{font-size:10px;padding:5px 8px}.research-header p{font-size:10px}.conversation-panel{padding:0 17px 17px}.welcome{margin-top:5vh;margin-bottom:20px}.welcome h2{font-size:21px}.welcome>p{font-size:12px}.research-symbol{font-size:36px;margin-bottom:17px}.suggestions button{font-size:11px;padding:11px}.composer{margin:0 12px;padding:12px}.composer-controls{flex-wrap:wrap;gap:6px}.composer select{max-width:135px;font-size:9px}.composer select:nth-of-type(2){max-width:100px}.composer .primary{font-size:10px;margin-left:auto;padding:7px 10px}.composer .spacer{display:none}.composer .icon-button{font-size:20px;padding:3px}.composer small{font-size:8px}.workspace-footer{font-size:8px;padding:9px 14px}.workspace-footer a,.workspace-footer button{font-size:8px}.run-status{gap:6px}.run-status .link{margin-left:auto}.run-status>span:nth-child(2){display:none}.modal{padding:20px}.form-grid{grid-template-columns:1fr;gap:0}.provider-card{flex-wrap:wrap}.provider-card>div{min-width:100%}.interaction{padding:14px}.markdown{font-size:12px}} +@media(prefers-reduced-motion:reduce){*{scroll-behavior:auto!important}} diff --git a/web/ui.tsx b/web/ui.tsx new file mode 100644 index 0000000..bf29498 --- /dev/null +++ b/web/ui.tsx @@ -0,0 +1,18 @@ +import { useEffect, useRef } from 'react'; +import type { ReactNode } from 'react'; +import Markdown, { defaultUrlTransform } from 'react-markdown'; + +export function ReportText({ children, onSource }: { children: string; onSource?: (id: string) => void }) { + return
; +} +export function Modal({ title, children, close, wide = false }: { title: string; children: ReactNode; close: () => void; wide?: boolean }) { + const ref = useRef(null); + useEffect(() => { const element = ref.current!; element.showModal(); return () => element.close(); }, []); + return { event.preventDefault(); close(); }} aria-label={title}> +

{title}

{children} +
; +} +export function Tag({ children, tone = '' }: { children: ReactNode; tone?: string }) { return {children}; } diff --git a/web/workspace.tsx b/web/workspace.tsx new file mode 100644 index 0000000..9c12994 --- /dev/null +++ b/web/workspace.tsx @@ -0,0 +1,226 @@ +import { lazy, Suspense, useCallback, useEffect, useRef, useState } from 'react'; +import type { CSSProperties, FormEvent } from 'react'; +import type { Interaction, Json, ObjectData, ResearchEvent, Run } from '../app/contracts'; +import type { ProviderEntry } from '../app/settings'; +import { api, initializeSession, uploadMaterial } from './api'; +import { Modal, ReportText, Tag } from './ui'; +import { Diagnostics, ExecutionLimits } from './diagnostics'; + +interface Project { id: string; name: string; archived: boolean; question?: string; discipline?: string; method?: string } +interface ConversationRecord { id: string; projectId: string; name: string } +interface Snapshot { project: Project; conversations: ConversationRecord[]; runs: Run[]; artifacts: ObjectData[]; sources: ObjectData[]; revisions: ObjectData[]; materials: ObjectData[] } +type Detail = Run & { events: ResearchEvent[] }; +const ended = new Set(['succeeded', 'failed', 'cancelled', 'interrupted']); +const Conversation = lazy(() => import('./conversation').then(module => ({ default: module.Conversation }))); +const stateNames: Record = { + queued: ['Queued', '排队中'], running: ['Running', '执行中'], awaiting_confirmation: ['Review the plan', '请确认计划'], awaiting_input: ['Your answer is needed', '等待回答'], awaiting_permission: ['Permission requested', '等待授权'], cancelling: ['Stopping the worker', '正在停止进程'], succeeded: ['Completed · review evidence', '已完成 · 请审阅证据'], failed: ['Failed · partial work saved', '失败 · 保留已保存内容'], cancelled: ['Cancelled · worker stopped', '已取消 · 进程已停止'], interrupted: ['Interrupted · retry available', '已中断 · 可以重试'], +}; +export function App() { + const [language, setLanguage] = useState(localStorage.getItem('openlab-language') === 'en' ? 'en' : 'zh'); + const zh = language === 'zh'; + const t = (en: string, cn: string) => zh ? cn : en; + const [ready, setReady] = useState(false); + const [connected, setConnected] = useState(true); + const [error, setError] = useState(''); + const [busy, setBusy] = useState(false); + const [projects, setProjects] = useState([]); + const [projectId, setProjectId] = useState(localStorage.getItem('openlab-project') ?? ''); + const [snapshot, setSnapshot] = useState(null); + const [conversationId, setConversationId] = useState(''); + const [runId, setRunId] = useState(''); + const [detail, setDetail] = useState(null); + const [providers, setProviders] = useState([]); + const [providerId, setProviderId] = useState(''); + const [integrations, setIntegrations] = useState([]); + const [skillId, setSkillId] = useState('builtin-literature'); + const [executionLimits, setExecutionLimits] = useState({ tokens: 30_000, turns: 12 }); + const [drafts, setDrafts] = useState>(() => { try { return JSON.parse(sessionStorage.getItem('openlab-drafts') ?? '{}'); } catch { return {}; } }); + const [modal, setModal] = useState<'project' | 'rename' | 'settings' | 'skills' | null>(null); + const [drawer, setDrawer] = useState<'projects' | 'artifacts' | null>(null); + const [navCollapsed, setNavCollapsed] = useState(false); + const [paneWidth, setPaneWidth] = useState(410); + const [tab, setTab] = useState<'reports' | 'sources' | 'materials'>('reports'); + const [sourceId, setSourceId] = useState(''); + const [revisionId, setRevisionId] = useState(''); + const [revision, setRevision] = useState<{ revision: ObjectData; content: string } | null>(null); + const [materialId, setMaterialId] = useState(''); + const [streamKey, setStreamKey] = useState(0); + const [highlightedStep, setHighlightedStep] = useState(''); + const inputFile = useRef(null); + const importFile = useRef(null); + const projectRef = useRef(projectId); + const selectedRunRef = useRef(runId); + const pendingRequest = useRef<{ fingerprint: string; key: string } | null>(null); + projectRef.current = projectId; selectedRunRef.current = runId; + const perform = async (action: () => Promise) => { setBusy(true); setError(''); try { await action(); } catch (failure) { setError(failure instanceof Error ? failure.message : 'Operation failed'); } finally { setBusy(false); } }; + const refreshCatalog = async () => { + const [list, configured, installed] = await Promise.all([api('/projects'), api('/providers'), api('/integrations')]); + setProjects(list); setProviders(configured); setIntegrations(installed); + setProviderId(current => configured.some(p => p.id === current) ? current : configured[0]?.id ?? ''); + setProjectId(current => list.some(p => p.id === current) ? current : list[0]?.id ?? ''); + }; + const refreshProject = async (selectedId = projectRef.current) => { + if (!selectedId) return; + const data = await api(`/projects/${selectedId}`); + if (projectRef.current !== selectedId) return; + setSnapshot(data); setConnected(true); + setConversationId(current => data.conversations.some(c => c.id === current) ? current : data.conversations[0]?.id ?? ''); + }; + useEffect(() => { void initializeSession().then(refreshCatalog).then(() => setReady(true)).catch(failure => { setError(failure.message); setConnected(false); }); }, []); + useEffect(() => { document.documentElement.lang = language; localStorage.setItem('openlab-language', language); }, [language]); + useEffect(() => { sessionStorage.setItem('openlab-drafts', JSON.stringify(drafts)); }, [drafts]); + useEffect(() => { + if (!ready || !projectId) return; + localStorage.setItem('openlab-project', projectId); + setSnapshot(null); setDetail(null); setRunId(''); setConversationId(''); setRevisionId(''); setRevision(null); setSourceId(''); + void refreshProject(projectId).catch(failure => { setError(failure.message); setConnected(false); }); + }, [ready, projectId]); + useEffect(() => { + if (!snapshot || !conversationId) return; + const runs = snapshot.runs.filter(r => r.conversationId === conversationId); + setRunId(current => runs.some(r => r.id === current) ? current : runs.at(-1)?.id ?? ''); + }, [conversationId, snapshot?.project.id, snapshot?.runs.length]); + useEffect(() => { + if (!runId) { setDetail(null); return; } + let stopped = false; + const update = async () => { + try { + const current = await api(`/runs/${runId}`); + if (stopped) return; + setDetail(current); setConnected(true); await refreshProject(); + } catch { if (!stopped) setConnected(false); } + }; + void update(); const timer = setInterval(() => { void update(); }, 1000); + return () => { stopped = true; clearInterval(timer); }; + }, [runId]); + useEffect(() => { + if (!snapshot || revisionId) return; + const latest = snapshot.artifacts.filter(a => !runId || a.runId === runId).at(-1); + if (latest) setRevisionId(String(latest.latest)); + }, [snapshot, revisionId, runId]); + useEffect(() => { + if (!revisionId || !snapshot) { setRevision(null); return; } + const record = snapshot.revisions.find(r => r.id === revisionId); + if (!record) return; + let current = true; + void api<{ revision: ObjectData; content: string }>(`/artifacts/${String(record.artifactId)}/revisions/${revisionId}`).then(data => { if (current) setRevision(data); }).catch(failure => { if (current) setError(failure.message); }); + return () => { current = false; }; + }, [revisionId, snapshot?.project.id]); + useEffect(() => { + if (!highlightedStep) return; + const timeout = setTimeout(() => { document.getElementById(`step-${highlightedStep}`)?.scrollIntoView({ block: 'center' }); }, 200); + return () => clearTimeout(timeout); + }, [highlightedStep, detail?.id]); + const openSource = useCallback((source: string) => { setSourceId(source); setTab('sources'); if (matchMedia('(max-width: 1000px)').matches) setDrawer('artifacts'); }, []); + const currentRun = detail?.id === runId ? detail : snapshot?.runs.find(r => r.id === runId); + const active = snapshot?.runs.find(r => r.conversationId === conversationId && !ended.has(r.state)); + const draft = drafts[conversationId] ?? ''; + const selectConversation = (selected: string) => { setConversationId(selected); setRunId(''); setDetail(null); setRevisionId(''); setSourceId(''); setDrawer(null); }; + const selectRun = (run: Run) => { setConversationId(run.conversationId); setRunId(run.id); setDetail(null); setRevisionId(''); }; + const createConversation = async () => { + if (!snapshot) return; + const created = await api(`/projects/${projectId}/conversations`, 'POST', { name: t(`Research ${snapshot.conversations.length + 1}`, `研究 ${snapshot.conversations.length + 1}`) }); + await refreshProject(); selectConversation(created.id); + }; + const startRun = async (question = draft, parentRunId?: string) => { + const fingerprint = JSON.stringify({ conversationId, question, providerId, skillId, parentRunId, executionLimits }); + if (pendingRequest.current?.fingerprint !== fingerprint) pendingRequest.current = { fingerprint, key: crypto.randomUUID() }; + const created = await api(`/conversations/${conversationId}/runs`, 'POST', { question, requestKey: pendingRequest.current.key, providerId, skillId, language, limits: executionLimits, ...(parentRunId ? { parentRunId } : {}) }); + pendingRequest.current = null; + setDrafts(current => ({ ...current, [conversationId]: '' })); await refreshProject(); setRunId(created.id); setRevisionId(''); setStreamKey(value => value + 1); + }; + const decision = async (interaction: Interaction, value: Json) => { + await api(`/runs/${runId}/interactions/${interaction.id}/response`, 'POST', { version: interaction.version, value }); + setDetail(await api(`/runs/${runId}`)); + }; + const navigateStep = (targetRun: string, step: string) => { + const run = snapshot?.runs.find(r => r.id === targetRun); + if (run) { selectRun(run); setHighlightedStep(step); setDrawer(null); } + }; + const selectedSource = snapshot?.sources.find(source => source.id === sourceId || source.originalId === sourceId); + const selectedMaterial = snapshot?.materials.find(material => material.id === materialId); + const reportArtifact = snapshot?.artifacts.find(artifact => artifact.id === revision?.revision.artifactId); + const events = detail?.id === runId ? detail.events : []; + const usage = events.filter(event => event.kind === 'usage').at(-1)?.data; + const stateLabel = (state: string) => stateNames[state]?.[zh ? 1 : 0] ?? state; + + const navigation = <> +
OpenLabRESEARCH
+ +
{t('YOUR PROJECTS', '项目')}
+ + {snapshot && <>
{t('CONVERSATIONS', '对话')}
+ +
{t('RUN HISTORY', '运行历史')}
} +
{t('Local workspace · Your records', '本地工作区 · 数据由你持有')}
+ ; + const artifactsPane = <> +
{t('RESEARCH RECORD', '研究档案')}

{t('Artifacts & evidence', '产物与证据')}

{snapshot && ↓}
+
{(['reports', 'sources', 'materials'] as const).map(value => )}
+
+ {tab === 'reports' && <>{!snapshot?.artifacts.length ?
▤

{t('A place for your findings', '留存每一步研究成果')}

{t('Saved drafts and report revisions will appear here, with their sources and producing steps.', '已保存的草稿、报告修订、引用来源和生成步骤会在这里汇集。')}

: <> + + {reportArtifact && reportArtifact.latest !== revisionId && } + {revision && <>
{revision.revision.committed ? t('Immutable revision', '不可变修订') : t('Saved draft · incomplete', '已保存草稿 · 未完成')}SHA-256 {String(revision.revision.hash).slice(0, 12)}…
{revision.content}} + }} + {tab === 'sources' && <>{!snapshot?.sources.length &&
⌕

{t('Follow the evidence', '追溯每一条证据')}

{t('Retrieved records keep original titles, verification scope and access limitations.', '检索记录保留原始标题、核实范围与访问限制。')}

} + {sourceId && !selectedSource &&

{t('This citation is not present in the selected project.', '当前项目中找不到此引用。')}

} + {snapshot?.sources.map(source => )} + {selectedSource &&

{String(selectedSource.title)}

{String(selectedSource.verification) === 'metadata_verified' ? t('Metadata verified', '元数据已核实') : t('Fetched page content', '已获取页面内容')}

{t('Model interpretation is unchecked. A metadata record or landing page does not verify the paper’s claims.', '模型解读未经核实。元数据或论文介绍页面不能证明论文支持某个论点。')}

DOI
{String(selectedSource.doi)}
{t('Full text', '全文')}
{String(selectedSource.fullText)}
{t('Open publisher / DOI ↗', '打开出版方 / DOI ↗')} + {selectedSource.contentScope &&

{String(selectedSource.contentScope)}

}{selectedSource.content &&
{t('Fetched text', '已读取文本')}
{String(selectedSource.content)}
} +

{t('Provenance', '来源记录')}

{(Array.isArray(selectedSource.accesses) ? selectedSource.accesses : []).map((value, index) => { const access = value as ObjectData; return ; })}
} + } + {tab === 'materials' && <>

{t('Copied into this project. Uploaded code and active HTML are never executed.', '附件会复制到当前项目。上传的代码和活动 HTML 不会执行。')}

{snapshot?.materials.map(material => )} + {selectedMaterial &&

{String(selectedMaterial.name)}

{typeof selectedMaterial.preview === 'string' ?
{selectedMaterial.preview}
: ['image/png', 'image/jpeg'].includes(String(selectedMaterial.contentType)) ? :

{t('Preview unavailable. Download to open with a suitable application.', '暂不支持预览,可下载后使用相应应用打开。')}

}{t('Download original', '下载原文件')}
} + } +
+ ; + + if (!ready) return
OpenLab

OpenLab

{error || t('Connecting to your local workspace…', '正在连接本地工作区…')}

{error && }
; + return
+ +
+
{snapshot?.project.name ?? 'OpenLab'}/{t('Research workspace', '研究工作区')}
{connected ? t('Local · connected', '本地 · 已连接') : t('Disconnected', '连接已断开')}
+ {error &&
{error}
} + {!connected &&
{t('Execution may continue. Reconnect to read the recorded state.', '执行可能仍在继续。重新连接以读取已记录的状态。')}
} +
{t('OPEN RESEARCH, TRACEABLE EVIDENCE', '开放研究 · 证据可循')}

{currentRun ? t('Research workspace', '研究工作台') : t('Start with a question.', '从一个问题开始。')}

{t('Keep your question, process and evidence in one place.', '让研究问题、执行过程与来源证据始终相连。')}

{snapshot && }
+
+ {currentRun ? <>
{stateLabel(currentRun.state)}{currentRun.phase === 'planning' ? t('Planning', '计划阶段') : currentRun.phase === 'research' ? t('Research', '研究阶段') : t('Recorded', '已记录')}{!ended.has(currentRun.state) && }
+ {t('Loading conversation…', '正在加载对话…')}

}>
+ {currentRun.error &&

{currentRun.error}

} + {currentRun.interaction && void perform(() => decision(currentRun.interaction!, value))} />} +
{t('Execution record', '执行记录')} · {events.filter(event => event.kind === 'step_finished').length} {t('model steps completed', '个模型步骤已完成')} · {usage ? `${String(usage.totalReported ?? '?')} ${t('reported tokens', '已报告 token')}${usage.unknownUsage ? t(' + unknown usage', ' + 部分用量未知') : ''}` : t('Usage unknown', '用量未知')}
    {events.filter(event => ['step_started', 'tool_intent', 'tool_result', 'interaction_answered', 'artifact_saved', 'run_finished'].includes(event.kind)).map(event =>
  1. {String(event.data.label ?? event.data.name ?? (event.kind === 'artifact_saved' ? t('Report saved', '报告已保存') : event.kind === 'tool_result' ? t('Tool result saved', '工具结果已保存') : event.kind === 'interaction_answered' ? t('Decision recorded', '已记录决定') : stateLabel(String(event.data.state))))}
    {t('Details', '详情')}
    {JSON.stringify(event.data, null, 2)}
  2. )}
+ {ended.has(currentRun.state) && !active && } + :
✳

{t('What would you like to investigate?', '你想探索什么问题?')}

{t('Choose a research question. Review a plan, follow the sources, and build a record you can keep.', '提出研究问题,确认研究计划,沿着来源追溯证据,留下可持有的研究档案。')}

{[t('How does the minimum wage affect employment?', '最低工资如何影响就业?'), t('What themes appear in Hamlet scholarship?', '《哈姆雷特》文本研究有哪些主题?')].map(question => )}
} +
+
{ event.preventDefault(); void perform(() => startRun()); }}>