saeeol 1.0.4 → 1.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "name": "saeeol",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -0,0 +1,14 @@
1
+ import type { Addon } from "./types"
2
+
3
+ // ── analytics: 사용량 통계 + 모델 테스트 + PR ────────────────────────
4
+ export const analytics: Addon = {
5
+ id: "analytics",
6
+ category: "analytics",
7
+ minTier: "master",
8
+ required: false,
9
+ commands: [
10
+ () => import("../cli/cmd/stats").then((m) => ({ StatsCommand: m.StatsCommand })),
11
+ () => import("../cli/cmd/pr").then((m) => ({ PrCommand: m.PrCommand })),
12
+ () => import("../saeeol/cli/cmd/roll-call").then((m) => ({ RollCallCommand: m.RollCallCommand })),
13
+ ],
14
+ }
@@ -0,0 +1,15 @@
1
+ import type { Addon } from "./types"
2
+
3
+ // ── core: TUI 채팅 + 프롬프트 실행 ──────────────────────────────────
4
+ export const core: Addon = {
5
+ id: "core",
6
+ category: "core",
7
+ minTier: "light",
8
+ required: true,
9
+ commands: [
10
+ () => import("../cli/cmd/tui/thread").then((m) => ({ TuiThreadCommand: m.TuiThreadCommand })),
11
+ () => import("../cli/cmd/run").then((m) => ({ RunCommand: m.RunCommand })),
12
+ () => import("../cli/cmd/generate").then((m) => ({ GenerateCommand: m.GenerateCommand })),
13
+ () => import("../cli/cmd/session").then((m) => ({ SessionCommand: m.SessionCommand })),
14
+ ],
15
+ }
@@ -0,0 +1,14 @@
1
+ import type { Addon } from "./types"
2
+
3
+ // ── data: 세션 내보내기/가져오기 + DB ───────────────────────────────
4
+ export const data: Addon = {
5
+ id: "data",
6
+ category: "data",
7
+ minTier: "code",
8
+ required: true,
9
+ commands: [
10
+ () => import("../cli/cmd/export").then((m) => ({ ExportCommand: m.ExportCommand })),
11
+ () => import("../cli/cmd/import").then((m) => ({ ImportCommand: m.ImportCommand })),
12
+ () => import("../cli/cmd/db").then((m) => ({ DbCommand: m.DbCommand })),
13
+ ],
14
+ }
@@ -0,0 +1,13 @@
1
+ import type { Addon } from "./types"
2
+
3
+ // ── dev: 에이전트 관리 + 디버깅 ─────────────────────────────────────
4
+ export const dev: Addon = {
5
+ id: "dev",
6
+ category: "dev",
7
+ minTier: "code",
8
+ required: false,
9
+ commands: [
10
+ () => import("../cli/cmd/agent").then((m) => ({ AgentCommand: m.AgentCommand })),
11
+ () => import("../cli/cmd/debug").then((m) => ({ DebugCommand: m.DebugCommand })),
12
+ ],
13
+ }
@@ -0,0 +1,13 @@
1
+ import type { Addon } from "./types"
2
+
3
+ // ── lifecycle: 설치/업그레이드/제거 ──────────────────────────────────
4
+ export const lifecycle: Addon = {
5
+ id: "lifecycle",
6
+ category: "lifecycle",
7
+ minTier: "light",
8
+ required: true,
9
+ commands: [
10
+ () => import("../cli/cmd/upgrade").then((m) => ({ UpgradeCommand: m.UpgradeCommand })),
11
+ () => import("../cli/cmd/uninstall").then((m) => ({ UninstallCommand: m.UninstallCommand })),
12
+ ],
13
+ }
@@ -0,0 +1,15 @@
1
+ import type { Addon } from "./types"
2
+
3
+ // ── llm: LLM provider 설정 + 모델 관리 ──────────────────────────────
4
+ export const llm: Addon = {
5
+ id: "llm",
6
+ category: "llm",
7
+ minTier: "light",
8
+ required: true,
9
+ commands: [
10
+ () => import("../cli/cmd/init").then((m) => ({ InitCommand: m.InitCommand })),
11
+ () => import("../cli/cmd/providers").then((m) => ({ ProvidersCommand: m.ProvidersCommand })),
12
+ () => import("../cli/cmd/models").then((m) => ({ ModelsCommand: m.ModelsCommand })),
13
+ () => import("../cli/cmd/config").then((m) => ({ ConfigCLICommand: m.ConfigCommand })),
14
+ ],
15
+ }
@@ -0,0 +1,12 @@
1
+ import type { Addon } from "./types"
2
+
3
+ // ── remote: 실시간 세션 릴레이 ──────────────────────────────────────
4
+ export const remote: Addon = {
5
+ id: "remote",
6
+ category: "remote",
7
+ minTier: "master",
8
+ required: false,
9
+ commands: [
10
+ () => import("../cli/cmd/remote").then((m) => ({ RemoteCommand: m.RemoteCommand })),
11
+ ],
12
+ }
@@ -0,0 +1,14 @@
1
+ import type { Addon } from "./types"
2
+
3
+ // ── server: headless 서버 + 원격 연결 + ACP ─────────────────────────
4
+ export const server: Addon = {
5
+ id: "server",
6
+ category: "server",
7
+ minTier: "code",
8
+ required: true,
9
+ commands: [
10
+ () => import("../cli/cmd/serve").then((m) => ({ ServeCommand: m.ServeCommand })),
11
+ () => import("../cli/cmd/tui/attach").then((m) => ({ AttachCommand: m.AttachCommand })),
12
+ () => import("../cli/cmd/acp").then((m) => ({ AcpCommand: m.AcpCommand })),
13
+ ],
14
+ }
@@ -0,0 +1,13 @@
1
+ import type { Addon } from "./types"
2
+
3
+ // ── tools: MCP + 플러그인 시스템 ────────────────────────────────────
4
+ export const tools: Addon = {
5
+ id: "tools",
6
+ category: "tools",
7
+ minTier: "code",
8
+ required: true,
9
+ commands: [
10
+ () => import("../cli/cmd/mcp").then((m) => ({ McpCommand: m.McpCommand })),
11
+ () => import("../cli/cmd/plug").then((m) => ({ PluginCommand: m.PluginCommand })),
12
+ ],
13
+ }
@@ -0,0 +1,12 @@
1
+ import type { Addon } from "./types"
2
+
3
+ // ── web: 브라우저 채팅 UI ───────────────────────────────────────────
4
+ export const web: Addon = {
5
+ id: "web",
6
+ category: "web",
7
+ minTier: "master",
8
+ required: true,
9
+ commands: [
10
+ () => import("../cli/cmd/web").then((m) => ({ WebCommand: m.WebCommand })),
11
+ ],
12
+ }
@@ -0,0 +1,65 @@
1
+ import type { Addon, Tier } from "./types"
2
+ import { core } from "./addon-core"
3
+ import { llm } from "./addon-llm"
4
+ import { lifecycle } from "./addon-lifecycle"
5
+ import { server } from "./addon-server"
6
+ import { tools } from "./addon-tools"
7
+ import { data } from "./addon-data"
8
+ import { dev } from "./addon-dev"
9
+ import { web } from "./addon-web"
10
+ import { remote } from "./addon-remote"
11
+ import { analytics } from "./addon-analytics"
12
+
13
+ // ╔══════════════════════════════════════════════════════════════════╗
14
+ // ║ 애드온 레지스트리 ║
15
+ // ║ ║
16
+ // ║ LIGHT = core + llm + lifecycle (3 addons, 10 cmd) ║
17
+ // ║ CODE = LIGHT + server + tools + data + dev (8 addons, 20 cmd) ║
18
+ // ║ MASTER = CODE + web + remote + analytics (11 addons, 24 cmd)║
19
+ // ╚══════════════════════════════════════════════════════════════════╝
20
+
21
+ export const allAddons: Addon[] = [
22
+ // LIGHT
23
+ core, // TUI 채팅, run, generate, session
24
+ llm, // init, auth, models, config
25
+ lifecycle, // upgrade, uninstall
26
+
27
+ // CODE
28
+ server, // serve, attach, acp
29
+ tools, // mcp, plugin
30
+ data, // export, import, db
31
+ dev, // agent, debug
32
+
33
+ // MASTER
34
+ web, // web UI
35
+ remote, // real-time relay
36
+ analytics, // stats, pr, roll-call
37
+ ]
38
+
39
+ const tierPriority: Record<Tier, number> = { light: 0, code: 1, master: 2 }
40
+
41
+ export function addonsForTier(tier: Tier): Addon[] {
42
+ return allAddons.filter((a) => tierPriority[tier] >= tierPriority[a.minTier])
43
+ }
44
+
45
+ export async function commandsForTier(tier: Tier): Promise<any[]> {
46
+ const addons = addonsForTier(tier)
47
+ const commands: any[] = []
48
+ for (const addon of addons) {
49
+ for (const loader of addon.commands) {
50
+ const mod = await loader()
51
+ commands.push(...Object.values(mod))
52
+ }
53
+ }
54
+ return commands
55
+ }
56
+
57
+ export function manifest(tier: Tier) {
58
+ const addons = addonsForTier(tier)
59
+ return addons.map((a) => ({
60
+ id: a.id,
61
+ category: a.category,
62
+ commands: a.commands.length,
63
+ required: a.required,
64
+ }))
65
+ }
@@ -0,0 +1,31 @@
1
+ // ╔══════════════════════════════════════════════════════════════════╗
2
+ // ║ SAEEOL 애드온 시스템 ║
3
+ // ║ ║
4
+ // ║ 원자 단위 기능 → 애드온 → 티어별 자동 활성화 ║
5
+ // ║ ║
6
+ // ║ LIGHT = core + llm + lifecycle ║
7
+ // ║ CODE = LIGHT + server + tools + data + dev ║
8
+ // ║ MASTER = CODE + web + remote + analytics ║
9
+ // ╚══════════════════════════════════════════════════════════════════╝
10
+
11
+ export type Tier = "light" | "code" | "master"
12
+
13
+ export interface Addon {
14
+ /** 애드온 ID */
15
+ id: string
16
+ /** 분류 */
17
+ category: "core" | "llm" | "lifecycle" | "server" | "tools" | "data" | "web" | "remote" | "analytics" | "dev"
18
+ /** 포함 명령어 */
19
+ commands: (() => Promise<Record<string, any>>)[]
20
+ /** 필요 티어 (이 티어부터 활성화) */
21
+ minTier: Tier
22
+ /** 필수/선택 */
23
+ required: boolean
24
+ }
25
+
26
+ // 티어 우선순위
27
+ const tierPriority: Record<Tier, number> = { light: 0, code: 1, master: 2 }
28
+
29
+ export function tierActive(addon: Addon, tier: Tier): boolean {
30
+ return tierPriority[tier] >= tierPriority[addon.minTier]
31
+ }
package/src/index.ts CHANGED
@@ -1,67 +1,97 @@
1
1
  import yargs from "yargs"
2
2
  import { hideBin } from "yargs/helpers"
3
+
4
+ // ╔══════════════════════════════════════════════════════════════════╗
5
+ // ║ SAEEOL CLI — 애드온 아키텍처 ║
6
+ // ║ ║
7
+ // ║ 원자 단위 애드온 → 티어별 자동 활성화 ║
8
+ // ║ ║
9
+ // ║ LIGHT = core + llm + lifecycle ║
10
+ // ║ CODE = LIGHT + server + tools + data + dev ║
11
+ // ║ MASTER = CODE + web + remote + analytics ║
12
+ // ╚══════════════════════════════════════════════════════════════════╝
13
+
14
+ // ── LIGHT: core ────────────────────────────────────────────────────
15
+ import { TuiThreadCommand } from "./cli/cmd/tui/thread"
3
16
  import { RunCommand } from "./cli/cmd/run"
4
17
  import { GenerateCommand } from "./cli/cmd/generate"
5
- import * as Log from "@saeeol/core/util/log"
6
- // import { LoginCommand, LogoutCommand, SwitchCommand, OrgsCommand } from "./cli/cmd/account"
7
- // import { ConsoleCommand } from "./cli/cmd/account"
8
- import { ConsoleCommand } from "./cli/cmd/account"
18
+ import { SessionCommand } from "./cli/cmd/session"
19
+
20
+ // ── LIGHT: llm ─────────────────────────────────────────────────────
21
+ import { InitCommand } from "./cli/cmd/init"
9
22
  import { ProvidersCommand } from "./cli/cmd/providers"
10
- import { AgentCommand } from "./cli/cmd/agent"
23
+ import { ModelsCommand } from "./cli/cmd/models"
24
+ import { ConfigCommand as ConfigCLICommand } from "./cli/cmd/config"
25
+
26
+ // ── LIGHT: lifecycle ───────────────────────────────────────────────
11
27
  import { UpgradeCommand } from "./cli/cmd/upgrade"
12
28
  import { UninstallCommand } from "./cli/cmd/uninstall"
13
- import { ModelsCommand } from "./cli/cmd/models"
29
+
30
+ // ── CODE: server ───────────────────────────────────────────────────
31
+ import { ServeCommand } from "./cli/cmd/serve"
32
+ import { AttachCommand } from "./cli/cmd/tui/attach"
33
+ import { AcpCommand } from "./cli/cmd/acp"
34
+
35
+ // ── CODE: tools ────────────────────────────────────────────────────
36
+ import { McpCommand } from "./cli/cmd/mcp"
37
+ import { PluginCommand } from "./cli/cmd/plug"
38
+
39
+ // ── CODE: data ─────────────────────────────────────────────────────
40
+ import { ExportCommand } from "./cli/cmd/export"
41
+ import { ImportCommand } from "./cli/cmd/import"
42
+ import { DbCommand } from "./cli/cmd/db"
43
+
44
+ // ── CODE: dev (선택) ───────────────────────────────────────────────
45
+ import { AgentCommand } from "./cli/cmd/agent"
46
+ import { DebugCommand } from "./cli/cmd/debug"
47
+
48
+ // ── MASTER: web ────────────────────────────────────────────────────
49
+ import { WebCommand } from "./cli/cmd/web"
50
+
51
+ // ── MASTER: remote (선택) ──────────────────────────────────────────
52
+ import { RemoteCommand } from "./cli/cmd/remote"
53
+
54
+ // ── MASTER: analytics (선택) ───────────────────────────────────────
55
+ import { StatsCommand } from "./cli/cmd/stats"
56
+ import { PrCommand } from "./cli/cmd/pr"
57
+ import { RollCallCommand } from "./saeeol/cli/cmd/roll-call"
58
+
59
+ // ── DEV 전용 ───────────────────────────────────────────────────────
60
+ import { DevSetupCommand, DevAliasCommand } from "./saeeol/cli/dev-setup"
61
+
62
+ // ── 공통 ────────────────────────────────────────────────────────────
63
+ import * as Log from "@saeeol/core/util/log"
14
64
  import { UI } from "./cli/ui"
15
65
  import { Installation } from "./installation"
16
66
  import { InstallationBuildKind, InstallationVersion } from "@saeeol/core/installation/version"
17
67
  import { NamedError } from "@saeeol/core/util/error"
18
68
  import { FormatError } from "./cli/error"
19
- import { ServeCommand } from "./cli/cmd/serve"
20
69
  import { Filesystem } from "@/util/filesystem"
21
- import { ConfigCommand as ConfigCLICommand } from "./cli/cmd/config"
22
- import { DebugCommand } from "./cli/cmd/debug"
23
- import { StatsCommand } from "./cli/cmd/stats"
24
- import { McpCommand } from "./cli/cmd/mcp"
25
- // import { GithubCommand } from "./cli/cmd/github"
26
- import { ExportCommand } from "./cli/cmd/export"
27
- import { ImportCommand } from "./cli/cmd/import"
28
- import { AttachCommand } from "./cli/cmd/tui/attach"
29
- import { TuiThreadCommand } from "./cli/cmd/tui/thread"
30
- import { AcpCommand } from "./cli/cmd/acp"
31
70
  import { EOL } from "os"
32
- // import { WebCommand } from "./cli/cmd/web"
33
- import { PrCommand } from "./cli/cmd/pr"
34
- import { SessionCommand } from "./cli/cmd/session"
35
- import { RemoteCommand } from "./cli/cmd/remote"
36
- import { RollCallCommand } from "./saeeol/cli/cmd/roll-call"
37
- import { DevSetupCommand, DevAliasCommand } from "./saeeol/cli/dev-setup"
38
71
  import { Telemetry } from "@saeeol/telemetry"
39
72
  import { InstanceStore } from "./project/instance-store"
40
73
  import { migrateLegacySaeeolAuth, ENV_FEATURE, ENV_VERSION } from "@saeeol/gateway"
41
- // (extension, cloud) which set their own SAEEOL_FEATURE env var. Direct CLI use
42
- // (any command other than 'serve') is tagged as 'cli'. If 'serve' is spawned without
43
- // the env var, it gets 'unknown' so the misconfiguration is visible in data.
44
- if (!process.env[ENV_FEATURE]) {
45
- const isServe = process.argv.includes("serve")
46
- process.env[ENV_FEATURE] = isServe ? "unknown" : "cli"
47
- }
48
- if (!process.env[ENV_VERSION]) {
49
- process.env[ENV_VERSION] = InstallationVersion
50
- }
51
74
  import { Config } from "./config/config"
52
75
  import { Auth } from "./auth"
53
- import { DbCommand } from "./cli/cmd/db"
54
76
  import path from "path"
55
77
  import { Global } from "@saeeol/core/global"
56
78
  import { createHelpCommand } from "./saeeol/help-command"
57
79
  import { JsonMigration } from "@/storage/json-migration"
58
80
  import { Database } from "@/storage/db"
59
81
  import { errorMessage } from "./util/error"
60
- import { PluginCommand } from "./cli/cmd/plug"
61
- import { InitCommand } from "./cli/cmd/init"
62
82
  import { Heap } from "./cli/heap"
63
83
  import { drizzle } from "drizzle-orm/bun-sqlite"
64
84
  import { ensureProcessMetadata } from "@saeeol/core/util/saeeol-process"
85
+ import type { Tier } from "./addons/types"
86
+ import { addonsForTier, manifest } from "./addons/registry"
87
+
88
+ if (!process.env[ENV_FEATURE]) {
89
+ const isServe = process.argv.includes("serve")
90
+ process.env[ENV_FEATURE] = isServe ? "unknown" : "cli"
91
+ }
92
+ if (!process.env[ENV_VERSION]) {
93
+ process.env[ENV_VERSION] = InstallationVersion
94
+ }
65
95
 
66
96
  const processMetadata = ensureProcessMetadata("main")
67
97
 
@@ -204,37 +234,71 @@ let cli = yargs(args)
204
234
  })
205
235
  .usage("")
206
236
  .completion("completion", "generate shell completion script")
207
- .command(AcpCommand)
208
- .command(McpCommand)
209
- .command(TuiThreadCommand)
210
- .command(AttachCommand)
211
- .command(RunCommand)
212
- .command(GenerateCommand)
213
- .command(DebugCommand)
214
- // .command(LoginCommand)
215
- // .command(LogoutCommand)
216
- // .command(SwitchCommand)
217
- // .command(OrgsCommand)
218
- // .command(ConsoleCommand)
219
- .command(ProvidersCommand)
220
- .command(AgentCommand)
221
- .command(UpgradeCommand)
222
- .command(UninstallCommand)
223
- .command(ServeCommand)
224
- // .command(WebCommand)
225
- .command(ModelsCommand)
226
- .command(RollCallCommand)
227
- .command(StatsCommand)
228
- .command(ExportCommand)
229
- .command(ImportCommand)
230
- // .command(GithubCommand)
231
- .command(PrCommand)
232
- .command(SessionCommand)
233
- .command(RemoteCommand)
234
- .command(ConfigCLICommand)
235
- .command(PluginCommand)
236
- .command(InitCommand)
237
- .command(DbCommand)
237
+
238
+ // ════════════════════════════════════════════════════════════════
239
+ // LIGHT: core (TUI 채팅 + 프롬프트 + 세션)
240
+ // ════════════════════════════════════════════════════════════════
241
+ .command(TuiThreadCommand) // [default] TUI 채팅
242
+ .command(RunCommand) // run 프롬프트 실행
243
+ .command(GenerateCommand) // generate 코드 생성
244
+ .command(SessionCommand) // session 세션 관리
245
+
246
+ // ════════════════════════════════════════════════════════════════
247
+ // LIGHT: llm (provider + 모델 + 설정)
248
+ // ════════════════════════════════════════════════════════════════
249
+ .command(InitCommand) // init 최초 설정
250
+ .command(ProvidersCommand) // auth provider 인증
251
+ .command(ModelsCommand) // models 모델 목록
252
+ .command(ConfigCLICommand) // config 설정 관리
253
+
254
+ // ════════════════════════════════════════════════════════════════
255
+ // LIGHT: lifecycle (설치/업그레이드/제거)
256
+ // ════════════════════════════════════════════════════════════════
257
+ .command(UpgradeCommand) // upgrade 버전 업그레이드
258
+ .command(UninstallCommand) // uninstall 제거
259
+
260
+ // ════════════════════════════════════════════════════════════════
261
+ // CODE: server (headless + 원격 + ACP)
262
+ // ════════════════════════════════════════════════════════════════
263
+ .command(ServeCommand) // serve headless 서버
264
+ .command(AttachCommand) // attach 원격 연결
265
+ .command(AcpCommand) // acp ACP 서버
266
+
267
+ // ════════════════════════════════════════════════════════════════
268
+ // CODE: tools (MCP + 플러그인)
269
+ // ════════════════════════════════════════════════════════════════
270
+ .command(McpCommand) // mcp MCP 서버 관리
271
+ .command(PluginCommand) // plugin 플러그인
272
+
273
+ // ════════════════════════════════════════════════════════════════
274
+ // CODE: data (export + import + db)
275
+ // ════════════════════════════════════════════════════════════════
276
+ .command(ExportCommand) // export 세션 내보내기
277
+ .command(ImportCommand) // import 세션 가져오기
278
+ .command(DbCommand) // db 데이터베이스
279
+
280
+ // ════════════════════════════════════════════════════════════════
281
+ // CODE: dev (선택)
282
+ // ════════════════════════════════════════════════════════════════
283
+ .command(AgentCommand) // agent 에이전트 관리
284
+ .command(DebugCommand) // debug 디버깅
285
+
286
+ // ════════════════════════════════════════════════════════════════
287
+ // MASTER: web (브라우저 채팅 UI)
288
+ // ════════════════════════════════════════════════════════════════
289
+ .command(WebCommand) // web 웹 UI + 브라우저
290
+
291
+ // ════════════════════════════════════════════════════════════════
292
+ // MASTER: remote (선택)
293
+ // ════════════════════════════════════════════════════════════════
294
+ .command(RemoteCommand) // remote 실시간 릴레이
295
+
296
+ // ════════════════════════════════════════════════════════════════
297
+ // MASTER: analytics (선택)
298
+ // ════════════════════════════════════════════════════════════════
299
+ .command(StatsCommand) // stats 사용량 통계
300
+ .command(PrCommand) // pr GitHub PR
301
+ .command(RollCallCommand) // roll-call 모델 연결 테스트
238
302
  if (InstallationBuildKind !== "release") {
239
303
  cli = cli.command(DevSetupCommand).command(DevAliasCommand)
240
304
  }
@@ -0,0 +1,33 @@
1
+ // Auto-generated by packages/web/generate.ts — DO NOT EDIT
2
+ import { writeFileSync, mkdirSync, existsSync } from "node:fs"
3
+ import { join, dirname } from "node:path"
4
+ import { tmpdir } from "node:os"
5
+ import { Buffer } from "node:buffer"
6
+
7
+ const encoded: Record<string, string> = {
8
+ "assets/index--nIB0GG1.js": "KGZ1bmN0aW9uKCl7Y29uc3QgdD1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCJsaW5rIikucmVsTGlzdDtpZih0JiZ0LnN1cHBvcnRzJiZ0LnN1cHBvcnRzKCJtb2R1bGVwcmVsb2FkIikpcmV0dXJuO2Zvcihjb25zdCBpIG9mIGRvY3VtZW50LnF1ZXJ5U2VsZWN0b3JBbGwoJ2xpbmtbcmVsPSJtb2R1bGVwcmVsb2FkIl0nKSlzKGkpO25ldyBNdXRhdGlvbk9ic2VydmVyKGk9Pntmb3IoY29uc3QgbCBvZiBpKWlmKGwudHlwZT09PSJjaGlsZExpc3QiKWZvcihjb25zdCBvIG9mIGwuYWRkZWROb2RlcylvLnRhZ05hbWU9PT0iTElOSyImJm8ucmVsPT09Im1vZHVsZXByZWxvYWQiJiZzKG8pfSkub2JzZXJ2ZShkb2N1bWVudCx7Y2hpbGRMaXN0OiEwLHN1YnRyZWU6ITB9KTtmdW5jdGlvbiBuKGkpe2NvbnN0IGw9e307cmV0dXJuIGkuaW50ZWdyaXR5JiYobC5pbnRlZ3JpdHk9aS5pbnRlZ3JpdHkpLGkucmVmZXJyZXJQb2xpY3kmJihsLnJlZmVycmVyUG9saWN5PWkucmVmZXJyZXJQb2xpY3kpLGkuY3Jvc3NPcmlnaW49PT0idXNlLWNyZWRlbnRpYWxzIj9sLmNyZWRlbnRpYWxzPSJpbmNsdWRlIjppLmNyb3NzT3JpZ2luPT09ImFub255bW91cyI/bC5jcmVkZW50aWFscz0ib21pdCI6bC5jcmVkZW50aWFscz0ic2FtZS1vcmlnaW4iLGx9ZnVuY3Rpb24gcyhpKXtpZihpLmVwKXJldHVybjtpLmVwPSEwO2NvbnN0IGw9bihpKTtmZXRjaChpLmhyZWYsbCl9fSkoKTtjb25zdCBrZT0hMSxPZT0oZSx0KT0+ZT09PXQsTmU9U3ltYm9sKCJzb2xpZC10cmFjayIpLFk9e2VxdWFsczpPZX07bGV0IEY9bnVsbCxiZT14ZTtjb25zdCBJPTEsWj0yLHZlPXtvd25lZDpudWxsLGNsZWFudXBzOm51bGwsY29udGV4dDpudWxsLG93bmVyOm51bGx9O3ZhciBoPW51bGw7bGV0IG9lPW51bGwsSWU9bnVsbCxwPW51bGwseT1udWxsLEE9bnVsbCxuZT0wO2Z1bmN0aW9uIFgoZSx0KXtjb25zdCBuPXAscz1oLGk9ZS5sZW5ndGg9PT0wLGw9dD09PXZvaWQgMD9zOnQsbz1pP3ZlOntvd25lZDpudWxsLGNsZWFudXBzOm51bGwsY29udGV4dDpsP2wuY29udGV4dDpudWxsLG93bmVyOmx9LHI9aT9lOigpPT5lKCgpPT5OKCgpPT5HKG8pKSk7aD1vLHA9bnVsbDt0cnl7cmV0dXJuIEgociwhMCl9ZmluYWxseXtwPW4saD1zfX1mdW5jdGlvbiBPKGUsdCl7dD10P09iamVjdC5hc3NpZ24oe30sWSx0KTpZO2NvbnN0IG49e3ZhbHVlOmUsb2JzZXJ2ZXJzOm51bGwsb2JzZXJ2ZXJTbG90czpudWxsLGNvbXBhcmF0b3I6dC5lcXVhbHN8fHZvaWQgMH0scz1pPT4odHlwZW9mIGk9PSJmdW5jdGlvbiImJihpPWkobi52YWx1ZSkpLEVlKG4saSkpO3JldHVybltTZS5iaW5kKG4pLHNdfWZ1bmN0aW9uIEsoZSx0LG4pe2NvbnN0IHM9c2UoZSx0LCExLEkpO0oocyl9ZnVuY3Rpb24gTGUoZSx0LG4pe2JlPVJlO2NvbnN0IHM9c2UoZSx0LCExLEkpO3MudXNlcj0hMCxBP0EucHVzaChzKTpKKHMpfWZ1bmN0aW9uIHEoZSx0LG4pe249bj9PYmplY3QuYXNzaWduKHt9LFksbik6WTtjb25zdCBzPXNlKGUsdCwhMCwwKTtyZXR1cm4gcy5vYnNlcnZlcnM9bnVsbCxzLm9ic2VydmVyU2xvdHM9bnVsbCxzLmNvbXBhcmF0b3I9bi5lcXVhbHN8fHZvaWQgMCxKKHMpLFNlLmJpbmQocyl9ZnVuY3Rpb24gTihlKXtpZihwPT09bnVsbClyZXR1cm4gZSgpO2NvbnN0IHQ9cDtwPW51bGw7dHJ5e3JldHVybiBlKCl9ZmluYWxseXtwPXR9fWZ1bmN0aW9uIERlKGUpe0xlKCgpPT5OKGUpKX1mdW5jdGlvbiBmZShlKXtyZXR1cm4gaD09PW51bGx8fChoLmNsZWFudXBzPT09bnVsbD9oLmNsZWFudXBzPVtlXTpoLmNsZWFudXBzLnB1c2goZSkpLGV9ZnVuY3Rpb24gQmUoZSx0KXtGfHwoRj1TeW1ib2woImVycm9yIikpLGg9c2Uodm9pZCAwLHZvaWQgMCwhMCksaC5jb250ZXh0PXsuLi5oLmNvbnRleHQsW0ZdOlt0XX07dHJ5e3JldHVybiBlKCl9Y2F0Y2gobil7aWUobil9ZmluYWxseXtoPWgub3duZXJ9fWZ1bmN0aW9uIFNlKCl7aWYodGhpcy5zb3VyY2VzJiZ0aGlzLnN0YXRlKWlmKHRoaXMuc3RhdGU9PT1JKUoodGhpcyk7ZWxzZXtjb25zdCBlPXk7eT1udWxsLEgoKCk9PmVlKHRoaXMpLCExKSx5PWV9aWYocCl7Y29uc3QgZT10aGlzLm9ic2VydmVycz90aGlzLm9ic2VydmVycy5sZW5ndGg6MDtwLnNvdXJjZXM/KHAuc291cmNlcy5wdXNoKHRoaXMpLHAuc291cmNlU2xvdHMucHVzaChlKSk6KHAuc291cmNlcz1bdGhpc10scC5zb3VyY2VTbG90cz1bZV0pLHRoaXMub2JzZXJ2ZXJzPyh0aGlzLm9ic2VydmVycy5wdXNoKHApLHRoaXMub2JzZXJ2ZXJTbG90cy5wdXNoKHAuc291cmNlcy5sZW5ndGgtMSkpOih0aGlzLm9ic2VydmVycz1bcF0sdGhpcy5vYnNlcnZlclNsb3RzPVtwLnNvdXJjZXMubGVuZ3RoLTFdKX1yZXR1cm4gdGhpcy52YWx1ZX1mdW5jdGlvbiBFZShlLHQsbil7bGV0IHM9ZS52YWx1ZTtyZXR1cm4oIWUuY29tcGFyYXRvcnx8IWUuY29tcGFyYXRvcihzLHQpKSYmKGUudmFsdWU9dCxlLm9ic2VydmVycyYmZS5vYnNlcnZlcnMubGVuZ3RoJiZIKCgpPT57Zm9yKGxldCBpPTA7aTxlLm9ic2VydmVycy5sZW5ndGg7aSs9MSl7Y29uc3QgbD1lLm9ic2VydmVyc1tpXSxvPW9lJiZvZS5ydW5uaW5nO28mJm9lLmRpc3Bvc2VkLmhhcyhsKSwobz8hbC50U3RhdGU6IWwuc3RhdGUpJiYobC5wdXJlP3kucHVzaChsKTpBLnB1c2gobCksbC5vYnNlcnZlcnMmJl9lKGwpKSxvfHwobC5zdGF0ZT1JKX1pZih5Lmxlbmd0aD4xZTYpdGhyb3cgeT1bXSxuZXcgRXJyb3J9LCExKSksdH1mdW5jdGlvbiBKKGUpe2lmKCFlLmZuKXJldHVybjtHKGUpO2NvbnN0IHQ9bmU7TWUoZSxlLnZhbHVlLHQpfWZ1bmN0aW9uIE1lKGUsdCxuKXtsZXQgcztjb25zdCBpPWgsbD1wO3A9aD1lO3RyeXtzPWUuZm4odCl9Y2F0Y2gobyl7cmV0dXJuIGUucHVyZSYmKGUuc3RhdGU9SSxlLm93bmVkJiZlLm93bmVkLmZvckVhY2goRyksZS5vd25lZD1udWxsKSxlLnVwZGF0ZWRBdD1uKzEsaWUobyl9ZmluYWxseXtwPWwsaD1pfSghZS51cGRhdGVkQXR8fGUudXBkYXRlZEF0PD1uKSYmKGUudXBkYXRlZEF0IT1udWxsJiYib2JzZXJ2ZXJzImluIGU/RWUoZSxzKTplLnZhbHVlPXMsZS51cGRhdGVkQXQ9bil9ZnVuY3Rpb24gc2UoZSx0LG4scz1JLGkpe2NvbnN0IGw9e2ZuOmUsc3RhdGU6cyx1cGRhdGVkQXQ6bnVsbCxvd25lZDpudWxsLHNvdXJjZXM6bnVsbCxzb3VyY2VTbG90czpudWxsLGNsZWFudXBzOm51bGwsdmFsdWU6dCxvd25lcjpoLGNvbnRleHQ6aD9oLmNvbnRleHQ6bnVsbCxwdXJlOm59O3JldHVybiBoPT09bnVsbHx8aCE9PXZlJiYoaC5vd25lZD9oLm93bmVkLnB1c2gobCk6aC5vd25lZD1bbF0pLGx9ZnVuY3Rpb24geihlKXtpZihlLnN0YXRlPT09MClyZXR1cm47aWYoZS5zdGF0ZT09PVopcmV0dXJuIGVlKGUpO2lmKGUuc3VzcGVuc2UmJk4oZS5zdXNwZW5zZS5pbkZhbGxiYWNrKSlyZXR1cm4gZS5zdXNwZW5zZS5lZmZlY3RzLnB1c2goZSk7Y29uc3QgdD1bZV07Zm9yKDsoZT1lLm93bmVyKSYmKCFlLnVwZGF0ZWRBdHx8ZS51cGRhdGVkQXQ8bmUpOyllLnN0YXRlJiZ0LnB1c2goZSk7Zm9yKGxldCBuPXQubGVuZ3RoLTE7bj49MDtuLS0paWYoZT10W25dLGUuc3RhdGU9PT1JKUooZSk7ZWxzZSBpZihlLnN0YXRlPT09Wil7Y29uc3Qgcz15O3k9bnVsbCxIKCgpPT5lZShlLHRbMF0pLCExKSx5PXN9fWZ1bmN0aW9uIEgoZSx0KXtpZih5KXJldHVybiBlKCk7bGV0IG49ITE7dHx8KHk9W10pLEE/bj0hMDpBPVtdLG5lKys7dHJ5e2NvbnN0IHM9ZSgpO3JldHVybiBQZShuKSxzfWNhdGNoKHMpe258fChBPW51bGwpLHk9bnVsbCxpZShzKX19ZnVuY3Rpb24gUGUoZSl7aWYoeSYmKHhlKHkpLHk9bnVsbCksZSlyZXR1cm47Y29uc3QgdD1BO0E9bnVsbCx0Lmxlbmd0aCYmSCgoKT0+YmUodCksITEpfWZ1bmN0aW9uIHhlKGUpe2ZvcihsZXQgdD0wO3Q8ZS5sZW5ndGg7dCsrKXooZVt0XSl9ZnVuY3Rpb24gUmUoZSl7bGV0IHQsbj0wO2Zvcih0PTA7dDxlLmxlbmd0aDt0Kyspe2NvbnN0IHM9ZVt0XTtzLnVzZXI/ZVtuKytdPXM6eihzKX1mb3IodD0wO3Q8bjt0KyspeihlW3RdKX1mdW5jdGlvbiBlZShlLHQpe2Uuc3RhdGU9MDtmb3IobGV0IG49MDtuPGUuc291cmNlcy5sZW5ndGg7bis9MSl7Y29uc3Qgcz1lLnNvdXJjZXNbbl07aWYocy5zb3VyY2VzKXtjb25zdCBpPXMuc3RhdGU7aT09PUk/cyE9PXQmJighcy51cGRhdGVkQXR8fHMudXBkYXRlZEF0PG5lKSYmeihzKTppPT09WiYmZWUocyx0KX19fWZ1bmN0aW9uIF9lKGUpe2ZvcihsZXQgdD0wO3Q8ZS5vYnNlcnZlcnMubGVuZ3RoO3QrPTEpe2NvbnN0IG49ZS5vYnNlcnZlcnNbdF07bi5zdGF0ZXx8KG4uc3RhdGU9WixuLnB1cmU/eS5wdXNoKG4pOkEucHVzaChuKSxuLm9ic2VydmVycyYmX2UobikpfX1mdW5jdGlvbiBHKGUpe2xldCB0O2lmKGUuc291cmNlcylmb3IoO2Uuc291cmNlcy5sZW5ndGg7KXtjb25zdCBuPWUuc291cmNlcy5wb3AoKSxzPWUuc291cmNlU2xvdHMucG9wKCksaT1uLm9ic2VydmVycztpZihpJiZpLmxlbmd0aCl7Y29uc3QgbD1pLnBvcCgpLG89bi5vYnNlcnZlclNsb3RzLnBvcCgpO3M8aS5sZW5ndGgmJihsLnNvdXJjZVNsb3RzW29dPXMsaVtzXT1sLG4ub2JzZXJ2ZXJTbG90c1tzXT1vKX19aWYoZS50T3duZWQpe2Zvcih0PWUudE93bmVkLmxlbmd0aC0xO3Q+PTA7dC0tKUcoZS50T3duZWRbdF0pO2RlbGV0ZSBlLnRPd25lZH1pZihlLm93bmVkKXtmb3IodD1lLm93bmVkLmxlbmd0aC0xO3Q+PTA7dC0tKUcoZS5vd25lZFt0XSk7ZS5vd25lZD1udWxsfWlmKGUuY2xlYW51cHMpe2Zvcih0PWUuY2xlYW51cHMubGVuZ3RoLTE7dD49MDt0LS0pZS5jbGVhbnVwc1t0XSgpO2UuY2xlYW51cHM9bnVsbH1lLnN0YXRlPTB9ZnVuY3Rpb24gamUoZSl7cmV0dXJuIGUgaW5zdGFuY2VvZiBFcnJvcj9lOm5ldyBFcnJvcih0eXBlb2YgZT09InN0cmluZyI/ZToiVW5rbm93biBlcnJvciIse2NhdXNlOmV9KX1mdW5jdGlvbiBwZShlLHQsbil7dHJ5e2Zvcihjb25zdCBzIG9mIHQpcyhlKX1jYXRjaChzKXtpZShzLG4mJm4ub3duZXJ8fG51bGwpfX1mdW5jdGlvbiBpZShlLHQ9aCl7Y29uc3Qgbj1GJiZ0JiZ0LmNvbnRleHQmJnQuY29udGV4dFtGXSxzPWplKGUpO2lmKCFuKXRocm93IHM7QT9BLnB1c2goe2ZuKCl7cGUocyxuLHQpfSxzdGF0ZTpJfSk6cGUocyxuLHQpfWNvbnN0IFVlPVN5bWJvbCgiZmFsbGJhY2siKTtmdW5jdGlvbiBnZShlKXtmb3IobGV0IHQ9MDt0PGUubGVuZ3RoO3QrKyllW3RdKCl9ZnVuY3Rpb24gS2UoZSx0LG49e30pe2xldCBzPVtdLGk9W10sbD1bXSxvPTAscj10Lmxlbmd0aD4xP1tdOm51bGw7cmV0dXJuIGZlKCgpPT5nZShsKSksKCk9PntsZXQgYT1lKCl8fFtdLGY9YS5sZW5ndGgsZCxjO3JldHVybiBhW05lXSxOKCgpPT57bGV0ICQsVCxFLGosUCxiLHYsdSxtO2lmKGY9PT0wKW8hPT0wJiYoZ2UobCksbD1bXSxzPVtdLGk9W10sbz0wLHImJihyPVtdKSksbi5mYWxsYmFjayYmKHM9W1VlXSxpWzBdPVgodz0+KGxbMF09dyxuLmZhbGxiYWNrKCkpKSxvPTEpO2Vsc2UgaWYobz09PTApe2ZvcihpPW5ldyBBcnJheShmKSxjPTA7YzxmO2MrKylzW2NdPWFbY10saVtjXT1YKEMpO289Zn1lbHNle2ZvcihFPW5ldyBBcnJheShmKSxqPW5ldyBBcnJheShmKSxyJiYoUD1uZXcgQXJyYXkoZikpLGI9MCx2PU1hdGgubWluKG8sZik7Yjx2JiZzW2JdPT09YVtiXTtiKyspO2Zvcih2PW8tMSx1PWYtMTt2Pj1iJiZ1Pj1iJiZzW3ZdPT09YVt1XTt2LS0sdS0tKUVbdV09aVt2XSxqW3VdPWxbdl0sciYmKFBbdV09clt2XSk7Zm9yKCQ9bmV3IE1hcCxUPW5ldyBBcnJheSh1KzEpLGM9dTtjPj1iO2MtLSltPWFbY10sZD0kLmdldChtKSxUW2NdPWQ9PT12b2lkIDA/LTE6ZCwkLnNldChtLGMpO2ZvcihkPWI7ZDw9djtkKyspbT1zW2RdLGM9JC5nZXQobSksYyE9PXZvaWQgMCYmYyE9PS0xPyhFW2NdPWlbZF0saltjXT1sW2RdLHImJihQW2NdPXJbZF0pLGM9VFtjXSwkLnNldChtLGMpKTpsW2RdKCk7Zm9yKGM9YjtjPGY7YysrKWMgaW4gRT8oaVtjXT1FW2NdLGxbY109altjXSxyJiYocltjXT1QW2NdLHJbY10oYykpKTppW2NdPVgoQyk7aT1pLnNsaWNlKDAsbz1mKSxzPWEuc2xpY2UoMCl9cmV0dXJuIGl9KTtmdW5jdGlvbiBDKCQpe2lmKGxbY109JCxyKXtjb25zdFtULEVdPU8oYyk7cmV0dXJuIHJbY109RSx0KGFbY10sVCl9cmV0dXJuIHQoYVtjXSl9fX1mdW5jdGlvbiBCKGUsdCl7cmV0dXJuIE4oKCk9PmUodHx8e30pKX1jb25zdCBWZT1lPT5gU3RhbGUgcmVhZCBmcm9tIDwke2V9Pi5gO2Z1bmN0aW9uIG1lKGUpe2NvbnN0IHQ9ImZhbGxiYWNrImluIGUmJntmYWxsYmFjazooKT0+ZS5mYWxsYmFja307cmV0dXJuIHEoS2UoKCk9PmUuZWFjaCxlLmNoaWxkcmVuLHR8fHZvaWQgMCkpfWZ1bmN0aW9uIGNlKGUpe2NvbnN0IHQ9ZS5rZXllZCxuPXEoKCk9PmUud2hlbix2b2lkIDAsdm9pZCAwKSxzPXQ/bjpxKG4sdm9pZCAwLHtlcXVhbHM6KGksbCk9PiFpPT0hbH0pO3JldHVybiBxKCgpPT57Y29uc3QgaT1zKCk7aWYoaSl7Y29uc3QgbD1lLmNoaWxkcmVuO3JldHVybiB0eXBlb2YgbD09ImZ1bmN0aW9uIiYmbC5sZW5ndGg+MD9OKCgpPT5sKHQ/aTooKT0+e2lmKCFOKHMpKXRocm93IFZlKCJTaG93Iik7cmV0dXJuIG4oKX0pKTpsfXJldHVybiBlLmZhbGxiYWNrfSx2b2lkIDAsdm9pZCAwKX1sZXQgVztmdW5jdGlvbiBGZShlKXtsZXQgdDtjb25zdFtuLHNdPU8odCx2b2lkIDApO3JldHVybiBXfHwoVz1uZXcgU2V0KSxXLmFkZChzKSxmZSgoKT0+Vy5kZWxldGUocykpLHEoKCk9PntsZXQgaTtpZihpPW4oKSl7Y29uc3QgbD1lLmZhbGxiYWNrO3JldHVybiB0eXBlb2YgbD09ImZ1bmN0aW9uIiYmbC5sZW5ndGg/TigoKT0+bChpLCgpPT5zKCkpKTpsfXJldHVybiBCZSgoKT0+ZS5jaGlsZHJlbixzKX0sdm9pZCAwLHZvaWQgMCl9ZnVuY3Rpb24gcWUoZSx0LG4pe2xldCBzPW4ubGVuZ3RoLGk9dC5sZW5ndGgsbD1zLG89MCxyPTAsYT10W2ktMV0ubmV4dFNpYmxpbmcsZj1udWxsO2Zvcig7bzxpfHxyPGw7KXtpZih0W29dPT09bltyXSl7bysrLHIrKztjb250aW51ZX1mb3IoO3RbaS0xXT09PW5bbC0xXTspaS0tLGwtLTtpZihpPT09byl7Y29uc3QgZD1sPHM/cj9uW3ItMV0ubmV4dFNpYmxpbmc6bltsLXJdOmE7Zm9yKDtyPGw7KWUuaW5zZXJ0QmVmb3JlKG5bcisrXSxkKX1lbHNlIGlmKGw9PT1yKWZvcig7bzxpOykoIWZ8fCFmLmhhcyh0W29dKSkmJnRbb10ucmVtb3ZlKCksbysrO2Vsc2UgaWYodFtvXT09PW5bbC0xXSYmbltyXT09PXRbaS0xXSl7Y29uc3QgZD10Wy0taV0ubmV4dFNpYmxpbmc7ZS5pbnNlcnRCZWZvcmUobltyKytdLHRbbysrXS5uZXh0U2libGluZyksZS5pbnNlcnRCZWZvcmUoblstLWxdLGQpLHRbaV09bltsXX1lbHNle2lmKCFmKXtmPW5ldyBNYXA7bGV0IGM9cjtmb3IoO2M8bDspZi5zZXQobltjXSxjKyspfWNvbnN0IGQ9Zi5nZXQodFtvXSk7aWYoZCE9bnVsbClpZihyPGQmJmQ8bCl7bGV0IGM9byxDPTEsJDtmb3IoOysrYzxpJiZjPGwmJiEoKCQ9Zi5nZXQodFtjXSkpPT1udWxsfHwkIT09ZCtDKTspQysrO2lmKEM+ZC1yKXtjb25zdCBUPXRbb107Zm9yKDtyPGQ7KWUuaW5zZXJ0QmVmb3JlKG5bcisrXSxUKX1lbHNlIGUucmVwbGFjZUNoaWxkKG5bcisrXSx0W28rK10pfWVsc2UgbysrO2Vsc2UgdFtvKytdLnJlbW92ZSgpfX19Y29uc3QgeWU9Il8kRFhfREVMRUdBVEUiO2Z1bmN0aW9uIEdlKGUsdCxuLHM9e30pe2xldCBpO3JldHVybiBYKGw9PntpPWwsdD09PWRvY3VtZW50P2UoKTpTKHQsZSgpLHQuZmlyc3RDaGlsZD9udWxsOnZvaWQgMCxuKX0scy5vd25lciksKCk9PntpKCksdC50ZXh0Q29udGVudD0iIn19ZnVuY3Rpb24gTShlLHQsbixzKXtsZXQgaTtjb25zdCBsPSgpPT57Y29uc3Qgcj1kb2N1bWVudC5jcmVhdGVFbGVtZW50KCJ0ZW1wbGF0ZSIpO3JldHVybiByLmlubmVySFRNTD1lLHIuY29udGVudC5maXJzdENoaWxkfSxvPSgpPT4oaXx8KGk9bCgpKSkuY2xvbmVOb2RlKCEwKTtyZXR1cm4gby5jbG9uZU5vZGU9byxvfWZ1bmN0aW9uIEFlKGUsdD13aW5kb3cuZG9jdW1lbnQpe2NvbnN0IG49dFt5ZV18fCh0W3llXT1uZXcgU2V0KTtmb3IobGV0IHM9MCxpPWUubGVuZ3RoO3M8aTtzKyspe2NvbnN0IGw9ZVtzXTtuLmhhcyhsKXx8KG4uYWRkKGwpLHQuYWRkRXZlbnRMaXN0ZW5lcihsLEhlKSl9fWZ1bmN0aW9uIEplKGUsdCl7dD09bnVsbD9lLnJlbW92ZUF0dHJpYnV0ZSgiY2xhc3MiKTplLmNsYXNzTmFtZT10fWZ1bmN0aW9uIHdlKGUsdCxuKXtyZXR1cm4gTigoKT0+ZSh0LG4pKX1mdW5jdGlvbiBTKGUsdCxuLHMpe2lmKG4hPT12b2lkIDAmJiFzJiYocz1bXSksdHlwZW9mIHQhPSJmdW5jdGlvbiIpcmV0dXJuIHRlKGUsdCxzLG4pO0soaT0+dGUoZSx0KCksaSxuKSxzKX1mdW5jdGlvbiBIZShlKXtsZXQgdD1lLnRhcmdldDtjb25zdCBuPWAkJCR7ZS50eXBlfWAscz1lLnRhcmdldCxpPWUuY3VycmVudFRhcmdldCxsPWE9Pk9iamVjdC5kZWZpbmVQcm9wZXJ0eShlLCJ0YXJnZXQiLHtjb25maWd1cmFibGU6ITAsdmFsdWU6YX0pLG89KCk9Pntjb25zdCBhPXRbbl07aWYoYSYmIXQuZGlzYWJsZWQpe2NvbnN0IGY9dFtgJHtufURhdGFgXTtpZihmIT09dm9pZCAwP2EuY2FsbCh0LGYsZSk6YS5jYWxsKHQsZSksZS5jYW5jZWxCdWJibGUpcmV0dXJufXJldHVybiB0Lmhvc3QmJnR5cGVvZiB0Lmhvc3QhPSJzdHJpbmciJiYhdC5ob3N0Ll8kaG9zdCYmdC5jb250YWlucyhlLnRhcmdldCkmJmwodC5ob3N0KSwhMH0scj0oKT0+e2Zvcig7bygpJiYodD10Ll8kaG9zdHx8dC5wYXJlbnROb2RlfHx0Lmhvc3QpOyk7fTtpZihPYmplY3QuZGVmaW5lUHJvcGVydHkoZSwiY3VycmVudFRhcmdldCIse2NvbmZpZ3VyYWJsZTohMCxnZXQoKXtyZXR1cm4gdHx8ZG9jdW1lbnR9fSksZS5jb21wb3NlZFBhdGgpe2NvbnN0IGE9ZS5jb21wb3NlZFBhdGgoKTtsKGFbMF0pO2ZvcihsZXQgZj0wO2Y8YS5sZW5ndGgtMiYmKHQ9YVtmXSwhIW8oKSk7ZisrKXtpZih0Ll8kaG9zdCl7dD10Ll8kaG9zdCxyKCk7YnJlYWt9aWYodC5wYXJlbnROb2RlPT09aSlicmVha319ZWxzZSByKCk7bChzKX1mdW5jdGlvbiB0ZShlLHQsbixzLGkpe2Zvcig7dHlwZW9mIG49PSJmdW5jdGlvbiI7KW49bigpO2lmKHQ9PT1uKXJldHVybiBuO2NvbnN0IGw9dHlwZW9mIHQsbz1zIT09dm9pZCAwO2lmKGU9byYmblswXSYmblswXS5wYXJlbnROb2RlfHxlLGw9PT0ic3RyaW5nInx8bD09PSJudW1iZXIiKXtpZihsPT09Im51bWJlciImJih0PXQudG9TdHJpbmcoKSx0PT09bikpcmV0dXJuIG47aWYobyl7bGV0IHI9blswXTtyJiZyLm5vZGVUeXBlPT09Mz9yLmRhdGEhPT10JiYoci5kYXRhPXQpOnI9ZG9jdW1lbnQuY3JlYXRlVGV4dE5vZGUodCksbj1VKGUsbixzLHIpfWVsc2UgbiE9PSIiJiZ0eXBlb2Ygbj09InN0cmluZyI/bj1lLmZpcnN0Q2hpbGQuZGF0YT10Om49ZS50ZXh0Q29udGVudD10fWVsc2UgaWYodD09bnVsbHx8bD09PSJib29sZWFuIiluPVUoZSxuLHMpO2Vsc2V7aWYobD09PSJmdW5jdGlvbiIpcmV0dXJuIEsoKCk9PntsZXQgcj10KCk7Zm9yKDt0eXBlb2Ygcj09ImZ1bmN0aW9uIjspcj1yKCk7bj10ZShlLHIsbixzKX0pLCgpPT5uO2lmKEFycmF5LmlzQXJyYXkodCkpe2NvbnN0IHI9W10sYT1uJiZBcnJheS5pc0FycmF5KG4pO2lmKGFlKHIsdCxuLGkpKXJldHVybiBLKCgpPT5uPXRlKGUscixuLHMsITApKSwoKT0+bjtpZihyLmxlbmd0aD09PTApe2lmKG49VShlLG4scyksbylyZXR1cm4gbn1lbHNlIGE/bi5sZW5ndGg9PT0wPyRlKGUscixzKTpxZShlLG4scik6KG4mJlUoZSksJGUoZSxyKSk7bj1yfWVsc2UgaWYodC5ub2RlVHlwZSl7aWYoQXJyYXkuaXNBcnJheShuKSl7aWYobylyZXR1cm4gbj1VKGUsbixzLHQpO1UoZSxuLG51bGwsdCl9ZWxzZSBuPT1udWxsfHxuPT09IiJ8fCFlLmZpcnN0Q2hpbGQ/ZS5hcHBlbmRDaGlsZCh0KTplLnJlcGxhY2VDaGlsZCh0LGUuZmlyc3RDaGlsZCk7bj10fX1yZXR1cm4gbn1mdW5jdGlvbiBhZShlLHQsbixzKXtsZXQgaT0hMTtmb3IobGV0IGw9MCxvPXQubGVuZ3RoO2w8bztsKyspe2xldCByPXRbbF0sYT1uJiZuW2UubGVuZ3RoXSxmO2lmKCEocj09bnVsbHx8cj09PSEwfHxyPT09ITEpKWlmKChmPXR5cGVvZiByKT09Im9iamVjdCImJnIubm9kZVR5cGUpZS5wdXNoKHIpO2Vsc2UgaWYoQXJyYXkuaXNBcnJheShyKSlpPWFlKGUscixhKXx8aTtlbHNlIGlmKGY9PT0iZnVuY3Rpb24iKWlmKHMpe2Zvcig7dHlwZW9mIHI9PSJmdW5jdGlvbiI7KXI9cigpO2k9YWUoZSxBcnJheS5pc0FycmF5KHIpP3I6W3JdLEFycmF5LmlzQXJyYXkoYSk/YTpbYV0pfHxpfWVsc2UgZS5wdXNoKHIpLGk9ITA7ZWxzZXtjb25zdCBkPVN0cmluZyhyKTthJiZhLm5vZGVUeXBlPT09MyYmYS5kYXRhPT09ZD9lLnB1c2goYSk6ZS5wdXNoKGRvY3VtZW50LmNyZWF0ZVRleHROb2RlKGQpKX19cmV0dXJuIGl9ZnVuY3Rpb24gJGUoZSx0LG49bnVsbCl7Zm9yKGxldCBzPTAsaT10Lmxlbmd0aDtzPGk7cysrKWUuaW5zZXJ0QmVmb3JlKHRbc10sbil9ZnVuY3Rpb24gVShlLHQsbixzKXtpZihuPT09dm9pZCAwKXJldHVybiBlLnRleHRDb250ZW50PSIiO2NvbnN0IGk9c3x8ZG9jdW1lbnQuY3JlYXRlVGV4dE5vZGUoIiIpO2lmKHQubGVuZ3RoKXtsZXQgbD0hMTtmb3IobGV0IG89dC5sZW5ndGgtMTtvPj0wO28tLSl7Y29uc3Qgcj10W29dO2lmKGkhPT1yKXtjb25zdCBhPXIucGFyZW50Tm9kZT09PWU7IWwmJiFvP2E/ZS5yZXBsYWNlQ2hpbGQoaSxyKTplLmluc2VydEJlZm9yZShpLG4pOmEmJnIucmVtb3ZlKCl9ZWxzZSBsPSEwfX1lbHNlIGUuaW5zZXJ0QmVmb3JlKGksbik7cmV0dXJuW2ldfWZ1bmN0aW9uIFFlKGUpe2NvbnN0IHQ9ZS5yZXBsYWNlKC9cLyskLywiIik7YXN5bmMgZnVuY3Rpb24gbihzLGkpe3JldHVybiBhd2FpdCBmZXRjaChgJHt0fSR7c31gLHsuLi5pLGhlYWRlcnM6eyJDb250ZW50LVR5cGUiOiJhcHBsaWNhdGlvbi9qc29uIiwuLi5pPy5oZWFkZXJzfX0pfXJldHVybnt1cmw6dCxmZXRjaDpuLGFzeW5jIGdldFNlc3Npb25zKCl7cmV0dXJuKGF3YWl0IG4oIi9pbnN0YW5jZS9zZXNzaW9ucyIpKS5qc29uKCl9LGFzeW5jIHByb21wdChzLGkpe3JldHVybiBuKCIvaW5zdGFuY2UvcHJvbXB0Iix7bWV0aG9kOiJQT1NUIixib2R5OkpTT04uc3RyaW5naWZ5KHttZXNzYWdlOnMsc2Vzc2lvbklkOml9KX0pfSxldmVudHM6e3N1YnNjcmliZShzKXtjb25zdCBpPW5ldyBFdmVudFNvdXJjZShgJHt0fS9nbG9iYWwvZXZlbnRgKTtyZXR1cm4gaS5vbm1lc3NhZ2U9bD0+e3RyeXtzKEpTT04ucGFyc2UobC5kYXRhKSl9Y2F0Y2h7fX0saX19fX12YXIgV2U9TSgiPGFzaWRlIGNsYXNzPXNpZGViYXI+PGRpdiBjbGFzcz1zaWRlYmFyLWhlYWRlcj48YnV0dG9uIGNsYXNzPW5ldy1jaGF0LWJ0bj4rIOyDiCDrjIDtmZQ8L2J1dHRvbj48L2Rpdj48ZGl2IGNsYXNzPXNpZGViYXItc2Vzc2lvbnM+IiksWGU9TSgiPGRpdiBjbGFzcz1lbXB0eS1zdGF0ZT48ZGl2IGNsYXNzPWVtcHR5LWxvZ28+4qyhPC9kaXY+PGgyPlNBRUVPTDwvaDI+PHA+QUkg7L2U65SpIOyXkOydtOyghO2KuDwvcD48ZGl2IGNsYXNzPWVtcHR5LWhpbnRzPjxkaXYgY2xhc3M9aGludD7tmITsnqwg7ZSE66Gc7KCd7Yq4IOq1rOyhsOulvCDshKTrqoXtlbTspJg8L2Rpdj48ZGl2IGNsYXNzPWhpbnQ+UkVBRE1FLm1k66W8IOyekeyEse2VtOykmDwvZGl2PjxkaXYgY2xhc3M9aGludD7rsoTqt7jrpbwg7LC+7JWE7IScIOyImOygle2VtOykmCIpLFllPU0oJzxkaXYgY2xhc3M9Im1lc3NhZ2UgbWVzc2FnZS1hc3Npc3RhbnQiPjxkaXYgY2xhc3M9bWVzc2FnZS1hdmF0YXI+4qyhPC9kaXY+PGRpdiBjbGFzcz1tZXNzYWdlLWNvbnRlbnQ+PGRpdiBjbGFzcz10eXBpbmctaW5kaWNhdG9yPjxzcGFuPjwvc3Bhbj48c3Bhbj48L3NwYW4+PHNwYW4+JyksWmU9TSgnPGRpdiBjbGFzcz1jaGF0LWxheW91dD48ZGl2IGNsYXNzPWNoYXQtbWFpbj48ZGl2IGNsYXNzPW1lc3NhZ2VzPjxkaXY+PC9kaXY+PC9kaXY+PGRpdiBjbGFzcz1pbnB1dC1hcmVhPjxkaXYgY2xhc3M9aW5wdXQtd3JhcHBlcj48dGV4dGFyZWEgcGxhY2Vob2xkZXI9IuuplOyLnOyngOulvCDsnoXroKXtlZjshLjsmpQuLi4icm93cz0xPjwvdGV4dGFyZWE+PGJ1dHRvbiBjbGFzcz1zZW5kLWJ0bj7soITshqEnKSx6ZT1NKCI8ZGl2IGNsYXNzPXNlc3Npb24taXRlbT48c3BhbiBjbGFzcz1zZXNzaW9uLXRpdGxlPiIpLGV0PU0oIjxkaXY+PGRpdiBjbGFzcz1tZXNzYWdlLWF2YXRhcj48L2Rpdj48ZGl2IGNsYXNzPW1lc3NhZ2UtY29udGVudD48ZGl2IGNsYXNzPW1lc3NhZ2UtdGV4dD48L2Rpdj48ZGl2IGNsYXNzPW1lc3NhZ2UtdGltZT4iKTtmdW5jdGlvbiB0dCgpe2NvbnN0W2UsdF09TyhbXSksW24sc109TygiIiksW2ksbF09TyghMSksW29dPU8oW10pLFtyLGFdPU8obnVsbCksW2YsZF09TyghMSksW2NdPU8oITApO2xldCBDLCQsVDtjb25zdCBFPSgpPT5gJHtsb2NhdGlvbi5wcm90b2NvbH0vLyR7bG9jYXRpb24uaG9zdH1gO0RlKGFzeW5jKCk9Pnt0cnl7VD1RZShFKCkpO2NvbnN0IHU9ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoImNvbm5lY3Rpb24tc3RhdHVzIik7dSYmKHUudGV4dENvbnRlbnQ9IuyXsOqysOuQqCIpLGQoITApLGF3YWl0IGooKX1jYXRjaHtjb25zdCBtPWRvY3VtZW50LmdldEVsZW1lbnRCeUlkKCJjb25uZWN0aW9uLXN0YXR1cyIpO20mJihtLnRleHRDb250ZW50PSLsl7DqsrAg7Iuk7YyoIil9fSksZmUoKCk9Pnt9KTthc3luYyBmdW5jdGlvbiBqKCl7dHJ5e2NvbnN0IHU9YXdhaXQgZmV0Y2goYCR7RSgpfS93b3Jrc3BhY2UvbGlzdGApO2lmKHUub2spe2NvbnN0IG09YXdhaXQgdS5qc29uKCksdz1tLmRpcmVjdG9yaWVzfHxtLndvcmtzcGFjZXN8fFtdfX1jYXRjaHt9fWFzeW5jIGZ1bmN0aW9uIFAoKXtjb25zdCB1PW4oKS50cmltKCk7aWYoIXV8fGkoKSlyZXR1cm47Y29uc3QgbT17aWQ6YHUtJHtEYXRlLm5vdygpfWAscm9sZToidXNlciIsY29udGVudDp1LHRpbWVzdGFtcDpEYXRlLm5vdygpfTt0KHc9PlsuLi53LG1dKSxzKCIiKSxsKCEwKTt0cnl7Y29uc3Qgdz17bWVzc2FnZTp1fTtyKCkmJih3LnNlc3Npb25JZD1yKCkpO2NvbnN0IHg9YXdhaXQgZmV0Y2goYCR7RSgpfS9pbnN0YW5jZS9wcm9tcHRgLHttZXRob2Q6IlBPU1QiLGhlYWRlcnM6eyJDb250ZW50LVR5cGUiOiJhcHBsaWNhdGlvbi9qc29uIn0sYm9keTpKU09OLnN0cmluZ2lmeSh3KX0pO2lmKCF4Lm9rKXtjb25zdCBsZT1hd2FpdCB4LnRleHQoKTt0KHJlPT5bLi4ucmUse2lkOmBlLSR7RGF0ZS5ub3coKX1gLHJvbGU6ImFzc2lzdGFudCIsY29udGVudDpg7Jik66WYOiAke3guc3RhdHVzfSAke2xlfWAsdGltZXN0YW1wOkRhdGUubm93KCl9XSl9fWNhdGNoKHcpe3QoeD0+Wy4uLngse2lkOmBlLSR7RGF0ZS5ub3coKX1gLHJvbGU6ImFzc2lzdGFudCIsY29udGVudDpg64Sk7Yq47JuM7YGsIOyYpOulmDogJHt3Lm1lc3NhZ2V9YCx0aW1lc3RhbXA6RGF0ZS5ub3coKX1dKX1maW5hbGx5e2woITEpLGIoKX19ZnVuY3Rpb24gYigpe3JlcXVlc3RBbmltYXRpb25GcmFtZSgoKT0+Qz8uc2Nyb2xsSW50b1ZpZXcoe2JlaGF2aW9yOiJzbW9vdGgifSkpfWZ1bmN0aW9uIHYodSl7dS5rZXk9PT0iRW50ZXIiJiYhdS5zaGlmdEtleSYmKHUucHJldmVudERlZmF1bHQoKSxQKCkpfXJldHVybigoKT0+e3ZhciB1PVplKCksbT11LmZpcnN0Q2hpbGQsdz1tLmZpcnN0Q2hpbGQseD13LmZpcnN0Q2hpbGQsbGU9dy5uZXh0U2libGluZyxyZT1sZS5maXJzdENoaWxkLFI9cmUuZmlyc3RDaGlsZCx1ZT1SLm5leHRTaWJsaW5nO1ModSxCKGNlLHtnZXQgd2hlbigpe3JldHVybiBjKCl9LGdldCBjaGlsZHJlbigpe3ZhciBnPVdlKCksXz1nLmZpcnN0Q2hpbGQsaz1fLmZpcnN0Q2hpbGQsVj1fLm5leHRTaWJsaW5nO3JldHVybiBrLiQkY2xpY2s9KCk9Pnt0KFtdKSxhKG51bGwpfSxTKFYsQihtZSx7Z2V0IGVhY2goKXtyZXR1cm4gbygpfSxjaGlsZHJlbjpMPT4oKCk9Pnt2YXIgRD16ZSgpLFE9RC5maXJzdENoaWxkO3JldHVybiBELiQkY2xpY2s9KCk9PnthKEwuaWQpLG5hdmlnYXRlKGAvcy8ke0wuaWR9YCl9LFMoUSwoKT0+TC50aXRsZXx8IuyDiCDrjIDtmZQiKSxEfSkoKX0pKSxnfX0pLG0pLFModyxCKGNlLHtnZXQgd2hlbigpe3JldHVybiBlKCkubGVuZ3RoPT09MH0sZ2V0IGNoaWxkcmVuKCl7dmFyIGc9WGUoKSxfPWcuZmlyc3RDaGlsZCxrPV8ubmV4dFNpYmxpbmcsVj1rLm5leHRTaWJsaW5nLEw9Vi5uZXh0U2libGluZyxEPUwuZmlyc3RDaGlsZCxRPUQubmV4dFNpYmxpbmcsVGU9US5uZXh0U2libGluZztyZXR1cm4gRC4kJGNsaWNrPSgpPT5zKCLtmITsnqwg7ZSE66Gc7KCd7Yq4IOq1rOyhsOulvCDshKTrqoXtlbTspJgiKSxRLiQkY2xpY2s9KCk9PnMoIlJFQURNRS5tZOulvCDsnpHshLHtlbTspJgiKSxUZS4kJGNsaWNrPSgpPT5zKCLrsoTqt7jrpbwg7LC+7JWE7IScIOyImOygle2VtOykmCIpLGd9fSkseCksUyh3LEIobWUse2dldCBlYWNoKCl7cmV0dXJuIGUoKX0sY2hpbGRyZW46Zz0+KCgpPT57dmFyIF89ZXQoKSxrPV8uZmlyc3RDaGlsZCxWPWsubmV4dFNpYmxpbmcsTD1WLmZpcnN0Q2hpbGQsRD1MLm5leHRTaWJsaW5nO3JldHVybiBTKGssKCk9Pmcucm9sZT09PSJ1c2VyIj8i8J+RpCI6IuKsoSIpLFMoTCwoKT0+Zy5jb250ZW50KSxTKEQsKCk9Pm5ldyBEYXRlKGcudGltZXN0YW1wKS50b0xvY2FsZVRpbWVTdHJpbmcoImtvLUtSIikpLEsoKCk9PkplKF8sYG1lc3NhZ2UgbWVzc2FnZS0ke2cucm9sZX1gKSksX30pKCl9KSx4KSxTKHcsQihjZSx7Z2V0IHdoZW4oKXtyZXR1cm4gaSgpfSxnZXQgY2hpbGRyZW4oKXtyZXR1cm4gWWUoKX19KSx4KTt2YXIgZGU9Qzt0eXBlb2YgZGU9PSJmdW5jdGlvbiI/d2UoZGUseCk6Qz14LFIuJCRrZXlkb3duPXYsUi4kJGlucHV0PWc9PnMoZy5jdXJyZW50VGFyZ2V0LnZhbHVlKTt2YXIgaGU9JDtyZXR1cm4gdHlwZW9mIGhlPT0iZnVuY3Rpb24iP3dlKGhlLFIpOiQ9Uix1ZS4kJGNsaWNrPVAsSyhnPT57dmFyIF89IWYoKSxrPSFuKCkudHJpbSgpfHxpKCl8fCFmKCk7cmV0dXJuIF8hPT1nLmUmJihSLmRpc2FibGVkPWcuZT1fKSxrIT09Zy50JiYodWUuZGlzYWJsZWQ9Zy50PWspLGd9LHtlOnZvaWQgMCx0OnZvaWQgMH0pLEsoKCk9PlIudmFsdWU9bigpKSx1fSkoKX1BZShbImNsaWNrIiwiaW5wdXQiLCJrZXlkb3duIl0pO3ZhciBudD1NKCI8ZGl2IGNsYXNzPWFwcD48bmF2IGNsYXNzPXRvcGJhcj48ZGl2IGNsYXNzPXRvcGJhci1sZWZ0PjxzcGFuIGNsYXNzPWxvZ28+4qyhIFNBRUVPTDwvc3Bhbj48L2Rpdj48ZGl2IGNsYXNzPXRvcGJhci1yaWdodD48c3BhbiBjbGFzcz1zdGF0dXMgaWQ9Y29ubmVjdGlvbi1zdGF0dXM+7Jew6rKwIOykkS4uLjwvc3Bhbj48L2Rpdj48L25hdj48bWFpbiBjbGFzcz1tYWluPiIpLHN0PU0oIjxkaXYgY2xhc3M9ZXJyb3Itc2NyZWVuPjxoMj7smKTrpZgg67Cc7IOdPC9oMj48cHJlPjwvcHJlPjxidXR0b24+7IOI66Gc6rOg7LmoIik7ZnVuY3Rpb24gaXQoKXtyZXR1cm4gQihGZSx7ZmFsbGJhY2s6ZT0+KCgpPT57dmFyIHQ9c3QoKSxuPXQuZmlyc3RDaGlsZCxzPW4ubmV4dFNpYmxpbmcsaT1zLm5leHRTaWJsaW5nO3JldHVybiBTKHMsKCk9PlN0cmluZyhlKSksaS4kJGNsaWNrPSgpPT5sb2NhdGlvbi5yZWxvYWQoKSx0fSkoKSxnZXQgY2hpbGRyZW4oKXt2YXIgZT1udCgpLHQ9ZS5maXJzdENoaWxkLG49dC5uZXh0U2libGluZztyZXR1cm4gUyhuLEIodHQse30pKSxlfX0pfUFlKFsiY2xpY2siXSk7Y29uc3QgQ2U9ZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQoInJvb3QiKTtpZighQ2UpdGhyb3cgbmV3IEVycm9yKCJSb290IGVsZW1lbnQgbm90IGZvdW5kIik7R2UoKCk9PkIoaXQse30pLENlKTsK",
9
+ "assets/index-BAFkjvN0.css": "OnJvb3R7LS1iZzogIzBhMGEwYTstLWJnLXNlY29uZGFyeTogIzE0MTQxNDstLWJnLXRlcnRpYXJ5OiAjMWUxZTFlOy0tdGV4dDogI2UwZTBlMDstLXRleHQtZGltOiAjODg4Oy0tYWNjZW50OiAjZjk3MzE2Oy0tYWNjZW50LWhvdmVyOiAjZmI5MjNjOy0tYm9yZGVyOiAjMmEyYTJhOy0tdXNlci1iZzogIzFhMWEyZTstLWFzc2lzdGFudC1iZzogIzE0MTQxNDstLXJhZGl1czogOHB4fS5hcHB7ZGlzcGxheTpmbGV4O2ZsZXgtZGlyZWN0aW9uOmNvbHVtbjtoZWlnaHQ6MTAwdmg7b3ZlcmZsb3c6aGlkZGVufS50b3BiYXJ7ZGlzcGxheTpmbGV4O2FsaWduLWl0ZW1zOmNlbnRlcjtqdXN0aWZ5LWNvbnRlbnQ6c3BhY2UtYmV0d2VlbjtwYWRkaW5nOjAgMTZweDtoZWlnaHQ6NDRweDttaW4taGVpZ2h0OjQ0cHg7YmFja2dyb3VuZDp2YXIoLS1iZy1zZWNvbmRhcnkpO2JvcmRlci1ib3R0b206MXB4IHNvbGlkIHZhcigtLWJvcmRlcil9LnRvcGJhci1sZWZ0e2Rpc3BsYXk6ZmxleDthbGlnbi1pdGVtczpjZW50ZXI7Z2FwOjEycHh9LmxvZ297Zm9udC13ZWlnaHQ6NzAwO2ZvbnQtc2l6ZToxcmVtO2NvbG9yOnZhcigtLWFjY2VudCk7Y3Vyc29yOnBvaW50ZXJ9LnRvcGJhci1yaWdodHtkaXNwbGF5OmZsZXg7YWxpZ24taXRlbXM6Y2VudGVyO2dhcDo4cHh9LnN0YXR1c3tmb250LXNpemU6Ljc1cmVtO2NvbG9yOnZhcigtLXRleHQtZGltKX0ubWFpbntmbGV4OjE7b3ZlcmZsb3c6aGlkZGVufS5jaGF0LWxheW91dHtkaXNwbGF5OmZsZXg7aGVpZ2h0OjEwMCV9LnNpZGViYXJ7d2lkdGg6MjQwcHg7bWluLXdpZHRoOjI0MHB4O2JhY2tncm91bmQ6dmFyKC0tYmctc2Vjb25kYXJ5KTtib3JkZXItcmlnaHQ6MXB4IHNvbGlkIHZhcigtLWJvcmRlcik7ZGlzcGxheTpmbGV4O2ZsZXgtZGlyZWN0aW9uOmNvbHVtbn0uc2lkZWJhci1oZWFkZXJ7cGFkZGluZzoxMnB4fS5uZXctY2hhdC1idG57d2lkdGg6MTAwJTtwYWRkaW5nOjhweCAxMnB4O2JhY2tncm91bmQ6dmFyKC0tYWNjZW50KTtjb2xvcjojZmZmO2JvcmRlcjpub25lO2JvcmRlci1yYWRpdXM6dmFyKC0tcmFkaXVzKTtjdXJzb3I6cG9pbnRlcjtmb250LXNpemU6Ljg1cmVtO2ZvbnQtd2VpZ2h0OjYwMH0ubmV3LWNoYXQtYnRuOmhvdmVye2JhY2tncm91bmQ6dmFyKC0tYWNjZW50LWhvdmVyKX0uc2lkZWJhci1zZXNzaW9uc3tmbGV4OjE7b3ZlcmZsb3cteTphdXRvO3BhZGRpbmc6NHB4IDhweH0uc2Vzc2lvbi1pdGVte3BhZGRpbmc6OHB4IDEycHg7Ym9yZGVyLXJhZGl1czo2cHg7Y3Vyc29yOnBvaW50ZXI7bWFyZ2luLWJvdHRvbToycHg7Zm9udC1zaXplOi44NXJlbTtjb2xvcjp2YXIoLS10ZXh0LWRpbSk7d2hpdGUtc3BhY2U6bm93cmFwO292ZXJmbG93OmhpZGRlbjt0ZXh0LW92ZXJmbG93OmVsbGlwc2lzfS5zZXNzaW9uLWl0ZW06aG92ZXJ7YmFja2dyb3VuZDp2YXIoLS1iZy10ZXJ0aWFyeSk7Y29sb3I6dmFyKC0tdGV4dCl9LnNlc3Npb24tdGl0bGV7ZGlzcGxheTpibG9jaztvdmVyZmxvdzpoaWRkZW47dGV4dC1vdmVyZmxvdzplbGxpcHNpc30uY2hhdC1tYWlue2ZsZXg6MTtkaXNwbGF5OmZsZXg7ZmxleC1kaXJlY3Rpb246Y29sdW1uO21pbi13aWR0aDowfS5tZXNzYWdlc3tmbGV4OjE7b3ZlcmZsb3cteTphdXRvO3BhZGRpbmc6MjBweCAyNHB4fS5lbXB0eS1zdGF0ZXtkaXNwbGF5OmZsZXg7ZmxleC1kaXJlY3Rpb246Y29sdW1uO2FsaWduLWl0ZW1zOmNlbnRlcjtqdXN0aWZ5LWNvbnRlbnQ6Y2VudGVyO2hlaWdodDoxMDAlO3RleHQtYWxpZ246Y2VudGVyO2dhcDo4cHh9LmVtcHR5LWxvZ297Zm9udC1zaXplOjNyZW07Y29sb3I6dmFyKC0tYWNjZW50KX0uZW1wdHktc3RhdGUgaDJ7Zm9udC1zaXplOjEuNXJlbTtjb2xvcjp2YXIoLS10ZXh0KTttYXJnaW46MH0uZW1wdHktc3RhdGUgcHtjb2xvcjp2YXIoLS10ZXh0LWRpbSk7bWFyZ2luOjAgMCAyMHB4fS5lbXB0eS1oaW50c3tkaXNwbGF5OmZsZXg7ZmxleC1kaXJlY3Rpb246Y29sdW1uO2dhcDo4cHg7bWF4LXdpZHRoOjQwMHB4O3dpZHRoOjEwMCV9LmhpbnR7cGFkZGluZzoxMnB4IDE2cHg7YmFja2dyb3VuZDp2YXIoLS1iZy10ZXJ0aWFyeSk7Ym9yZGVyOjFweCBzb2xpZCB2YXIoLS1ib3JkZXIpO2JvcmRlci1yYWRpdXM6dmFyKC0tcmFkaXVzKTtjdXJzb3I6cG9pbnRlcjtmb250LXNpemU6Ljg1cmVtO2NvbG9yOnZhcigtLXRleHQtZGltKTt0ZXh0LWFsaWduOmxlZnR9LmhpbnQ6aG92ZXJ7Ym9yZGVyLWNvbG9yOnZhcigtLWFjY2VudCk7Y29sb3I6dmFyKC0tdGV4dCl9Lm1lc3NhZ2V7ZGlzcGxheTpmbGV4O2dhcDoxMnB4O21hcmdpbi1ib3R0b206MjBweDttYXgtd2lkdGg6ODAwcHh9Lm1lc3NhZ2UtdXNlciwubWVzc2FnZS1hc3Npc3RhbnR7ZmxleC1kaXJlY3Rpb246cm93fS5tZXNzYWdlLWF2YXRhcnt3aWR0aDozMnB4O2hlaWdodDozMnB4O21pbi13aWR0aDozMnB4O2JvcmRlci1yYWRpdXM6NTAlO2Rpc3BsYXk6ZmxleDthbGlnbi1pdGVtczpjZW50ZXI7anVzdGlmeS1jb250ZW50OmNlbnRlcjtmb250LXNpemU6MXJlbTtiYWNrZ3JvdW5kOnZhcigtLWJnLXRlcnRpYXJ5KX0ubWVzc2FnZS1jb250ZW50e2ZsZXg6MTttaW4td2lkdGg6MH0ubWVzc2FnZS11c2VyIC5tZXNzYWdlLWNvbnRlbnR7YmFja2dyb3VuZDp2YXIoLS11c2VyLWJnKTtwYWRkaW5nOjEwcHggMTRweDtib3JkZXItcmFkaXVzOjEycHggMTJweCAxMnB4IDRweH0ubWVzc2FnZS1hc3Npc3RhbnQgLm1lc3NhZ2UtY29udGVudHtiYWNrZ3JvdW5kOnZhcigtLWFzc2lzdGFudC1iZyk7cGFkZGluZzoxMHB4IDE0cHg7Ym9yZGVyLXJhZGl1czoxMnB4IDEycHggNHB4fS5tZXNzYWdlLXRleHR7d2hpdGUtc3BhY2U6cHJlLXdyYXA7d29yZC1icmVhazpicmVhay13b3JkO2xpbmUtaGVpZ2h0OjEuNjtmb250LXNpemU6LjlyZW19Lm1lc3NhZ2UtdGltZXtmb250LXNpemU6LjdyZW07Y29sb3I6dmFyKC0tdGV4dC1kaW0pO21hcmdpbi10b3A6NHB4O3RleHQtYWxpZ246cmlnaHR9LnR5cGluZy1pbmRpY2F0b3J7ZGlzcGxheTpmbGV4O2dhcDo0cHg7cGFkZGluZzo0cHggMH0udHlwaW5nLWluZGljYXRvciBzcGFue3dpZHRoOjhweDtoZWlnaHQ6OHB4O2JhY2tncm91bmQ6dmFyKC0tdGV4dC1kaW0pO2JvcmRlci1yYWRpdXM6NTAlO2FuaW1hdGlvbjp0eXBpbmcgMS40cyBpbmZpbml0ZX0udHlwaW5nLWluZGljYXRvciBzcGFuOm50aC1jaGlsZCgyKXthbmltYXRpb24tZGVsYXk6LjJzfS50eXBpbmctaW5kaWNhdG9yIHNwYW46bnRoLWNoaWxkKDMpe2FuaW1hdGlvbi1kZWxheTouNHN9QGtleWZyYW1lcyB0eXBpbmd7MCUsODAlLHRve29wYWNpdHk6LjM7dHJhbnNmb3JtOnNjYWxlKC44KX00MCV7b3BhY2l0eToxO3RyYW5zZm9ybTpzY2FsZSgxKX19LmlucHV0LWFyZWF7cGFkZGluZzoxMnB4IDI0cHggMTZweDtiYWNrZ3JvdW5kOnZhcigtLWJnKX0uaW5wdXQtd3JhcHBlcntkaXNwbGF5OmZsZXg7YWxpZ24taXRlbXM6ZmxleC1lbmQ7Z2FwOjhweDtiYWNrZ3JvdW5kOnZhcigtLWJnLXNlY29uZGFyeSk7Ym9yZGVyOjFweCBzb2xpZCB2YXIoLS1ib3JkZXIpO2JvcmRlci1yYWRpdXM6MTJweDtwYWRkaW5nOjhweCAxMnB4fS5pbnB1dC13cmFwcGVyIHRleHRhcmVhe2ZsZXg6MTtiYWNrZ3JvdW5kOm5vbmU7Ym9yZGVyOm5vbmU7Y29sb3I6dmFyKC0tdGV4dCk7Zm9udC1zaXplOi45cmVtO3Jlc2l6ZTpub25lO291dGxpbmU6bm9uZTttaW4taGVpZ2h0OjI0cHg7bWF4LWhlaWdodDoxMjBweDtsaW5lLWhlaWdodDoxLjU7Zm9udC1mYW1pbHk6aW5oZXJpdH0uaW5wdXQtd3JhcHBlciB0ZXh0YXJlYTo6cGxhY2Vob2xkZXJ7Y29sb3I6dmFyKC0tdGV4dC1kaW0pfS5pbnB1dC13cmFwcGVyIHRleHRhcmVhOmRpc2FibGVke29wYWNpdHk6LjV9LnNlbmQtYnRue3BhZGRpbmc6NnB4IDE2cHg7YmFja2dyb3VuZDp2YXIoLS1hY2NlbnQpO2NvbG9yOiNmZmY7Ym9yZGVyOm5vbmU7Ym9yZGVyLXJhZGl1czo2cHg7Y3Vyc29yOnBvaW50ZXI7Zm9udC1zaXplOi44NXJlbTtmb250LXdlaWdodDo2MDA7d2hpdGUtc3BhY2U6bm93cmFwfS5zZW5kLWJ0bjpob3Zlcjpub3QoOmRpc2FibGVkKXtiYWNrZ3JvdW5kOnZhcigtLWFjY2VudC1ob3Zlcil9LnNlbmQtYnRuOmRpc2FibGVke29wYWNpdHk6LjQ7Y3Vyc29yOm5vdC1hbGxvd2VkfS5lcnJvci1zY3JlZW57ZGlzcGxheTpmbGV4O2ZsZXgtZGlyZWN0aW9uOmNvbHVtbjthbGlnbi1pdGVtczpjZW50ZXI7anVzdGlmeS1jb250ZW50OmNlbnRlcjtoZWlnaHQ6MTAwJTtnYXA6MTZweDtwYWRkaW5nOjIwcHh9LmVycm9yLXNjcmVlbiBwcmV7YmFja2dyb3VuZDp2YXIoLS1iZy10ZXJ0aWFyeSk7cGFkZGluZzoxNnB4O2JvcmRlci1yYWRpdXM6dmFyKC0tcmFkaXVzKTttYXgtd2lkdGg6NjAwcHg7b3ZlcmZsb3c6YXV0bztjb2xvcjojZjg3MTcxO2ZvbnQtc2l6ZTouODVyZW19LmVycm9yLXNjcmVlbiBidXR0b257cGFkZGluZzo4cHggMjRweDtiYWNrZ3JvdW5kOnZhcigtLWFjY2VudCk7Y29sb3I6I2ZmZjtib3JkZXI6bm9uZTtib3JkZXItcmFkaXVzOnZhcigtLXJhZGl1cyk7Y3Vyc29yOnBvaW50ZXI7Zm9udC1zaXplOi45cmVtfQo=",
10
+ "index.html": "PCFET0NUWVBFIGh0bWw+CjxodG1sIGxhbmc9ImtvIj4KICA8aGVhZD4KICAgIDxtZXRhIGNoYXJzZXQ9IlVURi04IiAvPgogICAgPG1ldGEgbmFtZT0idmlld3BvcnQiIGNvbnRlbnQ9IndpZHRoPWRldmljZS13aWR0aCwgaW5pdGlhbC1zY2FsZT0xLjAiIC8+CiAgICA8dGl0bGU+U0FFRU9MPC90aXRsZT4KICAgIDxsaW5rIHJlbD0iaWNvbiIgdHlwZT0iaW1hZ2Uvc3ZnK3htbCIgaHJlZj0iL2Zhdmljb24uc3ZnIiAvPgogICAgPHN0eWxlPgogICAgICAqIHsgbWFyZ2luOiAwOyBwYWRkaW5nOiAwOyBib3gtc2l6aW5nOiBib3JkZXItYm94OyB9CiAgICAgIGh0bWwsIGJvZHksICNyb290IHsgaGVpZ2h0OiAxMDAlOyB3aWR0aDogMTAwJTsgb3ZlcmZsb3c6IGhpZGRlbjsgfQogICAgICBib2R5IHsgZm9udC1mYW1pbHk6IC1hcHBsZS1zeXN0ZW0sIEJsaW5rTWFjU3lzdGVtRm9udCwgJ1NlZ29lIFVJJywgUm9ib3RvLCBzYW5zLXNlcmlmOyBiYWNrZ3JvdW5kOiAjMGEwYTBhOyBjb2xvcjogI2UwZTBlMDsgfQogICAgICAjbG9hZGluZyB7IGRpc3BsYXk6IGZsZXg7IGFsaWduLWl0ZW1zOiBjZW50ZXI7IGp1c3RpZnktY29udGVudDogY2VudGVyOyBoZWlnaHQ6IDEwMCU7IH0KICAgICAgI2xvYWRpbmcgc3BhbiB7IGNvbG9yOiAjZjk3MzE2OyBmb250LXNpemU6IDEuMnJlbTsgfQogICAgPC9zdHlsZT4KICAgIDxzY3JpcHQgdHlwZT0ibW9kdWxlIiBjcm9zc29yaWdpbiBzcmM9Ii9hc3NldHMvaW5kZXgtLW5JQjBHRzEuanMiPjwvc2NyaXB0PgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiBjcm9zc29yaWdpbiBocmVmPSIvYXNzZXRzL2luZGV4LUJBRmtqdk4wLmNzcyI+CiAgPC9oZWFkPgogIDxib2R5PgogICAgPGRpdiBpZD0icm9vdCI+CiAgICAgIDxkaXYgaWQ9ImxvYWRpbmciPjxzcGFuPlNBRUVPTCDroZzrk5wg7KSRLi4uPC9zcGFuPjwvZGl2PgogICAgPC9kaXY+CiAgPC9ib2R5Pgo8L2h0bWw+Cg=="
11
+ }
12
+
13
+ const _dir = join(tmpdir(), "saeeol-web-ui")
14
+ const _cache: Record<string, string> = {}
15
+
16
+ const files: Record<string, string> = {}
17
+ for (const key of Object.keys(encoded)) {
18
+ Object.defineProperty(files, key, {
19
+ get() {
20
+ if (_cache[key]) return _cache[key]
21
+ const p = join(_dir, key)
22
+ const d = dirname(p)
23
+ if (!existsSync(d)) mkdirSync(d, { recursive: true })
24
+ writeFileSync(p, Buffer.from(encoded[key], "base64"))
25
+ _cache[key] = p
26
+ return p
27
+ },
28
+ enumerable: true,
29
+ configurable: true,
30
+ })
31
+ }
32
+
33
+ export default files
@@ -5,17 +5,16 @@ import { HttpClient, HttpServerRequest, HttpServerResponse } from "effect/unstab
5
5
  import { Hono } from "hono"
6
6
  import { getMimeType } from "hono/utils/mime"
7
7
  import fs from "node:fs/promises"
8
+ // @ts-expect-error - generated file
9
+ import embeddedWebUIData from "../../saeeol-web-ui.gen.ts"
8
10
 
9
- const embeddedUIPromise = Flag.SAEEOL_DISABLE_EMBEDDED_WEB_UI
10
- ? Promise.resolve(null)
11
- : // @ts-expect-error - generated file at build time
12
- import("saeeol-web-ui.gen.ts").then((module) => module.default as Record<string, string>).catch(() => null)
11
+ const embeddedWebUI: Record<string, string> | null = embeddedWebUIData ?? null
13
12
 
14
13
  const DEFAULT_CSP =
15
14
  "default-src 'self'; script-src 'self' 'wasm-unsafe-eval'; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' data:; media-src 'self' data:; connect-src 'self' data:"
16
15
  function embeddedUI() {
17
16
  if (Flag.SAEEOL_DISABLE_EMBEDDED_WEB_UI) return Promise.resolve(null)
18
- return embeddedUIPromise
17
+ return Promise.resolve(embeddedWebUI)
19
18
  }
20
19
 
21
20
  export async function serveUI(request: Request) {
@@ -0,0 +1 @@
1
+ (function(){const t=document.createElement("link").relList;if(t&&t.supports&&t.supports("modulepreload"))return;for(const i of document.querySelectorAll('link[rel="modulepreload"]'))s(i);new MutationObserver(i=>{for(const l of i)if(l.type==="childList")for(const o of l.addedNodes)o.tagName==="LINK"&&o.rel==="modulepreload"&&s(o)}).observe(document,{childList:!0,subtree:!0});function n(i){const l={};return i.integrity&&(l.integrity=i.integrity),i.referrerPolicy&&(l.referrerPolicy=i.referrerPolicy),i.crossOrigin==="use-credentials"?l.credentials="include":i.crossOrigin==="anonymous"?l.credentials="omit":l.credentials="same-origin",l}function s(i){if(i.ep)return;i.ep=!0;const l=n(i);fetch(i.href,l)}})();const ke=!1,Oe=(e,t)=>e===t,Ne=Symbol("solid-track"),Y={equals:Oe};let F=null,be=xe;const I=1,Z=2,ve={owned:null,cleanups:null,context:null,owner:null};var h=null;let oe=null,Ie=null,p=null,y=null,A=null,ne=0;function X(e,t){const n=p,s=h,i=e.length===0,l=t===void 0?s:t,o=i?ve:{owned:null,cleanups:null,context:l?l.context:null,owner:l},r=i?e:()=>e(()=>N(()=>G(o)));h=o,p=null;try{return H(r,!0)}finally{p=n,h=s}}function O(e,t){t=t?Object.assign({},Y,t):Y;const n={value:e,observers:null,observerSlots:null,comparator:t.equals||void 0},s=i=>(typeof i=="function"&&(i=i(n.value)),Ee(n,i));return[Se.bind(n),s]}function K(e,t,n){const s=se(e,t,!1,I);J(s)}function Le(e,t,n){be=Re;const s=se(e,t,!1,I);s.user=!0,A?A.push(s):J(s)}function q(e,t,n){n=n?Object.assign({},Y,n):Y;const s=se(e,t,!0,0);return s.observers=null,s.observerSlots=null,s.comparator=n.equals||void 0,J(s),Se.bind(s)}function N(e){if(p===null)return e();const t=p;p=null;try{return e()}finally{p=t}}function De(e){Le(()=>N(e))}function fe(e){return h===null||(h.cleanups===null?h.cleanups=[e]:h.cleanups.push(e)),e}function Be(e,t){F||(F=Symbol("error")),h=se(void 0,void 0,!0),h.context={...h.context,[F]:[t]};try{return e()}catch(n){ie(n)}finally{h=h.owner}}function Se(){if(this.sources&&this.state)if(this.state===I)J(this);else{const e=y;y=null,H(()=>ee(this),!1),y=e}if(p){const e=this.observers?this.observers.length:0;p.sources?(p.sources.push(this),p.sourceSlots.push(e)):(p.sources=[this],p.sourceSlots=[e]),this.observers?(this.observers.push(p),this.observerSlots.push(p.sources.length-1)):(this.observers=[p],this.observerSlots=[p.sources.length-1])}return this.value}function Ee(e,t,n){let s=e.value;return(!e.comparator||!e.comparator(s,t))&&(e.value=t,e.observers&&e.observers.length&&H(()=>{for(let i=0;i<e.observers.length;i+=1){const l=e.observers[i],o=oe&&oe.running;o&&oe.disposed.has(l),(o?!l.tState:!l.state)&&(l.pure?y.push(l):A.push(l),l.observers&&_e(l)),o||(l.state=I)}if(y.length>1e6)throw y=[],new Error},!1)),t}function J(e){if(!e.fn)return;G(e);const t=ne;Me(e,e.value,t)}function Me(e,t,n){let s;const i=h,l=p;p=h=e;try{s=e.fn(t)}catch(o){return e.pure&&(e.state=I,e.owned&&e.owned.forEach(G),e.owned=null),e.updatedAt=n+1,ie(o)}finally{p=l,h=i}(!e.updatedAt||e.updatedAt<=n)&&(e.updatedAt!=null&&"observers"in e?Ee(e,s):e.value=s,e.updatedAt=n)}function se(e,t,n,s=I,i){const l={fn:e,state:s,updatedAt:null,owned:null,sources:null,sourceSlots:null,cleanups:null,value:t,owner:h,context:h?h.context:null,pure:n};return h===null||h!==ve&&(h.owned?h.owned.push(l):h.owned=[l]),l}function z(e){if(e.state===0)return;if(e.state===Z)return ee(e);if(e.suspense&&N(e.suspense.inFallback))return e.suspense.effects.push(e);const t=[e];for(;(e=e.owner)&&(!e.updatedAt||e.updatedAt<ne);)e.state&&t.push(e);for(let n=t.length-1;n>=0;n--)if(e=t[n],e.state===I)J(e);else if(e.state===Z){const s=y;y=null,H(()=>ee(e,t[0]),!1),y=s}}function H(e,t){if(y)return e();let n=!1;t||(y=[]),A?n=!0:A=[],ne++;try{const s=e();return Pe(n),s}catch(s){n||(A=null),y=null,ie(s)}}function Pe(e){if(y&&(xe(y),y=null),e)return;const t=A;A=null,t.length&&H(()=>be(t),!1)}function xe(e){for(let t=0;t<e.length;t++)z(e[t])}function Re(e){let t,n=0;for(t=0;t<e.length;t++){const s=e[t];s.user?e[n++]=s:z(s)}for(t=0;t<n;t++)z(e[t])}function ee(e,t){e.state=0;for(let n=0;n<e.sources.length;n+=1){const s=e.sources[n];if(s.sources){const i=s.state;i===I?s!==t&&(!s.updatedAt||s.updatedAt<ne)&&z(s):i===Z&&ee(s,t)}}}function _e(e){for(let t=0;t<e.observers.length;t+=1){const n=e.observers[t];n.state||(n.state=Z,n.pure?y.push(n):A.push(n),n.observers&&_e(n))}}function G(e){let t;if(e.sources)for(;e.sources.length;){const n=e.sources.pop(),s=e.sourceSlots.pop(),i=n.observers;if(i&&i.length){const l=i.pop(),o=n.observerSlots.pop();s<i.length&&(l.sourceSlots[o]=s,i[s]=l,n.observerSlots[s]=o)}}if(e.tOwned){for(t=e.tOwned.length-1;t>=0;t--)G(e.tOwned[t]);delete e.tOwned}if(e.owned){for(t=e.owned.length-1;t>=0;t--)G(e.owned[t]);e.owned=null}if(e.cleanups){for(t=e.cleanups.length-1;t>=0;t--)e.cleanups[t]();e.cleanups=null}e.state=0}function je(e){return e instanceof Error?e:new Error(typeof e=="string"?e:"Unknown error",{cause:e})}function pe(e,t,n){try{for(const s of t)s(e)}catch(s){ie(s,n&&n.owner||null)}}function ie(e,t=h){const n=F&&t&&t.context&&t.context[F],s=je(e);if(!n)throw s;A?A.push({fn(){pe(s,n,t)},state:I}):pe(s,n,t)}const Ue=Symbol("fallback");function ge(e){for(let t=0;t<e.length;t++)e[t]()}function Ke(e,t,n={}){let s=[],i=[],l=[],o=0,r=t.length>1?[]:null;return fe(()=>ge(l)),()=>{let a=e()||[],f=a.length,d,c;return a[Ne],N(()=>{let $,T,E,j,P,b,v,u,m;if(f===0)o!==0&&(ge(l),l=[],s=[],i=[],o=0,r&&(r=[])),n.fallback&&(s=[Ue],i[0]=X(w=>(l[0]=w,n.fallback())),o=1);else if(o===0){for(i=new Array(f),c=0;c<f;c++)s[c]=a[c],i[c]=X(C);o=f}else{for(E=new Array(f),j=new Array(f),r&&(P=new Array(f)),b=0,v=Math.min(o,f);b<v&&s[b]===a[b];b++);for(v=o-1,u=f-1;v>=b&&u>=b&&s[v]===a[u];v--,u--)E[u]=i[v],j[u]=l[v],r&&(P[u]=r[v]);for($=new Map,T=new Array(u+1),c=u;c>=b;c--)m=a[c],d=$.get(m),T[c]=d===void 0?-1:d,$.set(m,c);for(d=b;d<=v;d++)m=s[d],c=$.get(m),c!==void 0&&c!==-1?(E[c]=i[d],j[c]=l[d],r&&(P[c]=r[d]),c=T[c],$.set(m,c)):l[d]();for(c=b;c<f;c++)c in E?(i[c]=E[c],l[c]=j[c],r&&(r[c]=P[c],r[c](c))):i[c]=X(C);i=i.slice(0,o=f),s=a.slice(0)}return i});function C($){if(l[c]=$,r){const[T,E]=O(c);return r[c]=E,t(a[c],T)}return t(a[c])}}}function B(e,t){return N(()=>e(t||{}))}const Ve=e=>`Stale read from <${e}>.`;function me(e){const t="fallback"in e&&{fallback:()=>e.fallback};return q(Ke(()=>e.each,e.children,t||void 0))}function ce(e){const t=e.keyed,n=q(()=>e.when,void 0,void 0),s=t?n:q(n,void 0,{equals:(i,l)=>!i==!l});return q(()=>{const i=s();if(i){const l=e.children;return typeof l=="function"&&l.length>0?N(()=>l(t?i:()=>{if(!N(s))throw Ve("Show");return n()})):l}return e.fallback},void 0,void 0)}let W;function Fe(e){let t;const[n,s]=O(t,void 0);return W||(W=new Set),W.add(s),fe(()=>W.delete(s)),q(()=>{let i;if(i=n()){const l=e.fallback;return typeof l=="function"&&l.length?N(()=>l(i,()=>s())):l}return Be(()=>e.children,s)},void 0,void 0)}function qe(e,t,n){let s=n.length,i=t.length,l=s,o=0,r=0,a=t[i-1].nextSibling,f=null;for(;o<i||r<l;){if(t[o]===n[r]){o++,r++;continue}for(;t[i-1]===n[l-1];)i--,l--;if(i===o){const d=l<s?r?n[r-1].nextSibling:n[l-r]:a;for(;r<l;)e.insertBefore(n[r++],d)}else if(l===r)for(;o<i;)(!f||!f.has(t[o]))&&t[o].remove(),o++;else if(t[o]===n[l-1]&&n[r]===t[i-1]){const d=t[--i].nextSibling;e.insertBefore(n[r++],t[o++].nextSibling),e.insertBefore(n[--l],d),t[i]=n[l]}else{if(!f){f=new Map;let c=r;for(;c<l;)f.set(n[c],c++)}const d=f.get(t[o]);if(d!=null)if(r<d&&d<l){let c=o,C=1,$;for(;++c<i&&c<l&&!(($=f.get(t[c]))==null||$!==d+C);)C++;if(C>d-r){const T=t[o];for(;r<d;)e.insertBefore(n[r++],T)}else e.replaceChild(n[r++],t[o++])}else o++;else t[o++].remove()}}}const ye="_$DX_DELEGATE";function Ge(e,t,n,s={}){let i;return X(l=>{i=l,t===document?e():S(t,e(),t.firstChild?null:void 0,n)},s.owner),()=>{i(),t.textContent=""}}function M(e,t,n,s){let i;const l=()=>{const r=document.createElement("template");return r.innerHTML=e,r.content.firstChild},o=()=>(i||(i=l())).cloneNode(!0);return o.cloneNode=o,o}function Ae(e,t=window.document){const n=t[ye]||(t[ye]=new Set);for(let s=0,i=e.length;s<i;s++){const l=e[s];n.has(l)||(n.add(l),t.addEventListener(l,He))}}function Je(e,t){t==null?e.removeAttribute("class"):e.className=t}function we(e,t,n){return N(()=>e(t,n))}function S(e,t,n,s){if(n!==void 0&&!s&&(s=[]),typeof t!="function")return te(e,t,s,n);K(i=>te(e,t(),i,n),s)}function He(e){let t=e.target;const n=`$$${e.type}`,s=e.target,i=e.currentTarget,l=a=>Object.defineProperty(e,"target",{configurable:!0,value:a}),o=()=>{const a=t[n];if(a&&!t.disabled){const f=t[`${n}Data`];if(f!==void 0?a.call(t,f,e):a.call(t,e),e.cancelBubble)return}return t.host&&typeof t.host!="string"&&!t.host._$host&&t.contains(e.target)&&l(t.host),!0},r=()=>{for(;o()&&(t=t._$host||t.parentNode||t.host););};if(Object.defineProperty(e,"currentTarget",{configurable:!0,get(){return t||document}}),e.composedPath){const a=e.composedPath();l(a[0]);for(let f=0;f<a.length-2&&(t=a[f],!!o());f++){if(t._$host){t=t._$host,r();break}if(t.parentNode===i)break}}else r();l(s)}function te(e,t,n,s,i){for(;typeof n=="function";)n=n();if(t===n)return n;const l=typeof t,o=s!==void 0;if(e=o&&n[0]&&n[0].parentNode||e,l==="string"||l==="number"){if(l==="number"&&(t=t.toString(),t===n))return n;if(o){let r=n[0];r&&r.nodeType===3?r.data!==t&&(r.data=t):r=document.createTextNode(t),n=U(e,n,s,r)}else n!==""&&typeof n=="string"?n=e.firstChild.data=t:n=e.textContent=t}else if(t==null||l==="boolean")n=U(e,n,s);else{if(l==="function")return K(()=>{let r=t();for(;typeof r=="function";)r=r();n=te(e,r,n,s)}),()=>n;if(Array.isArray(t)){const r=[],a=n&&Array.isArray(n);if(ae(r,t,n,i))return K(()=>n=te(e,r,n,s,!0)),()=>n;if(r.length===0){if(n=U(e,n,s),o)return n}else a?n.length===0?$e(e,r,s):qe(e,n,r):(n&&U(e),$e(e,r));n=r}else if(t.nodeType){if(Array.isArray(n)){if(o)return n=U(e,n,s,t);U(e,n,null,t)}else n==null||n===""||!e.firstChild?e.appendChild(t):e.replaceChild(t,e.firstChild);n=t}}return n}function ae(e,t,n,s){let i=!1;for(let l=0,o=t.length;l<o;l++){let r=t[l],a=n&&n[e.length],f;if(!(r==null||r===!0||r===!1))if((f=typeof r)=="object"&&r.nodeType)e.push(r);else if(Array.isArray(r))i=ae(e,r,a)||i;else if(f==="function")if(s){for(;typeof r=="function";)r=r();i=ae(e,Array.isArray(r)?r:[r],Array.isArray(a)?a:[a])||i}else e.push(r),i=!0;else{const d=String(r);a&&a.nodeType===3&&a.data===d?e.push(a):e.push(document.createTextNode(d))}}return i}function $e(e,t,n=null){for(let s=0,i=t.length;s<i;s++)e.insertBefore(t[s],n)}function U(e,t,n,s){if(n===void 0)return e.textContent="";const i=s||document.createTextNode("");if(t.length){let l=!1;for(let o=t.length-1;o>=0;o--){const r=t[o];if(i!==r){const a=r.parentNode===e;!l&&!o?a?e.replaceChild(i,r):e.insertBefore(i,n):a&&r.remove()}else l=!0}}else e.insertBefore(i,n);return[i]}function Qe(e){const t=e.replace(/\/+$/,"");async function n(s,i){return await fetch(`${t}${s}`,{...i,headers:{"Content-Type":"application/json",...i?.headers}})}return{url:t,fetch:n,async getSessions(){return(await n("/instance/sessions")).json()},async prompt(s,i){return n("/instance/prompt",{method:"POST",body:JSON.stringify({message:s,sessionId:i})})},events:{subscribe(s){const i=new EventSource(`${t}/global/event`);return i.onmessage=l=>{try{s(JSON.parse(l.data))}catch{}},i}}}}var We=M("<aside class=sidebar><div class=sidebar-header><button class=new-chat-btn>+ 새 대화</button></div><div class=sidebar-sessions>"),Xe=M("<div class=empty-state><div class=empty-logo>⬡</div><h2>SAEEOL</h2><p>AI 코딩 에이전트</p><div class=empty-hints><div class=hint>현재 프로젝트 구조를 설명해줘</div><div class=hint>README.md를 작성해줘</div><div class=hint>버그를 찾아서 수정해줘"),Ye=M('<div class="message message-assistant"><div class=message-avatar>⬡</div><div class=message-content><div class=typing-indicator><span></span><span></span><span>'),Ze=M('<div class=chat-layout><div class=chat-main><div class=messages><div></div></div><div class=input-area><div class=input-wrapper><textarea placeholder="메시지를 입력하세요..."rows=1></textarea><button class=send-btn>전송'),ze=M("<div class=session-item><span class=session-title>"),et=M("<div><div class=message-avatar></div><div class=message-content><div class=message-text></div><div class=message-time>");function tt(){const[e,t]=O([]),[n,s]=O(""),[i,l]=O(!1),[o]=O([]),[r,a]=O(null),[f,d]=O(!1),[c]=O(!0);let C,$,T;const E=()=>`${location.protocol}//${location.host}`;De(async()=>{try{T=Qe(E());const u=document.getElementById("connection-status");u&&(u.textContent="연결됨"),d(!0),await j()}catch{const m=document.getElementById("connection-status");m&&(m.textContent="연결 실패")}}),fe(()=>{});async function j(){try{const u=await fetch(`${E()}/workspace/list`);if(u.ok){const m=await u.json(),w=m.directories||m.workspaces||[]}}catch{}}async function P(){const u=n().trim();if(!u||i())return;const m={id:`u-${Date.now()}`,role:"user",content:u,timestamp:Date.now()};t(w=>[...w,m]),s(""),l(!0);try{const w={message:u};r()&&(w.sessionId=r());const x=await fetch(`${E()}/instance/prompt`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(w)});if(!x.ok){const le=await x.text();t(re=>[...re,{id:`e-${Date.now()}`,role:"assistant",content:`오류: ${x.status} ${le}`,timestamp:Date.now()}])}}catch(w){t(x=>[...x,{id:`e-${Date.now()}`,role:"assistant",content:`네트워크 오류: ${w.message}`,timestamp:Date.now()}])}finally{l(!1),b()}}function b(){requestAnimationFrame(()=>C?.scrollIntoView({behavior:"smooth"}))}function v(u){u.key==="Enter"&&!u.shiftKey&&(u.preventDefault(),P())}return(()=>{var u=Ze(),m=u.firstChild,w=m.firstChild,x=w.firstChild,le=w.nextSibling,re=le.firstChild,R=re.firstChild,ue=R.nextSibling;S(u,B(ce,{get when(){return c()},get children(){var g=We(),_=g.firstChild,k=_.firstChild,V=_.nextSibling;return k.$$click=()=>{t([]),a(null)},S(V,B(me,{get each(){return o()},children:L=>(()=>{var D=ze(),Q=D.firstChild;return D.$$click=()=>{a(L.id),navigate(`/s/${L.id}`)},S(Q,()=>L.title||"새 대화"),D})()})),g}}),m),S(w,B(ce,{get when(){return e().length===0},get children(){var g=Xe(),_=g.firstChild,k=_.nextSibling,V=k.nextSibling,L=V.nextSibling,D=L.firstChild,Q=D.nextSibling,Te=Q.nextSibling;return D.$$click=()=>s("현재 프로젝트 구조를 설명해줘"),Q.$$click=()=>s("README.md를 작성해줘"),Te.$$click=()=>s("버그를 찾아서 수정해줘"),g}}),x),S(w,B(me,{get each(){return e()},children:g=>(()=>{var _=et(),k=_.firstChild,V=k.nextSibling,L=V.firstChild,D=L.nextSibling;return S(k,()=>g.role==="user"?"👤":"⬡"),S(L,()=>g.content),S(D,()=>new Date(g.timestamp).toLocaleTimeString("ko-KR")),K(()=>Je(_,`message message-${g.role}`)),_})()}),x),S(w,B(ce,{get when(){return i()},get children(){return Ye()}}),x);var de=C;typeof de=="function"?we(de,x):C=x,R.$$keydown=v,R.$$input=g=>s(g.currentTarget.value);var he=$;return typeof he=="function"?we(he,R):$=R,ue.$$click=P,K(g=>{var _=!f(),k=!n().trim()||i()||!f();return _!==g.e&&(R.disabled=g.e=_),k!==g.t&&(ue.disabled=g.t=k),g},{e:void 0,t:void 0}),K(()=>R.value=n()),u})()}Ae(["click","input","keydown"]);var nt=M("<div class=app><nav class=topbar><div class=topbar-left><span class=logo>⬡ SAEEOL</span></div><div class=topbar-right><span class=status id=connection-status>연결 중...</span></div></nav><main class=main>"),st=M("<div class=error-screen><h2>오류 발생</h2><pre></pre><button>새로고침");function it(){return B(Fe,{fallback:e=>(()=>{var t=st(),n=t.firstChild,s=n.nextSibling,i=s.nextSibling;return S(s,()=>String(e)),i.$$click=()=>location.reload(),t})(),get children(){var e=nt(),t=e.firstChild,n=t.nextSibling;return S(n,B(tt,{})),e}})}Ae(["click"]);const Ce=document.getElementById("root");if(!Ce)throw new Error("Root element not found");Ge(()=>B(it,{}),Ce);
@@ -0,0 +1 @@
1
+ :root{--bg: #0a0a0a;--bg-secondary: #141414;--bg-tertiary: #1e1e1e;--text: #e0e0e0;--text-dim: #888;--accent: #f97316;--accent-hover: #fb923c;--border: #2a2a2a;--user-bg: #1a1a2e;--assistant-bg: #141414;--radius: 8px}.app{display:flex;flex-direction:column;height:100vh;overflow:hidden}.topbar{display:flex;align-items:center;justify-content:space-between;padding:0 16px;height:44px;min-height:44px;background:var(--bg-secondary);border-bottom:1px solid var(--border)}.topbar-left{display:flex;align-items:center;gap:12px}.logo{font-weight:700;font-size:1rem;color:var(--accent);cursor:pointer}.topbar-right{display:flex;align-items:center;gap:8px}.status{font-size:.75rem;color:var(--text-dim)}.main{flex:1;overflow:hidden}.chat-layout{display:flex;height:100%}.sidebar{width:240px;min-width:240px;background:var(--bg-secondary);border-right:1px solid var(--border);display:flex;flex-direction:column}.sidebar-header{padding:12px}.new-chat-btn{width:100%;padding:8px 12px;background:var(--accent);color:#fff;border:none;border-radius:var(--radius);cursor:pointer;font-size:.85rem;font-weight:600}.new-chat-btn:hover{background:var(--accent-hover)}.sidebar-sessions{flex:1;overflow-y:auto;padding:4px 8px}.session-item{padding:8px 12px;border-radius:6px;cursor:pointer;margin-bottom:2px;font-size:.85rem;color:var(--text-dim);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.session-item:hover{background:var(--bg-tertiary);color:var(--text)}.session-title{display:block;overflow:hidden;text-overflow:ellipsis}.chat-main{flex:1;display:flex;flex-direction:column;min-width:0}.messages{flex:1;overflow-y:auto;padding:20px 24px}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;text-align:center;gap:8px}.empty-logo{font-size:3rem;color:var(--accent)}.empty-state h2{font-size:1.5rem;color:var(--text);margin:0}.empty-state p{color:var(--text-dim);margin:0 0 20px}.empty-hints{display:flex;flex-direction:column;gap:8px;max-width:400px;width:100%}.hint{padding:12px 16px;background:var(--bg-tertiary);border:1px solid var(--border);border-radius:var(--radius);cursor:pointer;font-size:.85rem;color:var(--text-dim);text-align:left}.hint:hover{border-color:var(--accent);color:var(--text)}.message{display:flex;gap:12px;margin-bottom:20px;max-width:800px}.message-user,.message-assistant{flex-direction:row}.message-avatar{width:32px;height:32px;min-width:32px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:1rem;background:var(--bg-tertiary)}.message-content{flex:1;min-width:0}.message-user .message-content{background:var(--user-bg);padding:10px 14px;border-radius:12px 12px 12px 4px}.message-assistant .message-content{background:var(--assistant-bg);padding:10px 14px;border-radius:12px 12px 4px}.message-text{white-space:pre-wrap;word-break:break-word;line-height:1.6;font-size:.9rem}.message-time{font-size:.7rem;color:var(--text-dim);margin-top:4px;text-align:right}.typing-indicator{display:flex;gap:4px;padding:4px 0}.typing-indicator span{width:8px;height:8px;background:var(--text-dim);border-radius:50%;animation:typing 1.4s infinite}.typing-indicator span:nth-child(2){animation-delay:.2s}.typing-indicator span:nth-child(3){animation-delay:.4s}@keyframes typing{0%,80%,to{opacity:.3;transform:scale(.8)}40%{opacity:1;transform:scale(1)}}.input-area{padding:12px 24px 16px;background:var(--bg)}.input-wrapper{display:flex;align-items:flex-end;gap:8px;background:var(--bg-secondary);border:1px solid var(--border);border-radius:12px;padding:8px 12px}.input-wrapper textarea{flex:1;background:none;border:none;color:var(--text);font-size:.9rem;resize:none;outline:none;min-height:24px;max-height:120px;line-height:1.5;font-family:inherit}.input-wrapper textarea::placeholder{color:var(--text-dim)}.input-wrapper textarea:disabled{opacity:.5}.send-btn{padding:6px 16px;background:var(--accent);color:#fff;border:none;border-radius:6px;cursor:pointer;font-size:.85rem;font-weight:600;white-space:nowrap}.send-btn:hover:not(:disabled){background:var(--accent-hover)}.send-btn:disabled{opacity:.4;cursor:not-allowed}.error-screen{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;gap:16px;padding:20px}.error-screen pre{background:var(--bg-tertiary);padding:16px;border-radius:var(--radius);max-width:600px;overflow:auto;color:#f87171;font-size:.85rem}.error-screen button{padding:8px 24px;background:var(--accent);color:#fff;border:none;border-radius:var(--radius);cursor:pointer;font-size:.9rem}
@@ -0,0 +1,23 @@
1
+ <!DOCTYPE html>
2
+ <html lang="ko">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>SAEEOL</title>
7
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
8
+ <style>
9
+ * { margin: 0; padding: 0; box-sizing: border-box; }
10
+ html, body, #root { height: 100%; width: 100%; overflow: hidden; }
11
+ body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; background: #0a0a0a; color: #e0e0e0; }
12
+ #loading { display: flex; align-items: center; justify-content: center; height: 100%; }
13
+ #loading span { color: #f97316; font-size: 1.2rem; }
14
+ </style>
15
+ <script type="module" crossorigin src="/assets/index--nIB0GG1.js"></script>
16
+ <link rel="stylesheet" crossorigin href="/assets/index-BAFkjvN0.css">
17
+ </head>
18
+ <body>
19
+ <div id="root">
20
+ <div id="loading"><span>SAEEOL 로드 중...</span></div>
21
+ </div>
22
+ </body>
23
+ </html>