xgen-dex-cli 1.25.6 → 1.26.0

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.
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../packages/protocol/src/client.ts", "../../../../packages/engine/src/errors.ts", "../../../../packages/engine/src/config-store.ts", "../../../../packages/engine/src/local-tools-config.ts", "../../../../packages/engine/src/dex-engine.ts", "../../../../packages/protocol/src/agent-data.ts", "../../../../packages/protocol/src/filestore.ts", "../../../../packages/protocol/src/agents.ts", "../../../../packages/protocol/src/hash.ts", "../../../../packages/protocol/src/auth.ts", "../../../../packages/protocol/src/avatars.ts", "../../../../packages/protocol/src/sse.ts", "../../../../packages/protocol/src/chat.ts", "../../../../packages/protocol/src/browser.ts", "../../../../packages/protocol/src/history.ts", "../../../../packages/protocol/src/preferences.ts", "../../../../packages/protocol/src/ssh.ts", "../../../../packages/protocol/src/teams.ts", "../../../../packages/protocol/src/voice.ts", "../../../../packages/protocol/src/index.ts", "../../../../packages/engine/src/mcp-bridge.ts", "../../../../packages/engine/src/secret-stores.ts", "../../../../packages/engine/src/host.ts", "../../../../packages/engine/src/mcp-secrets.ts", "../../../../packages/engine/src/mcp-oauth.ts", "../../../../packages/engine/src/mcp-manager.ts", "../../../../packages/engine/src/exec-resolve.ts", "../../../../packages/engine/src/local-tools.ts", "../../../../packages/engine/src/mcp-runtime-log.ts", "../../../../packages/engine/src/connection-security.ts", "../../../../packages/engine/src/credential-store.ts", "../../../../packages/engine/src/deployment-defaults.ts"],
4
+ "sourcesContent": ["/**\n * HttpClient \u2014 the base transport for all XGEN API calls.\n *\n * Responsibilities:\n * - Resolve URLs against the gateway base URL (everything lives under `/api`).\n * - Attach `Authorization: Bearer <accessToken>` when a token is set.\n * - JSON request/response helpers + a raw streaming fetch for SSE.\n * - Surface a typed `ApiError` and an auth-failure hook so the app can prompt\n * re-login on 401 (mirrors the frontend's `handleAuthFailure`).\n *\n * Framework-agnostic: takes a `fetch` implementation so it runs in the Electron\n * renderer, the main process (electron `net`/undici), Node tests, or a browser.\n */\n\nexport class ApiError extends Error {\n constructor(\n public status: number,\n message: string,\n public body?: unknown,\n ) {\n super(message);\n this.name = 'ApiError';\n }\n}\n\nexport type FetchLike = (input: string, init?: RequestInit) => Promise<Response>;\n\nexport interface HttpClientOptions {\n baseUrl: string;\n fetch?: FetchLike;\n /** Called on any 401 so the host can clear the token and prompt re-login. */\n onAuthFailure?: () => void;\n /** Default per-request timeout (ms) for non-streaming calls. */\n timeoutMs?: number;\n}\n\nexport class HttpClient {\n private baseUrl: string;\n private accessToken: string | null = null;\n private readonly fetchImpl: FetchLike;\n private readonly onAuthFailure?: () => void;\n private readonly timeoutMs: number;\n\n constructor(opts: HttpClientOptions) {\n this.baseUrl = normalizeBaseUrl(opts.baseUrl);\n this.fetchImpl = opts.fetch ?? (globalThis.fetch as FetchLike);\n this.onAuthFailure = opts.onAuthFailure;\n this.timeoutMs = opts.timeoutMs ?? 30_000;\n if (!this.fetchImpl) {\n throw new Error('HttpClient: no fetch implementation available');\n }\n }\n\n setBaseUrl(baseUrl: string): void {\n this.baseUrl = normalizeBaseUrl(baseUrl);\n }\n\n getBaseUrl(): string {\n return this.baseUrl;\n }\n\n setToken(token: string | null): void {\n this.accessToken = token;\n }\n\n private url(path: string): string {\n return `${this.baseUrl}${path.startsWith('/') ? path : `/${path}`}`;\n }\n\n private headers(extra?: Record<string, string>): Record<string, string> {\n const h: Record<string, string> = { ...extra };\n if (this.accessToken) h['Authorization'] = `Bearer ${this.accessToken}`;\n return h;\n }\n\n /** GET/POST/\u2026 returning parsed JSON. Throws ApiError on non-2xx. */\n async json<T>(\n method: string,\n path: string,\n body?: unknown,\n opts?: { auth?: boolean; timeoutMs?: number },\n ): Promise<T> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), opts?.timeoutMs ?? this.timeoutMs);\n let res: Response;\n try {\n res = await this.fetchImpl(this.url(path), {\n method,\n headers: this.headers({\n 'Content-Type': 'application/json',\n Accept: 'application/json',\n }),\n body: body === undefined ? undefined : JSON.stringify(body),\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n const text = await res.text();\n let parsed: unknown = undefined;\n if (text) {\n try {\n parsed = JSON.parse(text);\n } catch {\n parsed = text;\n }\n }\n if (!res.ok) {\n if (res.status === 401 && opts?.auth !== false) this.onAuthFailure?.();\n throw new ApiError(res.status, `${method} ${path} \u2192 ${res.status}`, parsed);\n }\n return parsed as T;\n }\n\n get<T>(path: string, opts?: { auth?: boolean; timeoutMs?: number }): Promise<T> {\n return this.json<T>('GET', path, undefined, opts);\n }\n\n /** Multipart upload (\uC544\uBC14\uD0C0 \uC5D0\uC14B \uB4F1). Content-Type \uC740 fetch \uAC00 boundary \uC640\n * \uD568\uAED8 \uC790\uB3D9 \uC124\uC815\uD558\uBBC0\uB85C \uC9C0\uC815\uD558\uC9C0 \uC54A\uB294\uB2E4. \uB300\uC6A9\uB7C9(\uBAA8\uB378 zip) \uB300\uBE44 \uAE34 \uD0C0\uC784\uC544\uC6C3. */\n async upload<T>(path: string, form: FormData, opts?: { timeoutMs?: number }): Promise<T> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), opts?.timeoutMs ?? 120_000);\n let res: Response;\n try {\n res = await this.fetchImpl(this.url(path), {\n method: 'POST',\n headers: this.headers({ Accept: 'application/json' }),\n body: form as unknown as BodyInit,\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n const text = await res.text();\n let parsed: unknown = undefined;\n if (text) {\n try {\n parsed = JSON.parse(text);\n } catch {\n parsed = text;\n }\n }\n if (!res.ok) {\n if (res.status === 401) this.onAuthFailure?.();\n throw new ApiError(res.status, `POST ${path} \u2192 ${res.status}`, parsed);\n }\n return parsed as T;\n }\n\n post<T>(path: string, body?: unknown, opts?: { auth?: boolean; timeoutMs?: number }): Promise<T> {\n return this.json<T>('POST', path, body, opts);\n }\n\n /**\n * POST a JSON body and read the raw BINARY response (e.g. TTS audio bytes).\n * Returns the bytes plus the response `Content-Type` so the caller can wrap a\n * correctly-typed Blob (audio/wav|mpeg|ogg). Throws ApiError on non-2xx.\n */\n async postBinary(\n path: string,\n body: unknown,\n opts?: { timeoutMs?: number },\n ): Promise<{ bytes: Uint8Array; contentType: string }> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), opts?.timeoutMs ?? this.timeoutMs);\n let res: Response;\n try {\n res = await this.fetchImpl(this.url(path), {\n method: 'POST',\n headers: this.headers({ 'Content-Type': 'application/json', Accept: 'audio/*' }),\n body: JSON.stringify(body),\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n if (!res.ok) {\n if (res.status === 401) this.onAuthFailure?.();\n const text = await res.text().catch(() => '');\n throw new ApiError(res.status, `POST ${path} \u2192 ${res.status}`, text);\n }\n const ab = await res.arrayBuffer();\n return { bytes: new Uint8Array(ab), contentType: res.headers.get('content-type') ?? '' };\n }\n\n /**\n * GET a raw BINARY response (Teams \uCCA8\uBD80 \uB2E4\uC6B4\uB85C\uB4DC \uB4F1). `postBinary` \uC758 GET \uC9DD.\n *\n * \uD30C\uC77C\uBA85\uC740 \uC751\uB2F5 \uD5E4\uB354\uAC00 \uC544\uB2C8\uB77C **\uD638\uCD9C\uC790\uAC00 \uC774\uBBF8 \uC544\uB294 \uAC12**\uC744 \uC4F4\uB2E4 \u2014\n * Content-Disposition \uC758 RFC 5987 \uC778\uCF54\uB529\uC744 \uC5EC\uAE30\uC11C \uB418\uD480\uC774 \uD30C\uC2F1\uD560 \uC774\uC720\uAC00 \uC5C6\uACE0,\n * \uC11C\uBC84\uB3C4 \uC6B0\uB9AC\uAC00 \uCFFC\uB9AC\uB85C \uB118\uAE34 \uC774\uB984\uC744 \uADF8\uB300\uB85C \uB418\uB3CC\uB824\uC904 \uBFD0\uC774\uB2E4.\n */\n async getBinary(\n path: string,\n opts?: { timeoutMs?: number },\n ): Promise<{ bytes: Uint8Array; contentType: string }> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), opts?.timeoutMs ?? 120_000);\n let res: Response;\n try {\n res = await this.fetchImpl(this.url(path), {\n method: 'GET',\n headers: this.headers({ Accept: '*/*' }),\n signal: controller.signal,\n });\n } finally {\n clearTimeout(timer);\n }\n if (!res.ok) {\n if (res.status === 401) this.onAuthFailure?.();\n const text = await res.text().catch(() => '');\n throw new ApiError(res.status, `GET ${path} \u2192 ${res.status}`, text);\n }\n const ab = await res.arrayBuffer();\n return { bytes: new Uint8Array(ab), contentType: res.headers.get('content-type') ?? '' };\n }\n\n put<T>(path: string, body?: unknown, opts?: { auth?: boolean; timeoutMs?: number }): Promise<T> {\n return this.json<T>('PUT', path, body, opts);\n }\n\n patch<T>(\n path: string,\n body?: unknown,\n opts?: { auth?: boolean; timeoutMs?: number },\n ): Promise<T> {\n return this.json<T>('PATCH', path, body, opts);\n }\n\n /** `delete` is a reserved word in some call sites \u2014 keep the short alias. */\n del<T>(path: string, opts?: { auth?: boolean; timeoutMs?: number }): Promise<T> {\n return this.json<T>('DELETE', path, undefined, opts);\n }\n\n /**\n * Open a raw streaming POST (for SSE). Returns the Response so the caller can\n * read `response.body` as a stream. Does NOT enforce the JSON timeout \u2014 SSE\n * connections are long-lived (the gateway allows 1h for `/stream` paths).\n */\n async stream(path: string, body: unknown, signal?: AbortSignal): Promise<Response> {\n const res = await this.fetchImpl(this.url(path), {\n method: 'POST',\n headers: this.headers({\n 'Content-Type': 'application/json',\n Accept: 'text/event-stream',\n }),\n body: JSON.stringify(body),\n signal,\n });\n if (!res.ok) {\n if (res.status === 401) this.onAuthFailure?.();\n const text = await res.text().catch(() => '');\n throw new ApiError(res.status, `stream ${path} \u2192 ${res.status}`, text);\n }\n return res;\n }\n}\n\n/** Strip trailing slashes so `${base}/api/...` never produces `//api`. */\nexport function normalizeBaseUrl(url: string): string {\n return (url || '').trim().replace(/\\/+$/, '');\n}\n", "import { ApiError } from '@dex/protocol/client';\n\nexport type DexErrorCode =\n | 'auth_required'\n | 'auth_invalid'\n | 'config_invalid'\n | 'credential_store_unavailable'\n | 'local_command_denied'\n | 'local_open_denied'\n | 'local_path_denied'\n | 'local_tool_failed'\n | 'local_tools_disabled'\n | 'network_error'\n | 'not_found'\n | 'protocol_mismatch'\n | 'usage_error';\n\nexport class DexError extends Error {\n constructor(\n readonly code: DexErrorCode,\n message: string,\n readonly details?: unknown,\n ) {\n super(message);\n this.name = 'DexError';\n }\n}\n\nexport function isUnauthorized(error: unknown): boolean {\n return error instanceof ApiError && (error.status === 401 || error.status === 403);\n}\n\nexport function publicError(error: unknown): {\n code: string;\n message: string;\n details?: unknown;\n} {\n if (error instanceof DexError) {\n return { code: error.code, message: error.message, details: error.details };\n }\n if (error instanceof ApiError) {\n return {\n code: `http_${error.status}`,\n message: error.message,\n details: error.body,\n };\n }\n if (error instanceof Error) {\n if (error.name === 'AbortError') return { code: 'cancelled', message: '\uC694\uCCAD\uC774 \uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.' };\n return { code: 'internal_error', message: error.message };\n }\n return { code: 'internal_error', message: String(error) };\n}\n", "import { chmod, mkdir, readFile, rename, writeFile } from 'node:fs/promises';\nimport { dirname, join } from 'node:path';\nimport { homedir, platform } from 'node:os';\nimport { DexError } from './errors';\nimport type { DexConfig, DexProfile } from './contract';\nimport { defaultLocalToolsConfig, normalizeLocalToolsConfig } from './local-tools-config';\n\nconst DEFAULT_PROFILE = 'default';\n\nexport interface ConfigStore {\n read(): Promise<DexConfig>;\n write(config: DexConfig): Promise<void>;\n}\n\nexport function defaultConfig(): DexConfig {\n return {\n version: 1,\n currentProfile: DEFAULT_PROFILE,\n profiles: {},\n localTools: defaultLocalToolsConfig(),\n };\n}\n\nexport function dataDirectory(env: NodeJS.ProcessEnv = process.env): string {\n if (env.DEX_CLI_HOME?.trim()) return env.DEX_CLI_HOME.trim();\n if (platform() === 'win32') return join(env.APPDATA || homedir(), 'xgen-dex-cli');\n if (platform() === 'darwin') return join(homedir(), 'Library', 'Application Support', 'xgen-dex-cli');\n return join(env.XDG_CONFIG_HOME || join(homedir(), '.config'), 'xgen-dex-cli');\n}\n\nexport function configPath(env: NodeJS.ProcessEnv = process.env): string {\n return join(dataDirectory(env), 'config.json');\n}\n\nfunction parseConfig(raw: unknown): DexConfig {\n if (!raw || typeof raw !== 'object') throw new DexError('config_invalid', '\uC124\uC815 \uD30C\uC77C\uC774 \uAC1D\uCCB4\uAC00 \uC544\uB2D9\uB2C8\uB2E4.');\n const value = raw as Record<string, unknown>;\n const profiles: Record<string, DexProfile> = {};\n if (value.profiles && typeof value.profiles === 'object') {\n for (const [name, profile] of Object.entries(value.profiles as Record<string, unknown>)) {\n if (!profile || typeof profile !== 'object') continue;\n const serverUrl = String((profile as Record<string, unknown>).serverUrl ?? '').trim();\n if (serverUrl) profiles[name] = { serverUrl };\n }\n }\n // \uC815\uADDC\uD654\uB294 \uD55C \uACF3(local-tools-config)\uC5D0\uB9CC \uC788\uB2E4 \u2014 \uC608\uC804\uC5D4 \uC5EC\uAE30\uC640 \uB3C4\uAD6C \uCABD\uC5D0 \uAC01\uAC01\n // \uC788\uC5C8\uACE0, \uC0C1\uD55C\uAC12\uC774 \uC11C\uB85C \uB2EC\uB790\uB2E4.\n const localTools = normalizeLocalToolsConfig(value.localTools);\n return {\n version: 1,\n currentProfile: String(value.currentProfile || DEFAULT_PROFILE),\n profiles,\n localTools,\n };\n}\n\nexport class FileConfigStore implements ConfigStore {\n private queue: Promise<void> = Promise.resolve();\n\n constructor(readonly path = configPath()) {}\n\n async read(): Promise<DexConfig> {\n try {\n return parseConfig(JSON.parse(await readFile(this.path, 'utf8')));\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return defaultConfig();\n if (error instanceof DexError) throw error;\n throw new DexError('config_invalid', `\uC124\uC815 \uD30C\uC77C\uC744 \uC77D\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${this.path}`, error);\n }\n }\n\n async write(config: DexConfig): Promise<void> {\n const operation = async (): Promise<void> => {\n await mkdir(dirname(this.path), { recursive: true, mode: 0o700 });\n const temporary = `${this.path}.${process.pid}.tmp`;\n await writeFile(temporary, `${JSON.stringify(config, null, 2)}\\n`, { mode: 0o600 });\n await chmod(temporary, 0o600);\n await rename(temporary, this.path);\n };\n this.queue = this.queue.then(operation, operation);\n await this.queue;\n }\n}\n\nexport class MemoryConfigStore implements ConfigStore {\n constructor(private value: DexConfig = defaultConfig()) {}\n\n async read(): Promise<DexConfig> {\n return structuredClone(this.value);\n }\n\n async write(config: DexConfig): Promise<void> {\n this.value = structuredClone(config);\n }\n}\n\nexport function validateProfileName(input: string): string {\n const name = input.trim();\n if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/.test(name)) {\n throw new DexError(\n 'config_invalid',\n '\uD504\uB85C\uD544 \uC774\uB984\uC740 \uC601\uBB38\uC790/\uC22B\uC790\uB85C \uC2DC\uC791\uD558\uACE0 \uC601\uBB38\uC790, \uC22B\uC790, \uC810, \uBC11\uC904, \uD558\uC774\uD508\uB9CC \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.',\n );\n }\n return name;\n}\n\n/**\n * \uC0AC\uB78C\uC774 \uCE5C \uAC83\uC744 \uC11C\uBC84 \uC8FC\uC18C\uB85C \uB9CC\uB4E0\uB2E4.\n *\n * **\uC2A4\uD0B4\uC744 \uC0DD\uB7B5\uD574\uB3C4 \uB41C\uB2E4.** `xgen.example.com` \uC774\uB77C\uACE0 \uCE58\uBA74 `https://` \uB97C \uBD99\uC778\uB2E4 \u2014\n * \uBE0C\uB77C\uC6B0\uC800 \uC8FC\uC18C\uCC3D\uC774 \uD558\uB294 \uC77C\uACFC \uAC19\uB2E4. \uC608\uC804\uC5D0\uB294 \uC5EC\uAE30\uC11C \uAC70\uC808\uD558\uACE0 \"http:// \uB610\uB294\n * https://\uB85C \uC2DC\uC791\uD574\uC57C \uD569\uB2C8\uB2E4\" \uB77C\uACE0 \uD588\uB294\uB370, \uADF8 \uB9D0\uC740 \uB9DE\uC9C0\uB9CC \uC0AC\uC6A9\uC790\uAC00 \uD560 \uC77C\uC740\n * \uC5EC\uB35F \uAE00\uC790\uB97C \uC55E\uC5D0 \uB354 \uCE58\uB294 \uAC83\uBFD0\uC774\uC5C8\uB2E4. \uAE30\uACC4\uAC00 \uD560 \uC218 \uC788\uB294 \uC77C\uC774\uB2E4.\n *\n * `http://` \uB97C \uBD99\uC774\uC9C0 \uC54A\uACE0 `https://` \uB97C \uBD99\uC774\uB294 \uC774\uC720: \uD3C9\uBB38\uC73C\uB85C \uB5A8\uC5B4\uB728\uB9AC\uB294 \uCABD\uC774\n * \uC870\uC6A9\uD55C \uC0AC\uACE0\uC774\uAE30 \uB54C\uBB38\uC774\uB2E4. \uC815\uB9D0\uB85C http \uAC00 \uD544\uC694\uD55C \uC0AC\uB0B4 \uC11C\uBC84\uB77C\uBA74 \uADF8\uB807\uAC8C \uC801\uC73C\uBA74 \uB41C\uB2E4.\n *\n * \uBD99\uC5EC \uC8FC\uC9C0 \uC54A\uB294 \uACBD\uC6B0\uB3C4 \uC788\uB2E4 \u2014 `ftp://` \uCC98\uB7FC **\uB2E4\uB978 \uC2A4\uD0B4\uC744 \uBA85\uC2DC**\uD588\uC744 \uB54C\uB2E4.\n * \uADF8\uAC74 \uC624\uD0C0\uAC00 \uC544\uB2C8\uB77C \uC758\uB3C4\uC774\uACE0, \uADF8 \uC758\uB3C4\uAC00 \uD2C0\uB838\uB2E4\uB294 \uAC83\uC744 \uB9D0\uD574 \uC918\uC57C \uD55C\uB2E4.\n */\nexport function validateServerUrl(input: string): string {\n const raw = input.trim();\n if (!raw) {\n throw new DexError('config_invalid', '\uC11C\uBC84 \uC8FC\uC18C\uB97C \uC785\uB825\uD558\uC138\uC694.');\n }\n // \uC2A4\uD0B4\uC774 \uC544\uC608 \uC5C6\uC744 \uB54C\uB9CC \uBD99\uC778\uB2E4. `//host` \uB3C4 \uC2A4\uD0B4 \uC5C6\uB294 \uAC83\uC73C\uB85C \uBCF8\uB2E4.\n const hasScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:\\/\\//.test(raw);\n const bare = raw.replace(/^\\/+/, '');\n // \uB85C\uCEEC \uC8FC\uC18C\uB9CC http \uB85C \uBD99\uC778\uB2E4. https \uB97C \uBD99\uC774\uBA74 TLS \uB85C \uBC18\uB4DC\uC2DC \uC2E4\uD328\uD558\uACE0, \uC0AC\uC6A9\uC790\uB294\n // \uC11C\uBC84\uAC00 \uC8FD\uC740 \uC904 \uC548\uB2E4 \u2014 \uB85C\uCEEC \uAC1C\uBC1C \uC11C\uBC84\uB294 \uAC70\uC758 \uC5B8\uC81C\uB098 \uD3C9\uBB38\uC774\uB2E4. \uC815\uB9D0\uB85C \uB85C\uCEEC\uC5D0\n // https \uB97C \uC4F4\uB2E4\uBA74 \uADF8\uB807\uAC8C \uC801\uC73C\uBA74 \uB41C\uB2E4.\n const local = /^(localhost|127\\.0\\.0\\.1|\\[?::1\\]?)(:|$|\\/)/i.test(bare);\n const candidate = hasScheme ? raw : `${local ? 'http' : 'https'}://${bare}`;\n\n let url: URL;\n try {\n url = new URL(candidate);\n } catch {\n throw new DexError('config_invalid', '\uC11C\uBC84 \uC8FC\uC18C\uB97C \uC54C\uC544\uBCFC \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. \uC608: xgen.example.com');\n }\n if (url.protocol !== 'http:' && url.protocol !== 'https:') {\n throw new DexError('config_invalid', '\uC11C\uBC84 URL\uC740 http:// \uB610\uB294 https://\uB9CC \uC0AC\uC6A9\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.');\n }\n if (!url.hostname) {\n throw new DexError('config_invalid', '\uC11C\uBC84 \uC8FC\uC18C\uC5D0 \uD638\uC2A4\uD2B8\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4. \uC608: xgen.example.com');\n }\n if (url.username || url.password || url.search || url.hash) {\n throw new DexError('config_invalid', '\uC11C\uBC84 URL\uC5D0\uB294 \uC790\uACA9 \uC99D\uBA85, query, fragment\uB97C \uB123\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.');\n }\n return url.toString().replace(/\\/$/, '');\n}\n", "/**\n * CLI/RPC \uAC00 \uB2E4\uB8E8\uB294 \uC124\uC815 \uBAA8\uC591 \u2194 \uC5D4\uC9C4 \uB3C4\uAD6C \uC81C\uACF5\uC790\uC758 \uBAA8\uC591.\n *\n * \uB450 \uBAA8\uC591\uC740 \uC0AC\uC2E4\uC0C1 \uAC19\uB2E4 \u2014 \uC774\uB984\uB9CC \uB2E4\uB974\uB2E4(`blockedCommands` \u2194 `blocked`). \uC608\uC804\uC5D0\n * \uB450 \uC800\uC7A5\uC18C\uC5D0 \uD558\uB098\uC529 \uC788\uC5C8\uB358 \uD754\uC801\uC774\uACE0, \uC9C0\uAE08\uC740 \uD55C\uCABD\uC774 \uC815\uBCF8\uC774\uACE0 \uB2E4\uB978 \uCABD\uC740 \uADF8\uAC83\uC758\n * \uBC14\uAE65 \uD45C\uD604\uC774\uB2E4. \uC5EC\uAE30\uAC00 \uADF8 \uB458\uC744 \uC787\uB294 \uC720\uC77C\uD55C \uC790\uB9AC\uB2E4.\n *\n * \uD558\uB098\uB9CC \uB2E4\uB974\uB2E4: `allowDangerous`.\n *\n * \uC5D4\uC9C4\uC740 \uC704\uD5D8\uD55C \uBA85\uB839\uC744 **\uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBB3C\uC5B4\uC11C** \uCC98\uB9AC\uD55C\uB2E4(InteractionPort). \uADF8\uB7F0\uB370\n * CLI \uB97C \uD30C\uC774\uD504\uC5D0 \uBB3C\uB824 \uB3CC\uB9AC\uAC70\uB098 \uC2A4\uD06C\uB9BD\uD2B8\uB85C \uBD80\uB97C \uB54C\uB294 \uBB3C\uC744 \uC0AC\uB78C\uC774 \uC5C6\uB2E4. \uADF8\uB54C\n * `allowDangerous: true` \uB294 \"\uC124\uC815 \uD30C\uC77C\uB85C \uBBF8\uB9AC \uC2B9\uC778\uD588\uB2E4\"\uB294 \uB73B\uC774\uACE0, \uADF8\uAC74 \uC138\uC158 \uB0B4\uB0B4\n * \uC2B9\uC778\uD55C \uAC83\uACFC \uC815\uD655\uD788 \uAC19\uB2E4 \u2014 \uADF8\uB798\uC11C \uADF8 \uB2F5\uC744 \uADF8\uB300\uB85C \uB0B4\uB294 InteractionPort \uB85C \uBC14\uAFBC\uB2E4.\n * \uBD88\uB9AC\uC5B8\uC744 \uB3C4\uAD6C \uCF54\uB4DC\uC5D0 \uB2E4\uC2DC \uC2EC\uC9C0 \uC54A\uB294 \uC774\uC720\uB294, \uADF8\uB7EC\uBA74 \uC2B9\uC778 \uACBD\uB85C\uAC00 \uB458\uC774 \uB418\uACE0 \uD55C\uCABD\uB9CC\n * \uACE0\uCCD0\uC9C0\uAE30 \uB54C\uBB38\uC774\uB2E4.\n */\nimport type { InteractionPort } from './ports/index';\nimport type { LocalShellConfig } from './local-tools';\n\nexport interface LocalToolsConfig {\n /** \uBA85\uC2DC\uC801 opt-in. false \uBA74 \uB85C\uCEEC \uB3C4\uAD6C\uAC00 \uC804\uD600 \uB178\uCD9C\uB418\uC9C0 \uC54A\uB294\uB2E4. */\n enabled: boolean;\n /** Shell \uACFC \uC0C1\uB300\uACBD\uB85C \uD30C\uC77C \uB3C4\uAD6C\uC758 \uAE30\uBCF8 \uC791\uC5C5 \uB514\uB809\uD130\uB9AC. */\n cwd: string;\n /** \uD3EC\uADF8\uB77C\uC6B4\uB4DC Shell \uC758 \uBCBD\uC2DC\uACC4 \uC0C1\uD55C(ms). */\n timeoutMs: number;\n /** \uAD6C\uC870\uC801 \uD30C\uC77C \uB3C4\uAD6C\uAC00 \uB9CC\uC9C8 \uC218 \uC788\uB294 \uB8E8\uD2B8\uB4E4. */\n allowedRoots: string[];\n /** Shell \uC774 \uAC70\uBD80\uD558\uB294 \uCCAB \uD1A0\uD070\uB4E4. */\n blockedCommands: string[];\n /** \uBB3C\uC744 \uC0AC\uB78C\uC774 \uC5C6\uB294 \uC2E4\uD589\uC5D0\uC11C \uD30C\uAD34\uC801 \uBA85\uB839\uC744 \uBBF8\uB9AC \uC2B9\uC778\uD55C\uB2E4. */\n allowDangerous: boolean;\n}\n\nexport function defaultLocalToolsConfig(): LocalToolsConfig {\n return {\n enabled: false,\n cwd: '',\n timeoutMs: 120_000,\n allowedRoots: [],\n blockedCommands: [],\n allowDangerous: false,\n };\n}\n\nconst MIN_TIMEOUT = 1_000;\nconst MAX_TIMEOUT = 3_600_000;\n\nfunction cleanList(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n const out: string[] = [];\n for (const item of value) {\n const s = String(item ?? '').trim();\n if (s && !out.includes(s)) out.push(s);\n }\n return out;\n}\n\nexport function normalizeLocalToolsConfig(value: unknown): LocalToolsConfig {\n const d = defaultLocalToolsConfig();\n const v = (value && typeof value === 'object' ? value : {}) as Record<string, unknown>;\n const timeout = Number(v.timeoutMs);\n return {\n enabled: v.enabled === true,\n cwd: String(v.cwd ?? '').trim(),\n timeoutMs: Number.isFinite(timeout)\n ? Math.min(MAX_TIMEOUT, Math.max(MIN_TIMEOUT, Math.round(timeout)))\n : d.timeoutMs,\n allowedRoots: cleanList(v.allowedRoots),\n blockedCommands: cleanList(v.blockedCommands),\n allowDangerous: v.allowDangerous === true,\n };\n}\n\n/** \uBC14\uAE65 \uD45C\uD604 \u2192 \uB3C4\uAD6C \uC81C\uACF5\uC790\uAC00 \uBC1B\uB294 \uBAA8\uC591. */\nexport function toShellConfig(config: LocalToolsConfig): LocalShellConfig {\n return {\n enabled: config.enabled,\n cwd: config.cwd,\n timeoutMs: config.timeoutMs,\n allowedRoots: config.allowedRoots,\n blocked: config.blockedCommands,\n };\n}\n\n/**\n * `allowDangerous` \uB97C \uC2B9\uC778 \uD3EC\uD2B8\uB85C \uBC14\uAFBC\uB2E4.\n *\n * \uCF1C\uC838 \uC788\uC73C\uBA74 \"\uC774 \uC138\uC158 \uB3D9\uC548 \uD5C8\uC6A9\"\uC744 \uADF8\uB300\uB85C \uB2F5\uD558\uACE0, \uAEBC\uC838 \uC788\uC73C\uBA74 `undefined` \uB97C\n * \uB3CC\uB824\uC900\uB2E4 \u2014 \uADF8\uB7EC\uBA74 \uD638\uC2A4\uD2B8\uAC00 \uC900 \uD3EC\uD2B8(\uC788\uB2E4\uBA74)\uAC00 \uADF8\uB300\uB85C \uC4F0\uC774\uACE0, \uC5C6\uC73C\uBA74 \uC5D4\uC9C4\uC774\n * \uAC70\uBD80\uD55C\uB2E4. \uC5EC\uAE30\uC11C \uC784\uC758\uB85C 'deny' \uB97C \uB2F5\uD558\uC9C0 \uC54A\uB294 \uC774\uC720: \uADF8\uB7EC\uBA74 \uB370\uC2A4\uD06C\uD1B1\uC5D0\uC11C \uBB3C\uC5B4\uBCFC\n * \uC218 \uC788\uB294\uB370\uB3C4 \uC124\uC815 \uD558\uB098 \uB54C\uBB38\uC5D0 \uBABB \uBB3B\uAC8C \uB41C\uB2E4.\n */\nexport function dangerousApprovalFromConfig(\n config: LocalToolsConfig,\n): InteractionPort['confirmDangerous'] | undefined {\n if (!config.allowDangerous) return undefined;\n return async () => 'session';\n}\n", "import { randomUUID } from 'node:crypto';\nimport { XgenClient } from '@dex/protocol';\nimport type { ConfigStore } from './config-store';\nimport { validateProfileName, validateServerUrl } from './config-store';\nimport type { CredentialStore } from './credential-store';\nimport { DexError, isUnauthorized } from './errors';\nimport { getMcpBridge, type McpBridge, type McpBridgeStatus } from './mcp-bridge';\nimport {\n getLocalToolProvider,\n type LocalToolProvider,\n type LocalToolResult,\n type LocalToolSchema,\n} from './local-tools';\nimport {\n dangerousApprovalFromConfig,\n normalizeLocalToolsConfig,\n toShellConfig,\n type LocalToolsConfig,\n} from './local-tools-config';\nimport { hostPorts, bindHost, isHostBound } from './host';\nimport type {\n Agent,\n AgentCreateOptions,\n AgentListQuery,\n AgentListResult,\n CreateAgentInput,\n AuthStatus,\n ChatEvent,\n ChatInput,\n Conversation,\n DexProfile,\n HistoryTurn,\n ResolvedChatInput,\n StoredSession,\n} from './contract';\nimport type {\n SshConfig,\n SshServer,\n SshServerInput,\n SshTestResult,\n} from '@dex/protocol/ssh';\n\ninterface ClientRecord {\n profile: string;\n serverUrl: string;\n client: XgenClient;\n authenticated: boolean;\n persisting: Promise<void>;\n}\n\nexport interface ProfileSummary extends DexProfile {\n name: string;\n current: boolean;\n}\n\nexport interface LocalToolsStatus {\n config: LocalToolsConfig;\n /** \uC9C0\uAE08 **\uC5D0\uC774\uC804\uD2B8\uC5D0\uAC8C \uB178\uCD9C\uB418\uB294** \uB3C4\uAD6C. \uAEBC\uC838 \uC788\uC73C\uBA74 \uBE44\uC5B4 \uC788\uB2E4. */\n tools: LocalToolSchema[];\n /** \uC774 \uAE30\uAE30\uAC00 \uC81C\uACF5\uD560 \uC218 \uC788\uB294 \uC804\uCCB4 \uBAA9\uB85D \u2014 \uCF1C\uC9D0 \uC5EC\uBD80\uC640 \uBB34\uAD00. \uC0AC\uC6A9\uC790\uAC00\n * \"\uBB58 \uD560 \uC218 \uC788\uC9C0\"\uB97C \uBB3C\uC744 \uB54C\uC758 \uB2F5\uC774\uB2E4. */\n catalog: LocalToolSchema[];\n bridge: McpBridgeStatus;\n}\n\nexport interface DexEngineOptions {\n localToolProvider?: LocalToolProvider;\n localToolBridge?: McpBridge;\n log?: (message: string) => void;\n}\n\nexport class DexEngine {\n private clients = new Map<string, ClientRecord>();\n private readonly localTools: LocalToolProvider;\n private readonly localToolBridge: McpBridge;\n\n constructor(\n private readonly configs: ConfigStore,\n private readonly credentials: CredentialStore,\n options: DexEngineOptions = {},\n ) {\n this.localTools = options.localToolProvider ?? getLocalToolProvider();\n this.localToolBridge = options.localToolBridge ?? getMcpBridge();\n }\n\n async listProfiles(): Promise<ProfileSummary[]> {\n const config = await this.configs.read();\n return Object.entries(config.profiles)\n .map(([name, profile]) => ({ name, ...profile, current: name === config.currentProfile }))\n .sort((a, b) => a.name.localeCompare(b.name));\n }\n\n onLocalToolsStatus(listener: (status: McpBridgeStatus) => void): () => void {\n this.localToolBridge.setStatusListener(listener);\n return () => this.localToolBridge.setStatusListener(() => undefined);\n }\n\n /**\n * \uC124\uC815\uC744 \uB3C4\uAD6C \uC81C\uACF5\uC790\uC5D0 \uBC18\uC601\uD55C\uB2E4.\n *\n * `allowDangerous` \uB294 \uC5EC\uAE30\uC11C \uC2B9\uC778 \uD3EC\uD2B8\uB85C \uBC14\uB010\uB2E4 \u2014 \uBB3C\uC744 \uC0AC\uB78C\uC774 \uC5C6\uB294 \uC2E4\uD589\uC5D0\uC11C\n * \"\uC124\uC815\uC73C\uB85C \uBBF8\uB9AC \uC2B9\uC778\uD588\uB2E4\"\uB97C \uD45C\uD604\uD558\uB294 \uC720\uC77C\uD55C \uC790\uB9AC\uB2E4. \uC774\uBBF8 \uD638\uC2A4\uD2B8\uAC00 \uBD99\uC5B4 \uC788\uC73C\uBA74\n * \uADF8 \uD3EC\uD2B8\uB97C \uC720\uC9C0\uD55C \uCC44 \uC2B9\uC778 \uB2F5\uB9CC \uB36E\uB294\uB2E4(\uB370\uC2A4\uD06C\uD1B1\uC5D0\uC11C \uBB3C\uC744 \uC218 \uC788\uB294 \uB2A5\uB825\uC744\n * \uC124\uC815 \uD558\uB098\uB85C \uC783\uC9C0 \uC54A\uAC8C).\n */\n private applyToolConfig(config: LocalToolsConfig): void {\n this.localTools.configure(toShellConfig(config));\n const preApproved = dangerousApprovalFromConfig(config);\n if (!preApproved) return;\n const current = isHostBound() ? hostPorts() : null;\n if (!current) return;\n bindHost({\n ...current,\n interaction: { ...(current.interaction ?? {}), confirmDangerous: preApproved },\n });\n }\n\n async localToolsStatus(): Promise<LocalToolsStatus> {\n const config = normalizeLocalToolsConfig((await this.configs.read()).localTools);\n this.applyToolConfig(config);\n const tools = this.localTools.advertise();\n // \uC0C1\uD0DC\uB294 \uBE0C\uB9BF\uC9C0\uAC00 \uB9D0\uD558\uB294 \uADF8\uB300\uB85C \uC4F4\uB2E4 \u2014 \uC608\uC804 CLI \uB294 \uC5EC\uAE30\uC11C advertisedTools \uB97C\n // \uC190\uC73C\uB85C \uCC44\uC6E0\uB294\uB370, \uADF8\uB7EC\uBA74 \"\uC11C\uBC84\uAC00 \uD655\uC778\uD55C \uC218\"\uC640 \"\uC6B0\uB9AC\uAC00 \uAC00\uC9C4 \uC218\"\uAC00 \uAC19\uC740 \uD544\uB4DC\uC5D0\n // \uC11E\uC5EC \uCE74\uD0C8\uB85C\uADF8\uAC00 \uC544\uC9C1 \uC548 \uBD99\uC5C8\uB294\uB370 \uBD99\uC740 \uAC83\uCC98\uB7FC \uBCF4\uC600\uB2E4.\n const bridge = this.localToolBridge.status();\n return { config, tools, catalog: this.localTools.catalog(), bridge };\n }\n\n async configureLocalTools(patch: Partial<LocalToolsConfig>): Promise<LocalToolsStatus> {\n const config = await this.configs.read();\n const current = normalizeLocalToolsConfig(config.localTools);\n config.localTools = normalizeLocalToolsConfig({\n ...current,\n ...patch,\n allowedRoots: patch.allowedRoots ?? current.allowedRoots,\n blockedCommands: patch.blockedCommands ?? current.blockedCommands,\n });\n await this.configs.write(config);\n this.applyToolConfig(config.localTools);\n if (!config.localTools.enabled) this.localToolBridge.stop();\n else this.localToolBridge.refreshCatalog();\n return this.localToolsStatus();\n }\n\n async runLocalTool(tool: string, args: unknown): Promise<LocalToolResult> {\n const config = normalizeLocalToolsConfig((await this.configs.read()).localTools);\n this.applyToolConfig(config);\n return this.localTools.callTool(tool, args);\n }\n\n async startLocalTools(requestedProfile?: string, waitMs = 0): Promise<LocalToolsStatus> {\n const config = normalizeLocalToolsConfig((await this.configs.read()).localTools);\n this.applyToolConfig(config);\n if (!config.enabled) {\n this.localToolBridge.stop();\n return this.localToolsStatus();\n }\n const record = await this.authenticatedRecord(requestedProfile);\n const userId = record.client.user?.userId?.trim();\n if (!userId) throw new DexError('auth_invalid', '\uB85C\uCEEC \uB3C4\uAD6C \uC5F0\uACB0\uC5D0 \uD544\uC694\uD55C \uC0AC\uC6A9\uC790 ID\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.');\n this.localToolBridge.start({\n serverUrl: record.serverUrl,\n userId,\n // CLI \uB294 \uC0AC\uB0B4 \uC778\uC99D\uC11C\uB97C \uC544\uC9C1 \uC124\uC815\uC73C\uB85C \uBC1B\uC9C0 \uC54A\uB294\uB2E4 \u2014 \uAE30\uBCF8\uC740 \uAC80\uC99D\uC774\uB2E4.\n allowPrivateCertificate: false,\n getToken: async () => {\n await this.flush(record);\n return record.client.getAccessTokenAfterRotation() || (await this.credentials.get(record.profile))?.accessToken || null;\n },\n refreshAuth: async () => {\n const session = await this.credentials.get(record.profile);\n const token = await record.client.ensureFreshAuth(session?.refreshToken);\n await this.flush(record);\n return token;\n },\n });\n if (waitMs > 0) await this.localToolBridge.waitUntilReady(waitMs);\n return this.localToolsStatus();\n }\n\n stopLocalTools(): void {\n this.localToolBridge.stop();\n }\n\n // \u2500\u2500 SSH \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n //\n // \uAC1C\uC778 SSH \uC11C\uBC84 \uBAA9\uB85D\uC740 XGEN \uACC4\uC815\uC5D0 \uC788\uACE0 \uC811\uC18D\uC740 \uC11C\uBC84\uAC00 \uC5F0\uB2E4 \u2014 \uC774 \uAE30\uAE30\uC5D0\uC11C\n // \uB2FF\uB294\uC9C0\uB294 \uC5D0\uC774\uC804\uD2B8\uC5D0\uAC8C \uC544\uBB34 \uC758\uBBF8\uAC00 \uC5C6\uB2E4. \uADF8\uB798\uC11C \uC5EC\uAE30\uB294 \uC587\uC740 \uD1B5\uACFC \uACC4\uCE35\uC774\uACE0,\n // \uAC80\uC99D(\uC774\uB984 \uADDC\uCE59 \u00B7 \uC810\uD504 \uADF8\uB798\uD504 \u00B7 \uC790\uACA9\uC99D\uBA85 \uC720\uBB34)\uC740 \uC804\uBD80 \uC11C\uBC84\uAC00 \uD55C\uB2E4.\n //\n // \uBE44\uBC00\uBC88\uD638\uC640 \uAC1C\uC778\uD0A4\uB294 \uC751\uB2F5\uC5D0 \uC2E4\uB9AC\uC9C0 \uC54A\uB294\uB2E4. \uC4F0\uAE30\uB294 \uBD80\uBD84 \uC218\uC815\uC774\uB77C \uBCF4\uB0B4\uC9C0 \uC54A\uC740\n // \uC790\uACA9\uC99D\uBA85\uC740 \uC720\uC9C0\uB418\uACE0 \uBE48 \uBB38\uC790\uC5F4\uB85C\uB9CC \uC9C0\uC6CC\uC9C4\uB2E4 \u2014 \uC124\uBA85\uB9CC \uACE0\uCE58\uB824\uB358 \uC800\uC7A5\uC774 \uC811\uC18D\uC744\n // \uB04A\uC73C\uBA74 \uC548 \uB41C\uB2E4.\n\n async sshConfig(profile?: string): Promise<SshConfig> {\n const record = await this.authenticatedRecord(profile);\n return record.client.ssh.getConfig();\n }\n\n async setSshEnabled(enabled: boolean, profile?: string): Promise<SshConfig> {\n const record = await this.authenticatedRecord(profile);\n return record.client.ssh.setEnabled(enabled);\n }\n\n async createSshServer(input: SshServerInput, profile?: string): Promise<SshServer> {\n const record = await this.authenticatedRecord(profile);\n return record.client.ssh.createServer(input);\n }\n\n async updateSshServer(\n name: string,\n input: SshServerInput,\n profile?: string,\n ): Promise<SshServer> {\n const record = await this.authenticatedRecord(profile);\n return record.client.ssh.updateServer(name, input);\n }\n\n async deleteSshServer(name: string, profile?: string): Promise<SshConfig> {\n const record = await this.authenticatedRecord(profile);\n return record.client.ssh.deleteServer(name);\n }\n\n /** \uC11C\uBC84\uAC00 \uC810\uD504 \uACBD\uB85C\uB97C \uADF8\uB300\uB85C \uD0C0\uACE0 \uC2E4\uC81C\uB85C \uC811\uC18D\uD574 \uBCF8\uB2E4. \uB9C8\uC2A4\uD130 \uC2A4\uC704\uCE58\uC640 \uBB34\uAD00\uD558\uB2E4 \u2014\n * \uCF1C\uAE30 \uC804\uC5D0 \uB9DE\uB294\uC9C0 \uD655\uC778\uD560 \uC218 \uC788\uC5B4\uC57C \uD55C\uB2E4. */\n async testSshServer(name: string, profile?: string): Promise<SshTestResult> {\n const record = await this.authenticatedRecord(profile);\n return record.client.ssh.testServer(name);\n }\n\n async setProfile(nameInput: string, serverUrlInput: string): Promise<ProfileSummary> {\n const name = validateProfileName(nameInput);\n const serverUrl = validateServerUrl(serverUrlInput);\n const config = await this.configs.read();\n const previous = config.profiles[name];\n config.profiles[name] = { serverUrl };\n if (!config.profiles[config.currentProfile]) config.currentProfile = name;\n await this.configs.write(config);\n this.clients.delete(name);\n if (name === config.currentProfile && previous?.serverUrl !== serverUrl) this.localToolBridge.stop();\n if (previous && previous.serverUrl !== serverUrl) await this.credentials.delete(name);\n return { name, serverUrl, current: name === config.currentProfile };\n }\n\n async useProfile(nameInput: string): Promise<ProfileSummary> {\n const name = validateProfileName(nameInput);\n const config = await this.configs.read();\n const profile = config.profiles[name];\n if (!profile) throw new DexError('not_found', `\uD504\uB85C\uD544\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${name}`);\n config.currentProfile = name;\n await this.configs.write(config);\n this.localToolBridge.stop();\n return { name, ...profile, current: true };\n }\n\n async login(email: string, password: string, requestedProfile?: string): Promise<AuthStatus> {\n if (!email.trim() || !password) throw new DexError('usage_error', '\uC774\uBA54\uC77C\uACFC \uBE44\uBC00\uBC88\uD638\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.');\n const { name, profile } = await this.resolveProfile(requestedProfile);\n this.clients.delete(name);\n const record = this.createClient(name, profile.serverUrl);\n const result = await record.client.login(email.trim(), password);\n const session: StoredSession = {\n serverUrl: profile.serverUrl,\n accessToken: result.accessToken,\n refreshToken: result.refreshToken,\n };\n await this.credentials.set(name, session);\n await this.flush(record);\n record.authenticated = true;\n return {\n profile: name,\n serverUrl: profile.serverUrl,\n authenticated: true,\n user: record.client.user ?? undefined,\n };\n }\n\n async authStatus(requestedProfile?: string): Promise<AuthStatus> {\n const { name, profile } = await this.resolveProfile(requestedProfile);\n const session = await this.credentials.get(name);\n if (!session || session.serverUrl !== profile.serverUrl) {\n return {\n profile: name,\n serverUrl: profile.serverUrl,\n authenticated: false,\n reason: 'missing_session',\n };\n }\n\n try {\n const record = await this.ensureAuthenticated(name, profile.serverUrl, session);\n return {\n profile: name,\n serverUrl: profile.serverUrl,\n authenticated: true,\n user: record.client.user ?? undefined,\n };\n } catch (error) {\n if (error instanceof DexError && error.code === 'network_error') {\n return {\n profile: name,\n serverUrl: profile.serverUrl,\n authenticated: false,\n reason: 'network',\n };\n }\n if (error instanceof DexError && (error.code === 'auth_required' || error.code === 'auth_invalid')) {\n return {\n profile: name,\n serverUrl: profile.serverUrl,\n authenticated: false,\n reason: 'invalid_session',\n };\n }\n throw error;\n }\n }\n\n async logout(requestedProfile?: string): Promise<void> {\n const { name, profile } = await this.resolveProfile(requestedProfile);\n const session = await this.credentials.get(name);\n if (session?.serverUrl === profile.serverUrl) {\n const record = this.createClient(name, profile.serverUrl);\n record.client.setTokens(session.accessToken, session.refreshToken);\n await record.client.logout().catch(() => {});\n }\n await this.credentials.delete(name);\n this.clients.delete(name);\n this.localToolBridge.stop();\n }\n\n async listAgents(query: AgentListQuery = {}, requestedProfile?: string): Promise<AgentListResult> {\n return this.withAuthRetry(requestedProfile, (client) => client.agents.list(query));\n }\n\n /**\n * \uB9CC\uB4E4\uAE30 \uD654\uBA74\uC774 \uADF8\uB9B4 \uAC83 \u2014 \uD504\uB85C\uBC14\uC774\uB354\u00B7\uBAA8\uB378\uACFC \uC190\uB308 \uC218 \uC788\uB294 \uC124\uC815.\n *\n * \uBAA9\uB85D\uC740 \uC11C\uBC84\uAC00 Agent XGeny \uB178\uB4DC\uC5D0\uC11C \uC77D\uC5B4 \uB0B4\uB824 \uC900\uB2E4. \uCEE4\uB125\uD130\uC640 CLI \uAC00 \uAC01\uC790 \uC801\uC5B4\n * \uB450\uBA74 \uB178\uB4DC\uAC00 \uBC14\uB014 \uB54C\uB9C8\uB2E4 \uC870\uC6A9\uD788 \uB0A1\uB294\uB2E4.\n */\n async agentCreateOptions(requestedProfile?: string): Promise<AgentCreateOptions> {\n return this.withAuthRetry(requestedProfile, (client) => client.agents.createOptions());\n }\n\n /**\n * \uC5D0\uC774\uC804\uD2B8 \uD558\uB098\uB97C \uC138\uC6B4\uB2E4 \u2014 Agent XGeny \uB178\uB4DC \uD558\uB098\uC9DC\uB9AC \uC6CC\uD06C\uD50C\uB85C\uC6B0.\n *\n * \uC5E3\uC9C0\uAC00 \uC5C6\uB294 \uAC83\uC774 \uC774 \uB178\uB4DC\uC758 \uC131\uC9C8\uC774\uB2E4. \uB3C4\uAD6C\uB3C4 \uAE30\uC5B5\uB3C4 \uC704\uC784\uB3C4 \uC790\uAE30\uC9C4\uD654\uB3C4 \uADF8 \uC548\uC5D0\n * \uC788\uC5B4\uC11C \uC5F0\uACB0 \uC5C6\uC774 \uADF8\uB300\uB85C \uB300\uD654\uAC00 \uB41C\uB2E4.\n */\n async createAgent(\n input: CreateAgentInput,\n requestedProfile?: string,\n ): Promise<{ workflowId: string; workflowName: string }> {\n const name = input.name.trim();\n if (!name) throw new DexError('usage_error', '\uC5D0\uC774\uC804\uD2B8 \uC774\uB984\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.');\n if (!input.provider) throw new DexError('usage_error', 'AI \uC81C\uACF5\uC0AC\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.');\n return this.withAuthRetry(requestedProfile, (client) =>\n client.agents.create({ ...input, name }),\n );\n }\n\n async listConversations(requestedProfile?: string): Promise<Conversation[]> {\n return this.withAuthRetry(requestedProfile, (client) => client.history.conversations());\n }\n\n async historyTurns(\n workflowId: string,\n interactionId: string,\n workflowName?: string,\n requestedProfile?: string,\n ): Promise<HistoryTurn[]> {\n if (!workflowId || !interactionId) {\n throw new DexError('usage_error', 'workflowId\uC640 interactionId\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.');\n }\n return this.withAuthRetry(requestedProfile, (client) =>\n client.history.turns(workflowId, interactionId, workflowName),\n );\n }\n\n async resolveChatInput(input: ChatInput): Promise<ResolvedChatInput> {\n const workflowId = input.workflowId.trim();\n if (!workflowId) throw new DexError('usage_error', 'Agent workflow ID\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4.');\n const { name } = await this.resolveProfile(input.profile);\n const workflowName = input.workflowName?.trim() || (await this.findAgent(workflowId, name)).workflowName;\n return {\n profile: name,\n workflowId,\n workflowName,\n input: input.input,\n interactionId: input.interactionId?.trim() || randomUUID(),\n };\n }\n\n async *chat(input: ChatInput, signal?: AbortSignal): AsyncGenerator<ChatEvent, ResolvedChatInput> {\n const resolved = await this.resolveChatInput(input);\n let record = await this.authenticatedRecord(resolved.profile);\n let emitted = false;\n\n try {\n const local = await this.startLocalTools(resolved.profile, 3_000);\n if (local.config.enabled) {\n yield local.bridge.catalogSynced\n ? {\n kind: 'status',\n surface: 'connector_local',\n detail: `\uB85C\uCEEC \uB3C4\uAD6C ${local.bridge.serverToolCount || local.tools.length}\uAC1C \uC5F0\uACB0\uB428`,\n }\n : {\n kind: 'status',\n surface: 'connector_local',\n detail: local.bridge.error || '\uB85C\uCEEC \uB3C4\uAD6C \uCE74\uD0C8\uB85C\uADF8 \uC5F0\uACB0 \uB300\uAE30 \uC911',\n reason: 'bridge_not_ready',\n };\n }\n } catch (error) {\n yield {\n kind: 'status',\n surface: 'connector_local',\n detail: `\uB85C\uCEEC \uB3C4\uAD6C \uC5F0\uACB0 \uC2E4\uD328: ${error instanceof Error ? error.message : String(error)}`,\n reason: 'bridge_error',\n };\n }\n\n for (let attempt = 0; attempt < 2; attempt += 1) {\n try {\n for await (const event of record.client.chat.stream(\n {\n workflowId: resolved.workflowId,\n workflowName: resolved.workflowName,\n input: resolved.input,\n interactionId: resolved.interactionId,\n },\n signal,\n )) {\n emitted = true;\n yield event;\n }\n return resolved;\n } catch (error) {\n if (attempt === 0 && !emitted && isUnauthorized(error)) {\n await this.refresh(record);\n record = await this.authenticatedRecord(resolved.profile);\n continue;\n }\n throw error;\n }\n }\n return resolved;\n }\n\n private async findAgent(selector: string, profile: string): Promise<Agent> {\n const agents = await this.withAuthRetry(profile, (client) => client.agents.listAll({}, 100));\n const normalized = selector.trim().toLocaleLowerCase();\n const found =\n agents.find((agent) => agent.workflowId === selector.trim()) ??\n agents.find((agent) => agent.workflowName.toLocaleLowerCase() === normalized);\n if (!found) throw new DexError('not_found', `Agent\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${selector}`);\n return found;\n }\n\n private async withAuthRetry<T>(\n requestedProfile: string | undefined,\n operation: (client: XgenClient) => Promise<T>,\n ): Promise<T> {\n const record = await this.authenticatedRecord(requestedProfile);\n try {\n return await operation(record.client);\n } catch (error) {\n if (!isUnauthorized(error)) throw error;\n await this.refresh(record);\n return operation(record.client);\n }\n }\n\n private async authenticatedRecord(requestedProfile?: string): Promise<ClientRecord> {\n const { name, profile } = await this.resolveProfile(requestedProfile);\n const session = await this.credentials.get(name);\n if (!session || session.serverUrl !== profile.serverUrl) {\n throw new DexError('auth_required', `\uB85C\uADF8\uC778\uC774 \uD544\uC694\uD569\uB2C8\uB2E4: dex login --profile ${name}`);\n }\n return this.ensureAuthenticated(name, profile.serverUrl, session);\n }\n\n private async ensureAuthenticated(\n name: string,\n serverUrl: string,\n session: StoredSession,\n ): Promise<ClientRecord> {\n const cached = this.clients.get(name);\n if (cached?.authenticated && cached.serverUrl === serverUrl) return cached;\n const record = cached?.serverUrl === serverUrl ? cached : this.createClient(name, serverUrl);\n const state = await record.client.restoreDetailed(session.accessToken, session.refreshToken);\n await this.flush(record);\n if (state === 'valid') {\n record.authenticated = true;\n return record;\n }\n if (state === 'invalid') {\n await this.credentials.delete(name);\n this.clients.delete(name);\n throw new DexError('auth_invalid', `\uC138\uC158\uC774 \uB9CC\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4: dex login --profile ${name}`);\n }\n throw new DexError('network_error', `XGEN \uC11C\uBC84\uC5D0 \uC5F0\uACB0\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${serverUrl}`);\n }\n\n private createClient(profile: string, serverUrl: string): ClientRecord {\n const existing = this.clients.get(profile);\n if (existing?.serverUrl === serverUrl) return existing;\n\n const record = {\n profile,\n serverUrl,\n authenticated: false,\n persisting: Promise.resolve(),\n } as ClientRecord;\n record.client = new XgenClient({\n baseUrl: serverUrl,\n onTokensRotated: (accessToken, refreshToken) => {\n record.persisting = record.persisting.then(() =>\n this.credentials.set(profile, { serverUrl, accessToken, refreshToken }),\n );\n },\n });\n this.clients.set(profile, record);\n return record;\n }\n\n private async refresh(record: ClientRecord): Promise<void> {\n const session = await this.credentials.get(record.profile);\n const accessToken = await record.client.ensureFreshAuth(session?.refreshToken);\n await this.flush(record);\n if (!accessToken) {\n await this.credentials.delete(record.profile);\n record.authenticated = false;\n throw new DexError('auth_invalid', `\uC138\uC158\uC774 \uB9CC\uB8CC\uB418\uC5C8\uC2B5\uB2C8\uB2E4: dex login --profile ${record.profile}`);\n }\n record.authenticated = true;\n }\n\n private async flush(record: ClientRecord): Promise<void> {\n await record.persisting;\n }\n\n private async resolveProfile(requested?: string): Promise<{ name: string; profile: DexProfile }> {\n const config = await this.configs.read();\n const name = requested ? validateProfileName(requested) : config.currentProfile;\n const profile = config.profiles[name];\n if (!profile) {\n throw new DexError(\n 'config_invalid',\n `XGEN \uC11C\uBC84 \uD504\uB85C\uD544\uC774 \uC5C6\uC2B5\uB2C8\uB2E4. \uBA3C\uC800 \uC2E4\uD589\uD558\uC138\uC694: dex profile set ${name} --server <URL>`,\n );\n }\n return { name, profile };\n }\n}\n", "/**\n * AgentDataApi \u2014 \uD55C \uC5D0\uC774\uC804\uD2B8(workflow)\uC758 \uAD00\uCE21 \uB370\uC774\uD130\uC640 \uC0AC\uC6A9\uC790 \uCCA8\uBD80 \uC5C5\uB85C\uB4DC.\n *\n * \uCC44\uD305 \uD5E4\uB354\uC758 [...] \uBA54\uB274\uC5D0\uC11C \uC5EC\uB294 \"\uC5D0\uC774\uC804\uD2B8 \uBDF0\uC5B4\"\uAC00 \uC4F0\uB294 \uC804\uC1A1 \uACC4\uCE35\uC774\uB2E4.\n * \uC11C\uBC84(xgen-workflow)\uC758 \uD45C\uC900 REST(owner/superuser \uAD8C\uD55C, \uBCA0\uC5B4\uB7EC \uD1A0\uD070) \uB97C \uADF8\uB300\uB85C\n * \uD638\uCD9C\uD558\uACE0, **\uC870\uD68C(GET)\uB9CC** \uD55C\uB2E4 \u2014 \uC0DD\uC131/\uC0AD\uC81C/\uBCC0\uACBD \uACBD\uB85C\uB294 \uC5EC\uAE30\uC5D0 \uC5C6\uB2E4.\n *\n * \uC751\uB2F5\uC740 \uC11C\uBC84 \uC640\uC774\uC5B4 \uD3EC\uB9F7(snake_case)\uC744 \uADF8\uB300\uB85C \uD0C0\uC785\uC73C\uB85C \uB454\uB2E4. \uD45C\uC2DC \uC804\uC6A9 \uB370\uC774\uD130\uB77C\n * camelCase \uC7AC\uC0AC\uC0C1\uC740 \uC774\uB4DD \uC5C6\uC774 \uB9E4\uD551 \uBC84\uADF8\uB9CC \uB298\uB9AC\uBBC0\uB85C(voice SttPref \uC640 \uAC19\uC740 \uD310\uB2E8),\n * \uB80C\uB354\uB7EC\uAC00 \uC11C\uBC84 \uD544\uB4DC\uBA85\uC744 \uADF8\uB300\uB85C \uC77D\uB294\uB2E4.\n */\nimport { HttpClient } from './client';\n\n/** \uC5D0\uC774\uC804\uD2B8 \uBDF0\uC5B4\uC758 \uD558\uC704 \uD0ED \u2014 **\uB2E8\uC77C \uC815\uC758**.\n *\n * \uB80C\uB354\uB7EC(workspace-layout)\uC640 main(config \uC758 \uB808\uC774\uC544\uC6C3 \uC601\uC18D \uC2A4\uD0A4\uB9C8)\uC774 \uB458 \uB2E4 \uC4F4\uB2E4.\n * \uC608\uC804\uC5D4 \uB450 \uACF3\uC5D0 \uC720\uB2C8\uC628\uC774 \uBCF5\uC81C\uB3FC \uC788\uC5B4\uC11C, \uD0ED\uC744 \uD558\uB098 \uB298\uB9AC\uBA74 \uC800\uC7A5 \uC2A4\uD0A4\uB9C8 \uCABD\uC774 \uC870\uC6A9\uD788\n * \uC548 \uB9DE\uACE0 \uB808\uC774\uC544\uC6C3 \uBCF5\uC6D0\uC5D0\uC11C \uADF8 \uD0ED\uB9CC \uC0AC\uB77C\uC84C\uB2E4. */\nexport type AgentViewerSub = 'basic' | 'memory' | 'tasks' | 'tools' | 'storage' | 'fulllog';\n\nexport interface WorkspaceUploadResult {\n ok: boolean;\n workflow_id?: string;\n workspace_path?: string;\n path?: string;\n size?: number;\n sha256?: string;\n seq?: number;\n status?: 'pending_approval';\n request_id?: number;\n}\n\n// \u2500\u2500 \uAE30\uBCF8\uC815\uBCF4(basic-info) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n//\n// \uC2E4\uD589 \uC5C6\uC774 \uC7AC\uAD6C\uC131\uD55C \uD134 \uD504\uB86C\uD504\uD2B8 + \uB3C4\uAD6C \uD45C\uBA74. \uC11C\uBC84\uB294 \uB450 \uD45C\uBA74(web/connector)\uC744\n// \uBAA8\uB450 \uB3CC\uB824\uC8FC\uACE0 **\uCEE4\uB125\uD130 \uBDF0\uC5B4\uB294 connector \uB9CC** \uBCF4\uC5EC \uC900\uB2E4 \u2014 \uC774 \uC571\uC5D0\uC11C \uB3C4\uB294 \uD134\uC774\n// \uADF8 \uD45C\uBA74\uC774\uAE30 \uB54C\uBB38\uC774\uB2E4. \uC6F9 \uD654\uBA74\uC740 \uBC18\uB300\uB85C web \uB9CC \uBCF4\uC5EC \uC900\uB2E4.\n\nexport interface BasicInfoPromptSection {\n key: string;\n title: string;\n source: string;\n text: string;\n dynamic: boolean;\n template?: string;\n}\n\nexport interface BasicInfoToolEntry {\n name: string;\n description: string;\n gateway?: boolean;\n}\n\nexport interface BasicInfoGroup {\n key: string;\n title: string;\n kind: string;\n gateway?: string | null;\n disclosure?: string | null;\n note?: string;\n tools: BasicInfoToolEntry[];\n}\n\nexport interface BasicInfoSurface {\n available: boolean;\n note: string;\n prompt: { sections: BasicInfoPromptSection[]; full_prompt: string };\n provision?: {\n exposure: string;\n mode_note: string;\n stages: { key: string; title: string; groups: BasicInfoGroup[] }[];\n };\n tools: BasicInfoToolEntry[];\n native_tools?: { kept: string[]; removed: string[]; note: string } | null;\n skills: { name: string; description: string }[];\n}\n\nexport interface AgentBasicInfo {\n workflow_id: string;\n provider: string;\n model: string;\n is_cli: boolean;\n surfaces?: { web: BasicInfoSurface; connector: BasicInfoSurface };\n errors: string[];\n}\n\n// \u2500\u2500 \uC804\uCCB4\uB85C\uADF8(trace) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport type SpanType =\n | 'agent_input'\n | 'agent_output'\n | 'llm_call'\n | 'tool_call'\n | 'tool_output'\n | 'rag_search'\n | 'file_process'\n | 'error'\n | 'warning'\n | 'info';\n\nexport interface Trace {\n trace_id: string;\n status?: string;\n model_name?: string;\n provider?: string;\n total_spans?: number;\n total_tool_calls?: number;\n total_llm_calls?: number;\n duration_ms?: number;\n error_message?: string;\n created_at?: string;\n updated_at?: string;\n interaction_id?: string;\n}\n\nexport interface Span {\n span_type: SpanType | string;\n span_order?: number;\n tool_name?: string;\n input_data?: unknown;\n output_data?: unknown;\n duration_ms?: number;\n error_message?: string;\n created_at?: string;\n /** \uC11C\uBC84\uAC00 \uBD99\uC778 \uBD80\uAC00 \uC815\uBCF4(JSON \uBB38\uC790\uC5F4 \uB610\uB294 \uAC1D\uCCB4). info span \uC740 \uC5EC\uAE30\uC5D0 \uC2E4\uD589 \uD658\uACBD\n * ``detail`` \uC744 \uB2F4\uB294\uB2E4 \u2014 \uC774 \uD134\uC758 \uB300\uD654 \uCD9C\uCC98\uC640 \uBD99\uC740 \uB85C\uCEEC \uB3C4\uAD6C \uBAA9\uB85D. */\n metadata?: unknown;\n}\n\nexport interface TraceListResult {\n traces: Trace[];\n total?: number;\n page?: number;\n page_size?: number;\n}\n\nexport interface TraceDetail {\n trace: Trace;\n spans: Span[];\n}\n\n// \u2500\u2500 \uBA54\uBAA8\uB9AC(geny-memory) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport interface MemoryFile {\n filename: string;\n title?: string;\n category?: string;\n tags?: string[];\n importance?: string;\n char_count?: number;\n modified?: string;\n first_paragraph?: string;\n}\n\nexport interface MemoryDetail {\n filename: string;\n title?: string;\n body: string;\n category?: string;\n tags?: string[];\n importance?: string;\n frontmatter?: Record<string, unknown>;\n links_to?: string[];\n linked_from?: string[];\n created?: string | null;\n modified?: string | null;\n}\n\nexport interface MemoryListResult {\n files: MemoryFile[];\n total?: number;\n}\n\n// \u2500\u2500 \uC791\uC5C5(geny-tasks) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport interface Task {\n task_id: string;\n kind?: string;\n status?: string;\n created_at?: string;\n started_at?: string;\n completed_at?: string;\n duration_s?: number | null;\n error?: string;\n interaction_id?: string;\n agent_type?: string;\n sub_agent_id?: string;\n title?: string;\n output_truncated?: boolean;\n}\n\n/** \uC9C0\uC18D \uC608\uC57D \uC791\uC5C5(schedule_sessions) \u2014 \uC791\uC5C5(task)\uACFC \uB2E4\uB978 \uAC1D\uCCB4 \uACC4\uC5F4\uC774\uC9C0\uB9CC \uAC19\uC774 \uC628\uB2E4. */\nexport interface Job {\n session_id: string;\n name?: string;\n status?: string;\n schedule_type?: string;\n cron_expression?: string;\n interval_seconds?: number | null;\n next_execution_at?: string;\n last_execution_at?: string;\n last_execution_status?: string;\n total_executions?: number;\n failed_executions?: number;\n created_at?: string;\n created_by_agent?: boolean;\n job_kind?: string;\n job_target?: string;\n notify?: boolean;\n prompt?: string;\n}\n\nexport interface TasksResult {\n tasks: Task[];\n counts?: Record<string, number>;\n total?: number;\n jobs?: Job[];\n}\n\n/** \uC608\uC57D \uC791\uC5C5 1\uD68C \uC2E4\uD589 \uAE30\uB85D. */\nexport interface JobRun {\n execution_number?: number;\n status?: string;\n scheduled_time?: string;\n started_at?: string;\n completed_at?: string;\n duration_s?: number | null;\n output?: string;\n error_message?: string;\n}\n\nexport interface JobRunsResult {\n runs: JobRun[];\n}\n\nexport interface TaskOutput {\n output: string;\n offset?: number;\n next_offset?: number;\n eof?: boolean;\n truncated?: boolean;\n result?: string;\n}\n\n// \u2500\u2500 \uB3C4\uAD6C(geny-tools, forged) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport interface ForgedTool {\n name: string;\n description?: string;\n entrypoint?: string;\n runtime?: string;\n input_schema?: Record<string, unknown>;\n argv?: unknown[];\n /** \uD0A4 \uC774\uB984\uB9CC \u2014 \uAC12(\uC2DC\uD06C\uB9BF)\uC740 \uC11C\uBC84\uAC00 \uC808\uB300 \uB178\uCD9C\uD558\uC9C0 \uC54A\uB294\uB2E4. */\n env_keys?: string[];\n dependencies?: string[];\n env_id?: string;\n timeout_s?: number;\n enabled?: boolean;\n verified?: boolean;\n last_test_error?: string | null;\n created_at?: string;\n updated_at?: string;\n calls?: number;\n errors?: number;\n last_used_at?: string;\n last_error?: string;\n script_exists?: boolean;\n status?: string;\n checked_at?: string | null;\n problem?: string | null;\n // with_source=true \uC77C \uB54C \uBCD1\uD569\uB428\n source?: string | null;\n source_truncated?: boolean;\n source_error?: string | null;\n}\n\nexport interface ToolsResult {\n tools: ForgedTool[];\n total?: number;\n enabled?: number;\n broken?: number;\n unknown?: number;\n unverified?: number;\n}\n\n// \u2500\u2500 \uC2A4\uD1A0\uB9AC\uC9C0(geny-workspace) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nexport interface WsNode {\n name: string;\n /** workspace \uB8E8\uD2B8 \uAE30\uC900 \uC0C1\uB300 \uACBD\uB85C. \uD074\uB77C\uC774\uC5B8\uD2B8\uAC00 \uC774 \uACBD\uB85C\uB85C \uD2B8\uB9AC\uB97C \uC870\uB9BD\uD55C\uB2E4. */\n path: string;\n is_dir: boolean;\n size?: number | null;\n modified_at?: string;\n origin?: string;\n origin_name?: string;\n}\n\nexport interface WorkspaceListResult {\n workflow_id: string;\n files: WsNode[];\n}\n\nexport interface WorkspaceFile {\n workflow_id: string;\n path: string;\n content: string;\n encoding: string;\n}\n\nexport interface WorkspaceBinary {\n bytes: Uint8Array;\n contentType: string;\n}\n\nexport type WorkspaceBinaryPurpose = 'chat_attachment';\n\n/** `/storage/list` \uB294 workspace \uB8E8\uD2B8 \uC0C1\uB300 \uACBD\uB85C\uB97C \uB3CC\uB824\uC8FC\uC9C0\uB9CC \uC77D\uAE30 API\uB294\n * \uC2A4\uD1A0\uB9AC\uC9C0 \uB8E8\uD2B8 \uC0C1\uB300(`workspace/...`)\uB97C \uBC1B\uB294\uB2E4. \uB450 \uACC4\uC57D\uC758 \uACBD\uACC4\uB97C \uC5EC\uAE30\uC11C\uB9CC\n * \uBCF4\uC815\uD574 \uD638\uCD9C\uBD80\uB9C8\uB2E4 \uC811\uB450\uC0AC\uB97C \uBD99\uC600\uB2E4 \uB5D0\uB2E4 \uD558\uC9C0 \uC54A\uAC8C \uD55C\uB2E4. */\nexport function workspaceStoragePath(path: string): string {\n const clean = String(path ?? '')\n .replace(/\\\\/g, '/')\n .replace(/^\\/+/, '');\n return clean === 'workspace' || clean.startsWith('workspace/') ? clean : `workspace/${clean}`;\n}\n\nexport class AgentDataApi {\n constructor(private http: HttpClient) {}\n\n // \u2500\u2500 \uC804\uCCB4\uB85C\uADF8 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n /** \uC774 \uC5D0\uC774\uC804\uD2B8\uC758 \uC2E4\uD589 \uD2B8\uB808\uC774\uC2A4 \uBAA9\uB85D(\uCD5C\uADFC 50). */\n traceList(workflowId: string): Promise<TraceListResult> {\n const params = new URLSearchParams({\n workflow_id: workflowId,\n page: '1',\n page_size: '50',\n });\n return this.http.get<TraceListResult>(`/api/agentflow/trace/list?${params}`);\n }\n\n /** \uD2B8\uB808\uC774\uC2A4 \uD558\uB098\uC758 \uC2A4\uD32C(\uB2E8\uACC4) \uC804\uBD80. */\n traceDetail(traceId: string): Promise<TraceDetail> {\n return this.http.get<TraceDetail>(`/api/agentflow/trace/detail/${encodeURIComponent(traceId)}`);\n }\n\n // \u2500\u2500 \uBA54\uBAA8\uB9AC \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n memoryList(workflowId: string): Promise<MemoryListResult> {\n return this.http.get<MemoryListResult>(\n `/api/agentflow/geny-memory/${encodeURIComponent(workflowId)}/files`,\n );\n }\n\n /** filename \uC740 \uC11C\uBC84\uC5D0\uC11C `{filename:path}` \u2014 \uC2AC\uB798\uC2DC\uB294 \uC0B4\uB9AC\uACE0 \uC138\uADF8\uBA3C\uD2B8\uB9CC \uC778\uCF54\uB529\uD55C\uB2E4. */\n memoryRead(workflowId: string, path: string): Promise<MemoryDetail> {\n const fp = path.split('/').map(encodeURIComponent).join('/');\n return this.http.get<MemoryDetail>(\n `/api/agentflow/geny-memory/${encodeURIComponent(workflowId)}/files/${fp}`,\n );\n }\n\n // \u2500\u2500 \uC791\uC5C5 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n tasksList(workflowId: string): Promise<TasksResult> {\n return this.http.get<TasksResult>(\n `/api/agentflow/geny-tasks/${encodeURIComponent(workflowId)}`,\n );\n }\n\n /** \uC608\uC57D \uC791\uC5C5(job=session_id) 1\uAC74\uC758 \uC2E4\uD589 \uAE30\uB85D. */\n taskRuns(workflowId: string, sessionId?: string): Promise<JobRunsResult> {\n return this.http.get<JobRunsResult>(\n `/api/agentflow/geny-tasks/${encodeURIComponent(workflowId)}/job/${encodeURIComponent(sessionId ?? '')}/runs`,\n );\n }\n\n /** \uBC31\uADF8\uB77C\uC6B4\uB4DC/\uC11C\uBE0C\uC5D0\uC774\uC804\uD2B8 \uC791\uC5C5(task_id) 1\uAC74\uC758 \uCD9C\uB825. */\n taskOutput(workflowId: string, runId: string): Promise<TaskOutput> {\n return this.http.get<TaskOutput>(\n `/api/agentflow/geny-tasks/${encodeURIComponent(workflowId)}/task/${encodeURIComponent(runId)}/output`,\n );\n }\n\n // \u2500\u2500 \uC138\uC158 \uC218\uBA85 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n /** '\uC9C4\uD589 \uC911 \uB300\uD654' \uC885\uB8CC \u2014 \uC11C\uBC84\uAC00 \uB4E4\uACE0 \uC788\uB294 \uC138\uC158 RAM(executor + \uB77C\uC6B0\uD305)\uC744 \uD68C\uC218\uD55C\uB2E4.\n * \uC774\uB825\uC740 \uC9C0\uC6B0\uC9C0 \uC54A\uB294\uB2E4(\uC0AD\uC81C\uB294 \uC138\uC158 \uC885\uB8CC\uC774\uC9C0 \uB300\uD654 \uAE30\uB85D \uC0AD\uC81C\uAC00 \uC544\uB2C8\uB2E4). */\n endSession(workflowId: string, interactionId: string): Promise<{ ok: boolean }> {\n return this.http.post<{ ok: boolean }>(\n `/api/agentflow/geny-agent/${encodeURIComponent(workflowId)}/end-session`,\n { interaction_id: interactionId },\n );\n }\n\n // \u2500\u2500 \uAE30\uBCF8\uC815\uBCF4 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n /** \uC2E4\uD589 \uC5C6\uC774 \uC7AC\uAD6C\uC131\uD55C \uD134 \uD504\uB86C\uD504\uD2B8 + \uB3C4\uAD6C \uD45C\uBA74(web/connector \uB458 \uB2E4). */\n basicInfo(workflowId: string): Promise<AgentBasicInfo> {\n return this.http.get<AgentBasicInfo>(\n `/api/agentflow/${encodeURIComponent(workflowId)}/basic-info`,\n );\n }\n\n // \u2500\u2500 \uB3C4\uAD6C \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n toolsList(workflowId: string): Promise<ToolsResult> {\n return this.http.get<ToolsResult>(\n `/api/agentflow/geny-tools/${encodeURIComponent(workflowId)}`,\n );\n }\n\n /** \uC81C\uC791 \uB3C4\uAD6C \uD558\uB098 \u2014 \uC18C\uC2A4 \uCF54\uB4DC\uAE4C\uC9C0. */\n toolGet(workflowId: string, functionId: string): Promise<ForgedTool> {\n return this.http.get<ForgedTool>(\n `/api/agentflow/geny-tools/${encodeURIComponent(workflowId)}/${encodeURIComponent(functionId)}?with_source=true`,\n );\n }\n\n // \u2500\u2500 \uC2A4\uD1A0\uB9AC\uC9C0 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n /** \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uC804\uCCB4(\uD3C9\uBA74) \uBAA9\uB85D \u2014 \uD30C\uC77C/\uD3F4\uB354 \uAC01\uAC01 \uD55C \uD56D\uBAA9. `path` \uB294 \uC608\uC57D(\uBBF8\uC0AC\uC6A9). */\n workspaceTree(workflowId: string, _path?: string): Promise<WorkspaceListResult> {\n return this.http.get<WorkspaceListResult>(\n `/api/agentflow/geny-workspace/${encodeURIComponent(workflowId)}/storage/list`,\n );\n }\n\n /** \uD14D\uC2A4\uD2B8 \uD30C\uC77C \uBBF8\uB9AC\uBCF4\uAE30. \uBC14\uC774\uB108\uB9AC/\uACFC\uB300 \uD30C\uC77C\uC740 \uC11C\uBC84\uAC00 415/413 \uC73C\uB85C \uAC70\uBD80\uD55C\uB2E4. */\n workspaceFile(workflowId: string, path: string): Promise<WorkspaceFile> {\n const params = new URLSearchParams({ path: workspaceStoragePath(path) });\n return this.http.get<WorkspaceFile>(\n `/api/agentflow/geny-workspace/${encodeURIComponent(workflowId)}/storage/text?${params}`,\n );\n }\n\n /** \uC6D0\uBC14\uC774\uD2B8 \uD30C\uC77C \uC77D\uAE30 \u2014 \uC774\uBBF8\uC9C0\uCC98\uB7FC \uD14D\uC2A4\uD2B8 API\uB85C \uC77D\uC744 \uC218 \uC5C6\uB294 \uBBF8\uB9AC\uBCF4\uAE30\uC6A9. */\n workspaceBinary(\n workflowId: string,\n path: string,\n purpose?: WorkspaceBinaryPurpose,\n ): Promise<WorkspaceBinary> {\n const encodedPath = workspaceStoragePath(path).split('/').map(encodeURIComponent).join('/');\n const query = purpose ? `?purpose=${encodeURIComponent(purpose)}` : '';\n return this.http.getBinary(\n `/api/agentflow/geny-workspace/${encodeURIComponent(workflowId)}/storage-raw/${encodedPath}${query}`,\n );\n }\n\n /** Image bytes land in this agent's durable workspace before chat execution. */\n workspaceUpload(\n workflowId: string,\n bytes: Uint8Array,\n filename: string,\n mimeType: string,\n interactionId: string,\n attachmentId: string,\n ): Promise<WorkspaceUploadResult> {\n const form = new FormData();\n const owned = new Uint8Array(bytes);\n form.append('file', new Blob([owned.buffer], { type: mimeType }), filename);\n return this.http.upload<WorkspaceUploadResult>(\n `/api/agentflow/geny-workspace/${encodeURIComponent(workflowId)}/storage/upload?subdir=uploads&purpose=chat_attachment&interaction_id=${encodeURIComponent(interactionId)}&attachment_id=${encodeURIComponent(attachmentId)}`,\n form,\n { timeoutMs: 300_000 },\n );\n }\n}\n", "/**\n * FilestoreApi \u2014 XGen **\uD30C\uC77C \uC800\uC7A5\uC18C**(xgen-documents filestore)\uC758 \uC870\uD68C \uD45C\uBA74.\n *\n * \uB3D9\uAE30\uD654 \uC804\uC1A1(`/api/filestore/sync/*`)\uACFC \uBCC4\uAC1C\uB85C, \uD56D\uBAA9/\uD3F4\uB354 \uBA54\uD0C0\uC640 \uC624\uD53C\uC2A4 \uBB38\uC11C\n * \uC11C\uBC84 \uB80C\uB354(filestore-preview \u2014 \uC6F9 [\uD30C\uC77C \uC800\uC7A5\uC18C] \uBDF0\uC5B4\uC640 \uB3D9\uC77C \uACC4\uC57D)\uB97C \uB2E4\uB8EC\uB2E4.\n * \uB370\uC2A4\uD06C\uD1B1 \uD30C\uC77C \uBDF0\uC5B4\uAC00 \"\uACBD\uB85C \u2192 \uD56D\uBAA9 id\" \uB97C \uD480\uACE0 \uBB38\uC11C \uD398\uC774\uC9C0 \uC774\uBBF8\uC9C0\uB97C \uBC1B\uC744 \uB54C\n * \uC4F4\uB2E4 \u2014 \uC571\uC740 \uC11C\uBC84 \uACBD\uB85C\uB97C \uC9C1\uC811 \uBD80\uB974\uC9C0 \uC54A\uB294\uB2E4\uB294 \uACC4\uC57D \uADDC\uCE59\uC758 \uC774\uD589\uCC98\uB2E4.\n */\nimport type { HttpClient } from './client';\n\nexport interface FilestoreFolder {\n id: number;\n folder_name: string;\n full_path: string;\n parent_folder_id: number | null;\n}\n\nexport interface FilestoreItem {\n id: number;\n file_name: string;\n file_size: number;\n folder_id: number | null;\n}\n\nexport interface FilestoreOfficePreview {\n /** \uD398\uC774\uC9C0 \uD30C\uC77C\uBA85(slide_NNN.svg | page-N.png) \u2014 officePreviewPage \uB85C \uAC00\uC838\uC628\uB2E4. */\n pages: string[];\n}\n\nexport class FilestoreApi {\n constructor(private http: HttpClient) {}\n\n /** \uC804\uCCB4 \uD3F4\uB354 \uD3C9\uBA74 \uBAA9\uB85D \u2014 full_path \uB85C \uACBD\uB85C\uB97C \uD47C\uB2E4. */\n async tree(): Promise<{ folders: FilestoreFolder[] }> {\n const res = await this.http.get<{ folders?: FilestoreFolder[] }>('/api/filestore/tree');\n return { folders: res.folders ?? [] };\n }\n\n /** \uB8E8\uD2B8 \uB0B4\uC6A9 \u2014 \uB8E8\uD2B8 \uBC14\uB85C \uBC11 \uD3F4\uB354/\uD30C\uC77C. */\n async root(): Promise<{ folders: FilestoreFolder[]; items: FilestoreItem[] }> {\n const res = await this.http.get<{ folders?: FilestoreFolder[]; items?: FilestoreItem[] }>(\n '/api/filestore/root',\n );\n return { folders: res.folders ?? [], items: res.items ?? [] };\n }\n\n /** \uD3F4\uB354 \uB0B4\uC6A9. */\n async folderItems(folderId: number): Promise<{ items: FilestoreItem[] }> {\n const res = await this.http.get<{ items?: FilestoreItem[] }>(\n `/api/filestore/folders/${folderId}/items`,\n );\n return { items: res.items ?? [] };\n }\n\n /** \uC800\uC7A5\uC18C \uC0C1\uB300 \uACBD\uB85C(\"a/b.txt\") \u2192 \uD56D\uBAA9. \uC5C6\uC73C\uBA74 null. */\n async resolveItemByPath(path: string): Promise<FilestoreItem | null> {\n const clean = path.replace(/^\\/+/, '');\n const slash = clean.lastIndexOf('/');\n const dirPath = slash === -1 ? '' : clean.slice(0, slash);\n const fileName = slash === -1 ? clean : clean.slice(slash + 1);\n let items: FilestoreItem[];\n if (!dirPath) {\n items = (await this.root()).items;\n } else {\n const { folders } = await this.tree();\n const folder = folders.find(\n (f) => String(f.full_path ?? '').replace(/^\\/+|\\/+$/g, '') === dirPath,\n );\n if (!folder) return null;\n items = (await this.folderItems(folder.id)).items;\n }\n return items.find((it) => it.file_name === fileName) ?? null;\n }\n\n /**\n * \uC624\uD53C\uC2A4 \uBB38\uC11C(pptx/docx/xlsx + hwp/hwpx/doc/xls/ppt) \uC11C\uBC84 \uB80C\uB354 \u2014 \uD398\uC774\uC9C0 \uBAA9\uB85D.\n * \uCF5C\uB4DC \uB80C\uB354\uB294 \uC218\uC2ED \uCD08\uAE4C\uC9C0 \uAC78\uB9B0\uB2E4 (\uC6F9 \uD30C\uC77C \uC800\uC7A5\uC18C\uC640 \uB3D9\uC77C \uACC4\uC57D).\n */\n async officePreview(itemId: number): Promise<FilestoreOfficePreview> {\n const res = await this.http.get<{ pages?: string[] }>(\n `/api/agentflow/filestore-preview/${itemId}`,\n { timeoutMs: 300_000 },\n );\n return { pages: Array.isArray(res.pages) ? res.pages : [] };\n }\n\n /** \uB80C\uB354\uB41C \uD398\uC774\uC9C0 \uC774\uBBF8\uC9C0 \uBC14\uC774\uD2B8. */\n officePreviewPage(\n itemId: number,\n page: string,\n ): Promise<{ bytes: Uint8Array; contentType: string }> {\n return this.http.getBinary(\n `/api/agentflow/filestore-preview/${itemId}/page/${encodeURIComponent(page)}`,\n );\n }\n}\n", "/**\n * Agent (agentflow) listing \u2014 the \"Agent \uBAA9\uB85D\" grid.\n *\n * Uses GET /api/agentflow/list/detail (paged) which is exactly what the UI grid\n * shows. Requires the `main.agentflow:read` permission on the logged-in user.\n * The returned {workflowId, workflowName} pair is what identifies an agent for\n * starting a chat. Node-agnostic: agent_geny / agent_xgen / agent_harness all\n * appear here and all chat through the same execute-stream endpoint.\n */\nimport { HttpClient } from './client';\nimport type {\n Agent,\n AgentCreateOptions,\n AgentListQuery,\n AgentListResult,\n AgentCreateSetting,\n CreateAgentInput,\n} from './types';\n\ninterface RawAgent {\n id: number;\n workflow_id: string;\n workflow_name: string;\n node_count?: number;\n is_shared?: boolean;\n is_deployed?: boolean;\n is_completed?: boolean;\n workflow_type?: string;\n description?: string;\n username?: string;\n full_name?: string;\n created_at?: string;\n updated_at?: string;\n has_agent_geny?: boolean;\n}\n\ninterface RawListResponse {\n items?: RawAgent[];\n workflows?: RawAgent[]; // legacy alias\n pagination?: {\n page: number;\n page_size: number;\n total_count: number;\n total_pages: number;\n };\n}\n\nfunction mapAgent(r: RawAgent): Agent {\n return {\n id: r.id,\n workflowId: r.workflow_id,\n workflowName: r.workflow_name,\n nodeCount: r.node_count ?? 0,\n isShared: !!r.is_shared,\n isDeployed: !!r.is_deployed,\n isCompleted: !!r.is_completed,\n workflowType: r.workflow_type ?? 'canvas',\n description: r.description ?? '',\n username: r.username ?? '',\n fullName: r.full_name ?? '',\n createdAt: r.created_at ?? '',\n updatedAt: r.updated_at ?? '',\n hasAgentGeny: !!r.has_agent_geny,\n };\n}\n\ninterface RawCreateOptions {\n providers?: Array<{\n value?: string;\n label?: string;\n models?: Array<{ value?: string; label?: string }>;\n default_model?: string;\n needs_base_url?: boolean;\n }>;\n default_provider?: string;\n settings?: Array<Record<string, unknown>>;\n defaults?: Record<string, unknown>;\n}\n\nexport class AgentsApi {\n constructor(private http: HttpClient) {}\n\n /** Paged agent list matching the UI grid (default page_size 24). */\n async list(query: AgentListQuery = {}): Promise<AgentListResult> {\n const params = new URLSearchParams();\n params.set('page', String(query.page ?? 1));\n params.set('page_size', String(query.pageSize ?? 24));\n if (query.search) params.set('search', query.search);\n if (query.status) params.set('status', query.status);\n if (query.owner) params.set('owner', query.owner);\n if (query.includeHarness) params.set('include_harness', 'true');\n\n const res = await this.http.get<RawListResponse>(`/api/agentflow/list/detail?${params}`);\n const raw = res.items ?? res.workflows ?? [];\n return {\n items: raw.map(mapAgent),\n pagination: {\n page: res.pagination?.page ?? query.page ?? 1,\n pageSize: res.pagination?.page_size ?? query.pageSize ?? 24,\n totalCount: res.pagination?.total_count ?? raw.length,\n totalPages: res.pagination?.total_pages ?? 1,\n },\n };\n }\n\n /**\n * Fetch every page and return the full agent list. Convenience for small\n * accounts / pickers; bounded by `maxPages` to avoid runaway loops.\n */\n async listAll(query: AgentListQuery = {}, maxPages = 50): Promise<Agent[]> {\n const first = await this.list({ ...query, page: 1 });\n const all = [...first.items];\n for (let page = 2; page <= Math.min(first.pagination.totalPages, maxPages); page++) {\n const next = await this.list({ ...query, page });\n all.push(...next.items);\n }\n return all;\n }\n /** \uB9CC\uB4E4\uAE30 \uD654\uBA74\uC774 \uADF8\uB9B4 \uAC83 \u2014 \uD504\uB85C\uBC14\uC774\uB354\u00B7\uBAA8\uB378 \uBAA9\uB85D\uACFC \uC190\uB308 \uC218 \uC788\uB294 \uC124\uC815. */\n async createOptions(): Promise<AgentCreateOptions> {\n const res = await this.http.get<RawCreateOptions>('/api/agentflow/create/options');\n return {\n providers: (res.providers ?? []).map((p) => ({\n value: String(p.value ?? ''),\n label: p.label ?? String(p.value ?? ''),\n models: (p.models ?? []).map((m) => ({\n value: String(m.value ?? ''),\n label: m.label ?? String(m.value ?? ''),\n })),\n defaultModel: p.default_model,\n needsBaseUrl: !!p.needs_base_url,\n })),\n defaultProvider: res.default_provider ?? 'openai',\n settings: (res.settings ?? []).map((raw) => ({\n id: String(raw.id ?? ''),\n label: String(raw.label ?? raw.id ?? ''),\n type: String(raw.type ?? 'STR'),\n default: raw.default,\n options: raw.options as AgentCreateSetting['options'],\n description: raw.description as string | undefined,\n min: raw.min as number | undefined,\n max: raw.max as number | undefined,\n step: raw.step as number | undefined,\n })),\n defaults: res.defaults ?? {},\n };\n }\n\n /**\n * \uC5D0\uC774\uC804\uD2B8 \uD558\uB098\uB97C \uC138\uC6B0\uACE0 \uADF8 workflowId \uB97C \uB3CC\uB824\uC900\uB2E4.\n *\n * \uB9CC\uB4E4\uC5B4\uC9C0\uB294 \uAC83\uC740 \uB178\uB4DC \uD558\uB098\uC9DC\uB9AC \uC6CC\uD06C\uD50C\uB85C\uC6B0\uB2E4. \uC5E3\uC9C0\uAC00 \uC5C6\uB294 \uAC83\uC774 \uC774 \uB178\uB4DC\uC758 \uC131\uC9C8\uC774\uB77C\n * \uC5F0\uACB0 \uC5C6\uC774 \uADF8\uB300\uB85C \uB300\uD654\uAC00 \uB41C\uB2E4.\n */\n async create(input: CreateAgentInput): Promise<{ workflowId: string; workflowName: string }> {\n const res = await this.http.post<{ workflow_id?: string; workflow_name?: string }>(\n '/api/agentflow/create',\n {\n workflow_name: input.name,\n provider: input.provider,\n model: input.model ?? null,\n settings: input.settings ?? null,\n },\n );\n return {\n workflowId: String(res.workflow_id ?? ''),\n workflowName: String(res.workflow_name ?? input.name),\n };\n }\n}\n", "/**\n * Password hashing for XGEN login.\n *\n * The XGEN gateway compares the submitted password VERBATIM against the stored\n * `users.password_hash`, and the official frontend sends the SHA-256 hex digest\n * of the plaintext (never the plaintext itself). We must do the same, or login\n * always fails. Uses Web Crypto (available in Electron renderer, modern Node,\n * and browsers); no native dependency.\n */\nasync function subtle(): Promise<SubtleCrypto> {\n // Browser / Electron renderer: globalThis.crypto. Node (<20 has no global\n // crypto): fall back to the built-in webcrypto.\n const g = (globalThis as { crypto?: Crypto }).crypto;\n if (g?.subtle) return g.subtle;\n const { webcrypto } = await import('node:crypto');\n return webcrypto.subtle as unknown as SubtleCrypto;\n}\n\nexport async function sha256Hex(plaintext: string): Promise<string> {\n const data = new TextEncoder().encode(plaintext);\n const digest = await (await subtle()).digest('SHA-256', data);\n return Array.from(new Uint8Array(digest))\n .map((b) => b.toString(16).padStart(2, '0'))\n .join('');\n}\n", "/**\n * Authentication against the XGEN gateway (native Rust routes under /api/auth).\n *\n * Flow: login \u2192 {access_token, refresh_token}. Every other call sends\n * `Authorization: Bearer <access_token>`. The current user + permissions are\n * resolved via validate-token (there is no /me endpoint). Access tokens are\n * refreshed with the refresh token.\n */\nimport { HttpClient } from './client';\nimport { sha256Hex } from './hash';\nimport type { CurrentUser, LoginResult } from './types';\n\ninterface RawLoginResponse {\n success: boolean;\n message?: string;\n access_token: string | null;\n refresh_token: string | null;\n token_type?: string;\n user_id?: string;\n username?: string;\n}\n\ninterface RawValidateResponse {\n valid: boolean;\n user_id?: string;\n username?: string;\n is_superuser?: boolean;\n roles?: string[];\n permissions?: string[];\n new_access_token?: string | null;\n}\n\nexport class AuthApi {\n constructor(private http: HttpClient) {}\n\n /**\n * Log in with email + plaintext password. The password is SHA-256-hex hashed\n * before sending (the gateway compares the hash verbatim). Returns tokens +\n * identity. Throws ApiError on bad credentials / locked / inactive account.\n */\n async login(email: string, password: string): Promise<LoginResult> {\n const passwordHash = await sha256Hex(password);\n const res = await this.http.post<RawLoginResponse>(\n '/api/auth/login',\n { email, password: passwordHash, token: null },\n // login itself must not trigger the onAuthFailure hook\n );\n if (!res.success || !res.access_token) {\n throw new Error(res.message || '\uB85C\uADF8\uC778\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.');\n }\n return {\n accessToken: res.access_token,\n refreshToken: res.refresh_token ?? undefined,\n tokenType: res.token_type ?? 'bearer',\n userId: res.user_id ?? '',\n username: res.username ?? email,\n };\n }\n\n /** SSO login with a pre-obtained token. */\n async loginWithToken(ssoToken: string): Promise<LoginResult> {\n const res = await this.http.post<RawLoginResponse>('/api/auth/login', {\n token: ssoToken,\n });\n if (!res.success || !res.access_token) {\n throw new Error(res.message || 'SSO \uB85C\uADF8\uC778\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.');\n }\n return {\n accessToken: res.access_token,\n refreshToken: res.refresh_token ?? undefined,\n tokenType: res.token_type ?? 'bearer',\n userId: res.user_id ?? '',\n username: res.username ?? '',\n };\n }\n\n /**\n * Validate the access token and return the current user + permissions. If the\n * access token is expired and a refresh token is supplied, the gateway may\n * return a rotated access token in `newAccessToken`.\n */\n async validate(\n accessToken: string,\n refreshToken?: string,\n ): Promise<{ user: CurrentUser | null; newAccessToken?: string }> {\n const res = await this.http.post<RawValidateResponse>(\n '/api/auth/validate-token',\n { token: accessToken, refresh_token: refreshToken },\n { auth: false },\n );\n if (!res.valid) return { user: null, newAccessToken: res.new_access_token ?? undefined };\n return {\n user: {\n userId: res.user_id ?? '',\n username: res.username ?? '',\n isSuperuser: !!res.is_superuser,\n roles: res.roles ?? [],\n permissions: res.permissions ?? [],\n },\n newAccessToken: res.new_access_token ?? undefined,\n };\n }\n\n /** Exchange a refresh token for a fresh access token. */\n async refresh(refreshToken: string): Promise<string | null> {\n const res = await this.http.post<RawLoginResponse>(\n '/api/auth/refresh',\n { refresh_token: refreshToken },\n { auth: false },\n );\n return res.success ? res.access_token : null;\n }\n\n async logout(accessToken: string): Promise<void> {\n try {\n await this.http.post('/api/auth/logout', { token: accessToken }, { timeoutMs: 8000 });\n } catch {\n /* logout is best-effort */\n }\n }\n\n /** Server session policy (timeouts) \u2014 useful for a refresh scheduler. */\n async sessionConfig(): Promise<{\n session_timeout_min?: number;\n inactivity_timeout_min?: number;\n refresh_token_ttl_days?: number;\n server_now_epoch_ms?: number;\n }> {\n return this.http.get('/api/auth/session-config', { timeoutMs: 8000 });\n }\n}\n", "/**\n * Avatars \u2014 binary assets + the shared avatar store (xgen-documents).\n *\n * Web(\uB9C8\uC774\uD398\uC774\uC9C0 \uC544\uBC14\uD0C0 \uC124\uC815)\uACFC \uB3D9\uC77C\uD55C API \uD45C\uBA74:\n * - \uC5D0\uC14B: POST /api/storage/avatar/upload (multipart, zip \uBAA8\uB378/\uC0AC\uC9C4),\n * DELETE /api/storage/avatar/{id}\n * - \uC2A4\uD1A0\uC5B4: publish / list / download / rate / unpublish\n * per-user config (\uC120\uD0DD/\uC774\uB984/\uBCC0\uD615)\uB294 PreferencesApi \uAC00 \uB2F4\uB2F9\uD55C\uB2E4.\n */\nimport { HttpClient } from './client';\nimport type { AvatarDescriptor, AvatarRuntime } from './preferences';\n\nexport interface StoreAvatar {\n storeId: string;\n name: string;\n description: string;\n runtime: AvatarRuntime;\n publisherUserId: number;\n publisherName: string;\n descriptor: AvatarDescriptor;\n createdAt: number;\n downloads: number;\n ratingAvg: number;\n ratingCount: number;\n myRating: number | null;\n}\n\nexport class AvatarsApi {\n constructor(private http: HttpClient) {}\n\n /** Upload one avatar file (model zip or photo) \u2192 parsed descriptor. */\n async uploadAsset(bytes: Uint8Array, filename: string): Promise<AvatarDescriptor> {\n const form = new FormData();\n // Uint8Array may be a view over a larger/shared buffer (IPC) \u2014 copy to a\n // standalone ArrayBuffer so Blob captures exactly the file bytes.\n const buf = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer;\n form.append('file', new Blob([buf]), filename);\n const res = await this.http.upload<{ avatar: AvatarDescriptor }>('/api/storage/avatar/upload', form);\n return res.avatar;\n }\n\n /** Delete an avatar's stored asset tree. */\n async deleteAsset(avatarId: string): Promise<void> {\n await this.http.json('DELETE', `/api/storage/avatar/${avatarId}`);\n }\n\n // \u2500\u2500 store \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async storeList(): Promise<StoreAvatar[]> {\n const res = await this.http.get<{ items: StoreAvatar[] }>('/api/storage/avatar/store/list');\n return res.items || [];\n }\n\n async storePublish(descriptor: AvatarDescriptor, name: string, description = ''): Promise<StoreAvatar> {\n const res = await this.http.post<{ item: StoreAvatar }>('/api/storage/avatar/store/publish', {\n descriptor,\n name,\n description,\n });\n return res.item;\n }\n\n /** Add a store avatar to my assets \u2192 descriptor with a fresh local id. */\n async storeDownload(storeId: string): Promise<AvatarDescriptor> {\n const res = await this.http.post<{ avatar: AvatarDescriptor }>(`/api/storage/avatar/store/${storeId}/download`, {});\n return res.avatar;\n }\n\n async storeRate(storeId: string, stars: number): Promise<StoreAvatar> {\n const res = await this.http.post<{ item: StoreAvatar }>(`/api/storage/avatar/store/${storeId}/rate`, { stars });\n return res.item;\n }\n\n async storeUnpublish(storeId: string): Promise<void> {\n await this.http.json('DELETE', `/api/storage/avatar/store/${storeId}`);\n }\n}\n", "/**\n * Minimal SSE frame parser for the XGEN chat stream.\n *\n * XGEN emits two frame shapes, separated by a blank line (\\n\\n):\n * 1. Named events: `event: <name>\\ndata: <json>\\n\\n`\n * 2. Default events: `data: <json>\\n\\n` (json carries a `type` field)\n *\n * `parseSseChunk` is a stateful, incremental parser: feed it raw text chunks as\n * they arrive off the network; it buffers partial frames and yields complete\n * `{ event, data }` records. A frame may also carry multiple `data:` lines\n * (concatenated with \\n per the SSE spec).\n */\n\nexport interface SseFrame {\n /** The `event:` name, or undefined for a default (\"message\") frame. */\n event?: string;\n /** The concatenated `data:` payload (raw string; usually JSON). */\n data: string;\n}\n\nexport class SseParser {\n private buffer = '';\n\n /** Feed a raw chunk; returns any complete frames it produced. */\n push(chunk: string): SseFrame[] {\n this.buffer += chunk;\n const frames: SseFrame[] = [];\n let sep: number;\n // Frames are separated by a blank line. Handle both \\n\\n and \\r\\n\\r\\n.\n while ((sep = this.nextSeparator()) !== -1) {\n const rawFrame = this.buffer.slice(0, sep.valueOf());\n this.buffer = this.buffer.slice(this.advanceAfterSeparator(sep));\n const frame = this.parseFrame(rawFrame);\n if (frame) frames.push(frame);\n }\n return frames;\n }\n\n /** Flush any trailing frame not terminated by a blank line (stream end). */\n flush(): SseFrame[] {\n const rest = this.buffer.trim();\n this.buffer = '';\n if (!rest) return [];\n const frame = this.parseFrame(rest);\n return frame ? [frame] : [];\n }\n\n private nextSeparator(): number {\n const a = this.buffer.indexOf('\\n\\n');\n const b = this.buffer.indexOf('\\r\\n\\r\\n');\n if (a === -1) return b;\n if (b === -1) return a;\n return Math.min(a, b);\n }\n\n private advanceAfterSeparator(sep: number): number {\n return this.buffer.startsWith('\\r\\n\\r\\n', sep) ? sep + 4 : sep + 2;\n }\n\n private parseFrame(raw: string): SseFrame | null {\n let event: string | undefined;\n const dataLines: string[] = [];\n for (const line of raw.split(/\\r?\\n/)) {\n if (!line || line.startsWith(':')) continue; // comment / heartbeat\n if (line.startsWith('event:')) {\n event = line.slice(6).trim();\n } else if (line.startsWith('data:')) {\n dataLines.push(line.slice(5).replace(/^ /, ''));\n }\n }\n if (dataLines.length === 0 && event === undefined) return null;\n return { event, data: dataLines.join('\\n') };\n }\n}\n", "/**\n * Chat streaming against an XGEN agent.\n *\n * Endpoint: POST /api/agentflow/execute/based-id/stream \u2192 text/event-stream.\n * This one endpoint drives EVERY agent node type (agent_geny, agent_xgen,\n * agent_harness, \u2026) \u2014 the connector is node-agnostic. Continue a conversation\n * by reusing the same `interactionId` across turns.\n *\n * The raw SSE frames are normalized into the `ChatEvent` union (see types.ts)\n * so callers get one stream of typed events: text chunks, tool/agent activity,\n * node status, citations (inside tool_result), an `execution_io` id, and a\n * terminal `end`.\n */\nimport { HttpClient } from './client';\nimport { SseParser } from './sse';\nimport type { ChatEvent, ChatRequest, ToolEvent } from './types';\n\nfunction toRequestBody(req: ChatRequest): Record<string, unknown> {\n return {\n workflow_name: req.workflowName,\n workflow_id: req.workflowId,\n input_data: req.input,\n interaction_id: req.interactionId,\n selected_collections: req.selectedCollections ?? [],\n selected_files: req.selectedFiles ?? [],\n include_logs: req.includeLogs ?? true,\n include_node_status: req.includeNodeStatus ?? true,\n include_tool_events: req.includeToolEvents ?? true,\n response_format: 'stream',\n // \uB300\uD654 \uCD9C\uCC98 \u2014 \uC774 \uD134\uC774 \uB370\uC2A4\uD06C\uD1B1 \uCEE4\uB125\uD130\uC5D0\uC11C \uC654\uC74C\uC744 \uC11C\uBC84\uC5D0 \uC54C\uB9B0\uB2E4. \uC11C\uBC84\uB294 \uC774\n // \uAC12\uC774 \"connector\" \uC778 \uC2E4\uD589\uC5D0\uB9CC \uCEE4\uB125\uD130 \uD638\uC2A4\uD305 \uB85C\uCEEC \uB3C4\uAD6C(\uC774 PC \uC758 \uD30C\uC77C/\uC178/\n // \uBE0C\uB77C\uC6B0\uC800/\uC624\uD53C\uC2A4 \uC870\uC791)\uB97C \uC5D0\uC774\uC804\uD2B8\uC5D0 \uB178\uCD9C\u00B7\uC2E4\uD589\uD55C\uB2E4. \uC6F9 \uCC44\uD305\uC740 \uC774 \uD544\uB4DC\uB97C\n // \uBCF4\uB0B4\uC9C0 \uC54A\uC73C\uBBC0\uB85C, \uAC19\uC740 \uC0AC\uC6A9\uC790\uAC00 \uCEE4\uB125\uD130\uB97C \uCF1C \uB454 \uC0C1\uD0DC\uB85C \uC6F9\uC5D0\uC11C \uB300\uD654\uD574\uB3C4\n // \uB85C\uCEEC \uB3C4\uAD6C\uB294 \uC808\uB300 \uC791\uB3D9\uD558\uC9C0 \uC54A\uB294\uB2E4.\n client_surface: 'connector',\n // \uC2E4\uD589 \uD658\uACBD \uC9C0\uC2DC \u2014 \uB85C\uCEEC \uC2E4\uD589 v2 \uD3F4\uBC31 \uD134\uC740 'sandbox'(\uC11C\uBC84 sandbox \uAC15\uC81C; \uCEE4\uB125\uD130\n // \uB85C\uCEEC \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uB97C \uC6D0\uACA9 \uC870\uC791\uD558\uB294 \uC911\uAC04 \uD615\uD0DC\uB97C \uC4F0\uC9C0 \uC54A\uB294\uB2E4). \uC5C6\uC73C\uBA74 \uC0DD\uB7B5(auto).\n ...(req.executionTarget ? { execution_target: req.executionTarget } : {}),\n };\n}\n\nfunction mapToolEvent(d: Record<string, unknown>): ToolEvent {\n return {\n eventType: String(d.event_type ?? d.type ?? 'tool'),\n toolName: d.tool_name as string | undefined,\n toolInput: d.tool_input,\n result: d.result as string | undefined,\n resultLength: d.result_length as number | undefined,\n error: d.error as string | undefined,\n citations: d.citations as ToolEvent['citations'],\n runId: d.run_id as string | undefined,\n indicator: d.indicator,\n durationMs: d.duration_ms as number | undefined,\n timestamp: d.timestamp as string | undefined,\n };\n}\n\nfunction parseData(raw: string): Record<string, unknown> | null {\n try {\n const v = JSON.parse(raw);\n return v && typeof v === 'object' ? (v as Record<string, unknown>) : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Translate one SSE frame into a ChatEvent (or null to ignore). Exported for\n * unit testing the protocol mapping without a live server.\n */\nexport function frameToChatEvent(\n frameEvent: string | undefined,\n rawData: string,\n): ChatEvent | null {\n const d = parseData(rawData);\n\n // Named event frames.\n switch (frameEvent) {\n case 'tool':\n return d ? { kind: 'tool', event: mapToolEvent(d) } : null;\n case 'node_status':\n return d\n ? {\n kind: 'node_status',\n event: { nodeId: String(d.node_id ?? ''), status: String(d.status ?? ''), ...d },\n }\n : null;\n case 'log':\n return { kind: 'log', data: d ?? rawData };\n case 'execution_io':\n return d ? { kind: 'execution_io', executionIoId: Number(d.execution_io_id ?? 0) } : null;\n case 'download_artifact':\n return d ? { kind: 'download', data: d } : null;\n case 'a2ui_command':\n return d ? { kind: 'ui_command', surface: 'a2ui', command: d } : null;\n case 'floui_command':\n return d ? { kind: 'ui_command', surface: 'floui', command: d } : null;\n case 'quota_warning':\n return d ? { kind: 'quota', level: 'warning', data: d } : null;\n case 'quota_exceeded':\n return d ? { kind: 'quota', level: 'exceeded', data: d } : null;\n case 'execution_suspended':\n return { kind: 'error', detail: '\uC6CC\uD06C\uD50C\uB85C\uC6B0\uAC00 \uAD00\uB9AC\uC790\uC5D0 \uC758\uD574 \uC77C\uC2DC \uC911\uC9C0\uB418\uC5C8\uC2B5\uB2C8\uB2E4.' };\n case undefined:\n case '':\n case 'message':\n break; // default frame \u2014 dispatch on the JSON `type` below\n default:\n // Unknown named event \u2014 ignore.\n return null;\n }\n\n // Default (\"message\") frames carry a `type`.\n if (!d) return null;\n switch (d.type) {\n case 'data':\n return { kind: 'text', content: String(d.content ?? '') };\n case 'summary': {\n const data = (d.data as Record<string, unknown>) ?? {};\n const outputs = (data.outputs as unknown[]) ?? [];\n return { kind: 'summary', text: outputs.map(String).join(''), data };\n }\n case 'end':\n return { kind: 'end' };\n case 'error':\n return { kind: 'error', detail: String(d.detail ?? d.error ?? 'unknown error') };\n // Some tool/agent frames arrive as bare `data:` JSON (no event: line).\n case 'tool_call':\n case 'tool_start':\n case 'tool_result':\n case 'tool_error':\n return { kind: 'tool', event: mapToolEvent(d) };\n default:\n return null;\n }\n}\n\nexport class ChatApi {\n constructor(private http: HttpClient) {}\n\n /**\n * Stream a chat turn. Yields normalized ChatEvents until the terminal `end`\n * (or the stream closes). Pass an AbortSignal to cancel mid-stream.\n */\n async *stream(req: ChatRequest, signal?: AbortSignal): AsyncGenerator<ChatEvent, void, void> {\n const res = await this.http.stream(\n '/api/agentflow/execute/based-id/stream',\n toRequestBody(req),\n signal,\n );\n const body = res.body;\n if (!body) throw new Error('\uC2A4\uD2B8\uB9BC \uC751\uB2F5 \uBCF8\uBB38\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.');\n\n const reader = (body as ReadableStream<Uint8Array>).getReader();\n const decoder = new TextDecoder();\n const parser = new SseParser();\n try {\n for (;;) {\n const { value, done } = await reader.read();\n if (done) break;\n const frames = parser.push(decoder.decode(value, { stream: true }));\n for (const f of frames) {\n const ev = frameToChatEvent(f.event, f.data);\n if (ev) {\n yield ev;\n if (ev.kind === 'end') return;\n }\n }\n }\n for (const f of parser.flush()) {\n const ev = frameToChatEvent(f.event, f.data);\n if (ev) yield ev;\n }\n } finally {\n try {\n reader.releaseLock();\n } catch {\n /* ignore */\n }\n }\n }\n\n /**\n * Convenience: run a turn to completion and return the accumulated assistant\n * text plus collected tool events. Ignores intermediate UI/log frames.\n */\n async complete(\n req: ChatRequest,\n onEvent?: (e: ChatEvent) => void,\n signal?: AbortSignal,\n ): Promise<{ text: string; tools: ToolEvent[]; error?: string; executionIoId?: number }> {\n let text = '';\n let summary = '';\n const tools: ToolEvent[] = [];\n let error: string | undefined;\n let executionIoId: number | undefined;\n for await (const e of this.stream(req, signal)) {\n onEvent?.(e);\n if (e.kind === 'text') text += e.content;\n else if (e.kind === 'summary') summary = e.text;\n else if (e.kind === 'tool') tools.push(e.event);\n else if (e.kind === 'execution_io') executionIoId = e.executionIoId;\n else if (e.kind === 'error') error = e.detail;\n }\n return { text: text || summary, tools, error, executionIoId };\n }\n}\n", "/** Shared browser contracts used by the main process, preload and renderer. */\n\nexport type BrowserPageMode = 'shared' | 'background';\n\nexport type BrowserLoadingState = 'idle' | 'loading' | 'error';\n\nexport const BROWSER_SEARCH_PROVIDERS = {\n google: {\n label: 'Google',\n searchUrl: 'https://www.google.com/search?q={query}',\n },\n} as const;\n\nexport type BrowserSearchProvider = keyof typeof BROWSER_SEARCH_PROVIDERS;\n\nexport interface BrowserAddressSearchConfig {\n enabled?: boolean;\n provider?: BrowserSearchProvider;\n}\n\n/** Normalize user-entered browser URLs while keeping the runtime scheme allowlist narrow. */\nexport function normalizeBrowserUrl(raw: unknown): string | null {\n const value = String(raw ?? '').trim();\n if (!value || value === 'about:blank') return 'about:blank';\n const withScheme = /^[a-z][a-z0-9+.-]*:/i.test(value) ? value : `https://${value}`;\n try {\n const url = new URL(withScheme);\n if (url.protocol !== 'http:' && url.protocol !== 'https:') return null;\n return url.toString();\n } catch {\n return null;\n }\n}\n\nfunction looksLikeBrowserUrl(value: string): boolean {\n if (value === 'about:blank' || /^[a-z][a-z0-9+.-]*:/i.test(value)) return true;\n if (/\\s/.test(value)) return false;\n const normalized = normalizeBrowserUrl(value);\n if (!normalized || normalized === 'about:blank') return false;\n try {\n const hostname = new URL(normalized).hostname;\n return (\n hostname === 'localhost' ||\n hostname.includes('.') ||\n /^\\d{1,3}(?:\\.\\d{1,3}){3}$/.test(hostname) ||\n hostname.includes(':')\n );\n } catch {\n return false;\n }\n}\n\n/** Resolve an omnibox value to a URL, optionally falling back to a configured search provider. */\nexport function resolveBrowserAddress(\n raw: unknown,\n search: BrowserAddressSearchConfig = {},\n): string | null {\n const value = String(raw ?? '').trim();\n if (!value) return null;\n if (looksLikeBrowserUrl(value)) return normalizeBrowserUrl(value);\n if (!search.enabled) return null;\n const provider = search.provider ?? 'google';\n const definition = BROWSER_SEARCH_PROVIDERS[provider];\n if (!definition) return null;\n return definition.searchUrl.replace('{query}', encodeURIComponent(value));\n}\n\nexport interface BrowserPageInfo {\n pageId: string;\n workflowId: string;\n workflowName: string;\n mode: BrowserPageMode;\n url: string;\n title: string;\n loading: BrowserLoadingState;\n error?: string;\n canGoBack: boolean;\n canGoForward: boolean;\n /** Persisted Electron partition. It contains only a hashed account key. */\n partition: string;\n /** Changes when navigation/process replacement invalidates snapshot refs. */\n generation: number;\n}\n\nexport interface BrowserState {\n enabled: boolean;\n pages: BrowserPageInfo[];\n activeByWorkflow: Record<string, string>;\n popupRequests: BrowserPopupRequest[];\n}\n\n/** One unique URL returned by the account-local omnibox history index. */\nexport interface BrowserHistorySuggestion {\n placeId: string;\n url: string;\n title: string;\n lastVisitedAt: number;\n visitCount: number;\n}\n\n/** One chronological visit shown in the browser history panel. */\nexport interface BrowserHistoryVisit {\n visitId: string;\n placeId: string;\n url: string;\n title: string;\n visitedAt: number;\n}\n\nexport interface BrowserHistorySuggestionsRequest {\n query?: string;\n limit?: number;\n}\n\nexport interface BrowserHistoryListRequest {\n offset?: number;\n limit?: number;\n}\n\nexport interface BrowserHistoryListResult {\n items: BrowserHistoryVisit[];\n total: number;\n}\n\n/** Remove one visit or every visit for one URL. Exactly one id is expected. */\nexport interface BrowserHistoryRemoveRequest {\n visitId?: string;\n placeId?: string;\n}\n\nexport interface BrowserConnectionEvent {\n phase: 'required' | 'connected' | 'timeout' | 'cancelled';\n pageId: string;\n workflowId: string;\n workflowName: string;\n}\n\nexport interface BrowserCreateRequest {\n workflowId: string;\n workflowName?: string;\n mode?: BrowserPageMode;\n url?: string;\n}\n\nexport interface BrowserNavigateRequest {\n pageId: string;\n action: 'goto' | 'back' | 'forward' | 'reload' | 'stop';\n url?: string;\n}\n\nexport type BrowserSelectionMode = 'element' | 'region';\n\nexport interface BrowserSelectionPoint {\n x: number;\n y: number;\n}\n\nexport interface BrowserSelectionRect extends BrowserSelectionPoint {\n width: number;\n height: number;\n}\n\nexport interface BrowserSelectionBeginRequest {\n pageId: string;\n generation: number;\n mode: BrowserSelectionMode;\n}\n\n/** One-shot, main-process-owned permission to inspect and capture one visible page. */\nexport interface BrowserSelectionSession {\n token: string;\n pageId: string;\n generation: number;\n mode: BrowserSelectionMode;\n expiresAt: number;\n}\n\nexport interface BrowserSelectionInspectRequest {\n token: string;\n point: BrowserSelectionPoint;\n}\n\nexport interface BrowserSelectionCompleteRequest {\n token: string;\n point?: BrowserSelectionPoint;\n rect?: BrowserSelectionRect;\n}\n\nexport interface BrowserSelectionPreview {\n tag: string;\n label: string;\n rect: BrowserSelectionRect;\n}\n\nexport interface BrowserElementContext {\n tag: string;\n role?: string;\n name?: string;\n text?: string;\n selector?: string;\n href?: string;\n /** A short allowlisted reconstruction, never the page's raw outerHTML. */\n html?: string;\n rect: BrowserSelectionRect;\n}\n\nexport interface BrowserSelectionResult {\n id: string;\n workflowId: string;\n pageId: string;\n generation: number;\n kind: BrowserSelectionMode;\n title: string;\n url: string;\n rect: BrowserSelectionRect;\n viewport: {\n width: number;\n height: number;\n scrollX: number;\n scrollY: number;\n };\n elements: BrowserElementContext[];\n image: {\n dataUrl: string;\n name: string;\n mime: 'image/png' | 'image/jpeg';\n size: number;\n width: number;\n height: number;\n };\n}\n\nexport type BrowserPopupPermission = 'allow' | 'block';\n\n/** Persisted popup rules keyed first by the account-hashed browser partition. */\nexport type BrowserPopupPermissions = Record<string, Record<string, BrowserPopupPermission>>;\n\nexport type BrowserPopupDecision = 'allow_always' | 'allow_session' | 'block';\n\n/**\n * A secret-free popup summary exposed to the renderer. The main process keeps\n * the complete target URL (including query/fragment) behind requestId.\n */\nexport interface BrowserPopupRequest {\n requestId: string;\n pageId: string;\n workflowId: string;\n openerOrigin: string;\n targetOrigin: string;\n targetDisplayUrl: string;\n createdAt: number;\n}\n\nexport interface BrowserPopupResolveRequest {\n requestId: string;\n decision: BrowserPopupDecision;\n}\n\n/** Exact http(s) origin used as the popup permission key. */\nexport function browserOrigin(raw: unknown): string | null {\n const normalized = normalizeBrowserUrl(raw);\n if (!normalized || normalized === 'about:blank') return null;\n try {\n return new URL(normalized).origin;\n } catch {\n return null;\n }\n}\n\nexport type BrowserErrorCode =\n | 'browser_disabled'\n | 'browser_no_page'\n | 'browser_page_not_found'\n | 'browser_stale_ref'\n | 'browser_timeout'\n | 'browser_denied';\n\nexport const BROWSER_CONTEXT_START = '<xgen_browser_context>';\nexport const BROWSER_CONTEXT_END = '</xgen_browser_context>';\n\n/** Only expose origin + pathname to chat. Query strings and fragments can hold secrets. */\nexport function sanitizedBrowserUrl(raw: string): string {\n try {\n const url = new URL(raw);\n if (url.protocol === 'about:') return 'about:blank';\n if (url.protocol !== 'http:' && url.protocol !== 'https:') return '';\n return `${url.origin}${url.pathname}`;\n } catch {\n return '';\n }\n}\n\n/** Remove the connector-only browser envelope before text is shown to a user. */\nexport function stripBrowserContext(text: string): string {\n if (typeof text !== 'string' || !text.startsWith(BROWSER_CONTEXT_START)) return text;\n const end = text.indexOf(BROWSER_CONTEXT_END);\n if (end < 0) return text;\n return text.slice(end + BROWSER_CONTEXT_END.length).replace(/^\\r?\\n/, '');\n}\n\n/** Add a machine-readable envelope only when this workflow owns live pages. */\nexport function prependBrowserContext(\n input: string,\n workflowId: string,\n state: BrowserState,\n selections: BrowserSelectionResult[] = [],\n): string {\n if (!state.enabled) return input;\n const pages = state.pages\n .filter((page) => page.workflowId === workflowId)\n .map((page) => ({\n workflow_id: page.workflowId,\n page_id: page.pageId,\n mode: page.mode,\n title: page.title,\n url: sanitizedBrowserUrl(page.url),\n }));\n const selected = selections\n .filter((selection) => selection.workflowId === workflowId)\n .map((selection) => ({\n id: selection.id,\n page_id: selection.pageId,\n generation: selection.generation,\n kind: selection.kind,\n title: selection.title,\n url: sanitizedBrowserUrl(selection.url),\n rect: selection.rect,\n viewport: selection.viewport,\n elements: selection.elements,\n image_name: selection.image.name,\n }));\n if (!pages.length && !selected.length) return input;\n const envelope = JSON.stringify({\n version: selected.length ? 2 : 1,\n workflow_id: workflowId,\n pages,\n ...(selected.length ? { selections: selected } : {}),\n });\n return `${BROWSER_CONTEXT_START}\\n${envelope}\\n${BROWSER_CONTEXT_END}\\n${input}`;\n}\n", "/**\n * Conversation history + past-conversation listing.\n *\n * - io-logs: the ordered turns of one conversation (workflowId + interactionId).\n * - interactions: the list of past conversations for a sidebar.\n */\nimport { HttpClient } from './client';\nimport type { Conversation, HistoryAttachment, HistoryTurn } from './types';\nimport { stripBrowserContext } from './browser';\n\ninterface RawIoLog {\n log_id: number;\n io_id: number;\n interaction_id: string;\n workflow_id: string;\n workflow_name: string;\n // \u26A0 \uC11C\uBC84\uB294 `result` \uB97C \uADF8\uB300\uB85C \uC2E3\uB294\uB2E4 \u2014 \uAD6C\uC870\uD654 \uCD9C\uB825(Schema Provider) \uD134\uC740\n // dict, \uBA40\uD2F0\uBAA8\uB2EC \uC785\uB825\uC740 [{type,text},{type,image_url}] \uBC30\uC5F4\uC774\uB77C **\uBB38\uC790\uC5F4\uC774\n // \uC544\uB2C8\uB2E4.** \uD0C0\uC785\uB9CC string \uC774\uB77C \uBBFF\uACE0 \uADF8\uB300\uB85C \uB80C\uB354\uD558\uBA74 React \uAC00\n // \"Objects are not valid as a React child\" \uB85C \uC8FD\uC5B4 \uD654\uBA74\uC774 \uD1B5\uC9F8\uB85C \uAC80\uAC8C \uB41C\uB2E4\n // (\uAE30\uC874 \uCC44\uD305 \uBD88\uB7EC\uC624\uAE30 \uD06C\uB798\uC2DC\uC758 \uADFC\uBCF8 \uC6D0\uC778). ``toDisplayText`` \uB85C \uD56D\uC0C1 \uBB38\uC790\uC5F4\uD654.\n input_data: unknown;\n output_data: unknown;\n attachments?: unknown;\n updated_at: string;\n}\n\n/** Keep the history boundary tolerant of both the current camelCase response\n * and older/raw DB-style keys. Invalid entries are ignored instead of making\n * the whole conversation impossible to reopen. */\nexport function toHistoryAttachments(value: unknown): HistoryAttachment[] {\n if (!Array.isArray(value)) return [];\n const result: HistoryAttachment[] = [];\n for (const item of value) {\n if (!item || typeof item !== 'object') continue;\n const raw = item as Record<string, unknown>;\n const path = String(raw.minioPath ?? raw.filePath ?? raw.object_name ?? raw.path ?? '').trim();\n if (!path) continue;\n const name = String(raw.name ?? raw.original_name ?? path.split('/').pop() ?? 'attachment');\n const contentType = String(raw.contentType ?? raw.content_type ?? 'application/octet-stream')\n .split(';', 1)[0]\n .trim()\n .toLowerCase();\n const type = raw.type === 'picture' || contentType.startsWith('image/') ? 'picture' : 'file';\n const numericSize = Number(raw.size ?? raw.file_size ?? 0);\n result.push({\n id: typeof raw.id === 'string' || typeof raw.id === 'number' ? raw.id : undefined,\n name,\n size: Number.isFinite(numericSize) && numericSize > 0 ? numericSize : 0,\n contentType,\n type,\n path,\n bucket: String(raw.bucket ?? ''),\n });\n }\n return result;\n}\n\n/** Resolve only the server-issued XGeny chat-workspace reference. The server\n * still performs the authoritative current-user path/access check. */\nexport function xgenyHistoryWorkspacePath(attachment: HistoryAttachment): string | null {\n const marker = 'geny-workspace:';\n const rawPath = String(attachment.path ?? '')\n .replace(/\\\\/g, '/')\n .trim();\n if (!rawPath.startsWith(marker) && attachment.bucket !== 'geny-workspace') return null;\n let path = rawPath.startsWith(marker) ? rawPath.slice(marker.length) : rawPath;\n path = path.replace(/^\\/+/, '');\n if (path.startsWith('workspace/')) path = path.slice('workspace/'.length);\n const parts = path.split('/');\n if (\n !path.startsWith('uploads/users/') ||\n parts.some((part) => !part || part === '.' || part === '..')\n ) {\n return null;\n }\n return path;\n}\n\n/** \uC11C\uBC84\uAC00 \uC900 turn \uAC12(\uBB38\uC790\uC5F4/\uBA40\uD2F0\uBAA8\uB2EC \uBC30\uC5F4/\uAD6C\uC870\uD654 dict)\uC744 **\uD45C\uC2DC\uC6A9 \uBB38\uC790\uC5F4**\uB85C.\n *\n * - \uBB38\uC790\uC5F4: \uADF8\uB300\uB85C.\n * - \uBA40\uD2F0\uBAA8\uB2EC content \uBC30\uC5F4: text \uBE14\uB85D\uB9CC \uC774\uC5B4 \uBD99\uC774\uACE0, \uC774\uBBF8\uC9C0 \uB4F1\uC740 \uD45C\uC2DD\uC73C\uB85C.\n * - \uADF8 \uC678(dict/number/\u2026): JSON \uC73C\uB85C. (\uB80C\uB354\uAC00 \uC808\uB300 non-string \uC744 \uBC1B\uC9C0 \uC54A\uAC8C.)\n */\nexport function toDisplayText(v: unknown): string {\n if (v == null) return '';\n if (typeof v === 'string') return stripBrowserContext(v);\n if (typeof v === 'number' || typeof v === 'boolean') return String(v);\n if (Array.isArray(v)) {\n const parts = v.map((b) => {\n if (b == null) return '';\n if (typeof b === 'string') return b;\n if (typeof b === 'object') {\n const o = b as Record<string, unknown>;\n if (typeof o.text === 'string') return o.text;\n const t = typeof o.type === 'string' ? o.type : '';\n if (t.includes('image')) return '[\uC774\uBBF8\uC9C0]';\n try {\n return JSON.stringify(b);\n } catch {\n return String(b);\n }\n }\n return String(b);\n });\n return stripBrowserContext(parts.filter(Boolean).join('\\n'));\n }\n try {\n return JSON.stringify(v, null, 2);\n } catch {\n return String(v);\n }\n}\n\ninterface RawInteraction {\n id: number;\n interaction_id: string;\n workflow_id: string;\n workflow_name: string;\n interaction_count?: number;\n metadata?: Record<string, unknown>;\n created_at?: string;\n updated_at?: string;\n}\n\nexport class HistoryApi {\n constructor(private http: HttpClient) {}\n\n /** Ordered turns of one conversation. */\n async turns(workflowId: string, interactionId: string, workflowName?: string): Promise<HistoryTurn[]> {\n const params = new URLSearchParams({ workflow_id: workflowId, interaction_id: interactionId });\n if (workflowName) params.set('workflow_name', workflowName);\n const res = await this.http.get<{ in_out_logs?: RawIoLog[] }>(`/api/chat/io-logs?${params}`);\n return (res.in_out_logs ?? []).map((r) => ({\n logId: r.log_id,\n ioId: r.io_id,\n interactionId: r.interaction_id,\n workflowId: r.workflow_id,\n workflowName: r.workflow_name,\n input: toDisplayText(r.input_data),\n output: toDisplayText(r.output_data),\n attachments: toHistoryAttachments(r.attachments),\n updatedAt: r.updated_at,\n }));\n }\n\n /** Past conversations (interactions) for the sidebar. */\n async conversations(): Promise<Conversation[]> {\n const res = await this.http.get<{ execution_meta_list?: RawInteraction[] }>('/api/interaction/list');\n return (res.execution_meta_list ?? []).map((r) => ({\n id: r.id,\n interactionId: r.interaction_id,\n workflowId: r.workflow_id,\n workflowName: r.workflow_name,\n interactionCount: r.interaction_count ?? 0,\n metadata: r.metadata ?? {},\n createdAt: r.created_at ?? '',\n updatedAt: r.updated_at ?? '',\n }));\n }\n}\n", "/**\n * User preferences \u2014 the per-user avatar config lives in xgen-core\n * `preferences.avatar` and is read via the profile endpoint. The connector shows\n * the user's DEFAULT avatar globally (not per-session), so this is the only\n * avatar source the overlay needs.\n */\nimport { HttpClient } from './client';\n\nexport type AvatarRuntime = 'live2d' | 'spine' | 'image';\n\nexport interface AvatarDescriptor {\n id: string;\n name: string;\n runtime: AvatarRuntime;\n source: string;\n modelUrl: string; // e.g. /api/storage/avatar/{user}/{id}/x.model3.json (relative)\n atlasUrl?: string;\n thumbnail?: string | null;\n scale?: number;\n position?: { x: number; y: number };\n idleMotionGroupName?: string;\n emotionMap?: Record<string, number>;\n emotionMotionMap?: Record<string, string>;\n tapMotions?: Record<string, Record<string, number>>;\n hiddenParts?: string[];\n}\n\nexport interface AvatarConfig {\n enabled: boolean;\n defaultAvatarId: string | null;\n avatars: AvatarDescriptor[];\n}\n\nexport const EMPTY_AVATAR_CONFIG: AvatarConfig = { enabled: false, defaultAvatarId: null, avatars: [] };\n\ninterface RawProfile {\n // preferences may arrive as a parsed object OR (defensively) a JSON string.\n user?: { preferences?: Record<string, unknown> | string | null } | null;\n}\n\nexport class PreferencesApi {\n constructor(private http: HttpClient) {}\n\n /** GET /api/admin/user \u2192 preferences.avatar.\n *\n * THROWS on failure (network / 401 / not-yet-authenticated) rather than\n * returning an empty config: at startup the overlay's fetch can beat the\n * main window's session restore, and masking that as `{enabled:false}` made\n * the avatar look permanently absent. Propagating lets the caller retry until\n * the client is authed. A genuinely empty config (feature off) still returns\n * normally. */\n async getAvatarConfig(): Promise<AvatarConfig> {\n const res = await this.http.get<RawProfile>('/api/admin/user');\n if (!res || !res.user) {\n // Unauthenticated pass-through / no profile yet \u2192 not a real answer.\n throw new Error('avatar config: no authenticated profile');\n }\n let prefs: unknown = res.user.preferences ?? {};\n if (typeof prefs === 'string') {\n try {\n prefs = JSON.parse(prefs);\n } catch {\n prefs = {};\n }\n }\n const raw = ((prefs as Record<string, unknown> | null)?.avatar ?? {}) as Partial<AvatarConfig>;\n return {\n enabled: !!raw.enabled,\n defaultAvatarId: typeof raw.defaultAvatarId === 'string' ? raw.defaultAvatarId : null,\n avatars: Array.isArray(raw.avatars) ? (raw.avatars as AvatarDescriptor[]) : [],\n };\n }\n\n /** Persist the whole avatar config (PUT shallow-merges preferences top-level,\n * so sending {avatar} replaces just that key). Used when the overlay adjusts\n * the avatar's scale/position in-place. */\n async saveAvatarConfig(config: AvatarConfig): Promise<void> {\n await this.http.put('/api/admin/user', { preferences: { avatar: config } });\n }\n\n /** Read-modify-write: \uC11C\uBC84\uC758 CURRENT config \uB97C \uC77D\uC5B4 \uCD5C\uC18C \uD328\uCE58\uB9CC \uC801\uC6A9\uD55C\uB2E4.\n * \uD654\uBA74\uC5D0 \uCE90\uC2DC\uB41C \uC2A4\uB0C5\uC0F7 \uC804\uCCB4\uB97C \uC800\uC7A5\uD558\uBA74 \uADF8 \uC0AC\uC774\uC758 \uBCC0\uACBD(\uC120\uD0DD \uB4F1)\uC744 \uC870\uC6A9\uD788\n * \uB418\uB3CC\uB9B0\uB2E4 \u2014 \uBAA8\uB4E0 \uBD80\uBD84 \uC218\uC815\uC740 \uBC18\uB4DC\uC2DC \uC774 \uACBD\uB85C\uB97C \uC4F4\uB2E4. */\n async mutateAvatarConfig(mutate: (cur: AvatarConfig) => AvatarConfig): Promise<AvatarConfig> {\n const cfg = await this.getAvatarConfig();\n const next = mutate(cfg);\n await this.saveAvatarConfig(next);\n return next;\n }\n\n /** Persist ONE avatar's scale/position (read-modify-write). */\n async saveAvatarTransform(\n avatarId: string,\n tf: { scale: number; position: { x: number; y: number } },\n ): Promise<void> {\n await this.mutateAvatarConfig((cfg) => ({\n ...cfg,\n avatars: cfg.avatars.map((a) =>\n a.id === avatarId ? { ...a, scale: tf.scale, position: tf.position } : a,\n ),\n }));\n }\n\n setAvatarEnabled(enabled: boolean): Promise<AvatarConfig> {\n return this.mutateAvatarConfig((c) => ({ ...c, enabled }));\n }\n\n selectAvatar(id: string): Promise<AvatarConfig> {\n return this.mutateAvatarConfig((c) => ({ ...c, defaultAvatarId: id }));\n }\n\n renameAvatar(id: string, name: string): Promise<AvatarConfig> {\n return this.mutateAvatarConfig((c) => ({\n ...c,\n avatars: c.avatars.map((a) => (a.id === id ? { ...a, name } : a)),\n }));\n }\n\n /** Add an uploaded/downloaded descriptor (optionally renamed); first avatar\n * becomes the selection. */\n addAvatar(descriptor: AvatarDescriptor, name?: string): Promise<AvatarConfig> {\n return this.mutateAvatarConfig((c) => ({\n ...c,\n avatars: [...c.avatars, { ...descriptor, name: (name ?? descriptor.name) || descriptor.name }],\n defaultAvatarId: c.defaultAvatarId ?? descriptor.id,\n }));\n }\n\n removeAvatar(id: string): Promise<AvatarConfig> {\n return this.mutateAvatarConfig((c) => {\n const remaining = c.avatars.filter((a) => a.id !== id);\n return {\n ...c,\n avatars: remaining,\n defaultAvatarId: c.defaultAvatarId === id ? (remaining[0]?.id ?? null) : c.defaultAvatarId,\n };\n });\n }\n}\n", "/**\n * Per-user SSH servers \u2014 the same store the web mypage [SSH \uC5F0\uB3D9 \uC124\uC815] writes to.\n *\n * There is no syncing code here, and that is the point: both surfaces call\n * `/api/agentflow/user-ssh`, so there is only ever one copy. Edit on either side\n * and the other sees it on its next read. A local mirror would have to be\n * reconciled, and every reconciliation strategy eventually resurrects a server\n * the user deleted somewhere else.\n *\n * Credentials never come back from the server \u2014 only `has_password` /\n * `has_private_key`. Writes are partial: a key you do not send keeps its stored\n * value, an empty string clears it. That is what lets the UI edit a description\n * without knowing (or destroying) the password.\n */\nimport { HttpClient } from './client';\n\nconst BASE = '/api/agentflow/user-ssh';\n\nexport interface SshServer {\n name: string;\n host: string;\n port: number;\n username: string;\n description: string;\n strict_host_key: boolean;\n /** Other server names to hop through, nearest first. */\n jump_via: string[];\n enabled: boolean;\n sort_order: number;\n has_password: boolean;\n has_private_key: boolean;\n has_passphrase: boolean;\n /** 'password' | 'key' | 'password+key' | 'none' \u2014 computed server-side. */\n auth: string;\n}\n\nexport interface SshServerInput {\n name?: string;\n host?: string;\n port?: number;\n username?: string;\n description?: string;\n strict_host_key?: boolean;\n jump_via?: string[];\n enabled?: boolean;\n sort_order?: number;\n /** Plaintext. Omit to keep, `''` to clear. Never echoed back. */\n password?: string;\n private_key?: string;\n passphrase?: string;\n}\n\nexport interface SshConfig {\n enabled: boolean;\n servers: SshServer[];\n limits: { max_servers: number; max_jump_depth: number };\n}\n\nexport interface SshTestResult {\n success: boolean;\n latency_ms?: number;\n error?: string;\n /** The dial order actually taken \u2014 the only clue to *which* hop broke. */\n hops?: string[];\n}\n\nexport const EMPTY_SSH_CONFIG: SshConfig = {\n enabled: false,\n servers: [],\n limits: { max_servers: 50, max_jump_depth: 8 },\n};\n\nexport class SshApi {\n constructor(private http: HttpClient) {}\n\n getConfig(): Promise<SshConfig> {\n return this.http.get<SshConfig>(`${BASE}/config`);\n }\n\n /** Master switch only \u2014 the server list survives being turned off. */\n setEnabled(enabled: boolean): Promise<SshConfig> {\n return this.http.put<SshConfig>(`${BASE}/config`, { enabled });\n }\n\n createServer(input: SshServerInput): Promise<SshServer> {\n return this.http.post<SshServer>(`${BASE}/servers`, input);\n }\n\n /** Partial update. Renaming also rewrites this server out of others' jump paths. */\n updateServer(name: string, input: SshServerInput): Promise<SshServer> {\n return this.http.put<SshServer>(`${BASE}/servers/${encodeURIComponent(name)}`, input);\n }\n\n /** Refused (400) while another server still lists it as a jump host. */\n deleteServer(name: string): Promise<SshConfig> {\n return this.http.del<SshConfig>(`${BASE}/servers/${encodeURIComponent(name)}`);\n }\n\n /**\n * Dial it for real, through the jump path.\n *\n * Works regardless of the master switch \u2014 you must be able to check a server\n * *before* turning the feature on, otherwise the only order available is\n * \"switch it on and hope\".\n *\n * The connection is opened by the XGEN server, not this machine: the agent\n * runs there, so that is the only reachability that matters.\n */\n testServer(name: string): Promise<SshTestResult> {\n return this.http.post<SshTestResult>(\n `${BASE}/servers/${encodeURIComponent(name)}/test`,\n {},\n // A three-hop chain can legitimately take a while; the default JSON\n // timeout would report a failure the server never saw.\n { timeoutMs: 70_000 },\n );\n }\n}\n", "/**\n * XGEN Teams \u2014 \uC0AC\uB78C \uC0AC\uC774\uC758 \uB300\uD654 (`/api/teams/*`).\n *\n * \uC11C\uBC84\uB294 xgen-workflow \uC758 teams \uBAA8\uB4C8\uC774\uACE0, \uAC8C\uC774\uD2B8\uC6E8\uC774 `services.yaml` \uC774\n * `teams` \uB97C workflow-service \uB85C \uB77C\uC6B0\uD305\uD55C\uB2E4 \u2014 \uCEE4\uB125\uD130\uAC00 \uC774\uBBF8 \uC4F0\uB294 base URL \uACFC\n * \uC778\uC99D(Bearer)\uC774 \uADF8\uB300\uB85C \uD1B5\uD55C\uB2E4. \uBCC4\uB3C4 \uC11C\uBC84 \uC791\uC5C5\uC740 \uD544\uC694 \uC5C6\uB2E4.\n *\n * \uC774 \uD30C\uC77C\uC758 \uC720\uC77C\uD55C \uCC45\uC784: \uC11C\uBC84\uC758 snake_case \uC751\uB2F5\uC744 \uCEE4\uB125\uD130 \uD0C0\uC785\uC73C\uB85C \uC62E\uAE30\uB294 \uAC83.\n * \uB80C\uB354\uB7EC\uB3C4 \uBA54\uC778 \uD504\uB85C\uC138\uC2A4\uB3C4 raw \uC751\uB2F5\uC744 \uBCF4\uC9C0 \uC54A\uB294\uB2E4 \u2014 \uC11C\uBC84 \uC2A4\uD0A4\uB9C8\uAC00 \uD754\uB4E4\uB824\uB3C4\n * \uACE0\uCE60 \uACF3\uC774 \uC5EC\uAE30 \uD558\uB098\uB85C \uC720\uC9C0\uB41C\uB2E4.\n *\n * \uC2E4\uC2DC\uAC04(WebSocket)\uC740 \uC5EC\uAE30 \uC5C6\uB2E4. \uBE0C\uB77C\uC6B0\uC800 WebSocket \uC740 \uD578\uB4DC\uC170\uC774\uD06C\uC5D0 \uD5E4\uB354\uB97C\n * \uC2E4\uC744 \uC218 \uC5C6\uC5B4 \uC778\uC99D\uC774 \uBD88\uAC00\uB2A5\uD558\uBBC0\uB85C, WS \uB294 \uBA54\uC778 \uD504\uB85C\uC138\uC2A4 \uC804\uC6A9\n * (`src/main/teams-ws.ts`)\uC774\uB2E4.\n */\nimport { HttpClient } from './client';\nimport type {\n TeamsAttachment,\n TeamsMember,\n TeamsMessage,\n TeamsReaction,\n TeamsRoom,\n TeamsRouterMode,\n TeamsSenderType,\n TeamsUser,\n} from './types';\n\n/** \uC11C\uBC84 \uACF5\uD1B5 \uC751\uB2F5 \uBD09\uD22C \u2014 \uBAA8\uB4E0 teams \uC5D4\uB4DC\uD3EC\uC778\uD2B8\uAC00 `{success, data}` \uB85C \uB3CC\uB824\uC900\uB2E4. */\ninterface Envelope<T> {\n success?: boolean;\n data?: T;\n}\n\ntype Raw = Record<string, unknown>;\n\nconst str = (v: unknown, fallback = ''): string =>\n typeof v === 'string' ? v : v === null || v === undefined ? fallback : String(v);\nconst num = (v: unknown, fallback = 0): number => {\n const n = Number(v);\n return Number.isFinite(n) ? n : fallback;\n};\n\nfunction normalizeRouterMode(v: unknown): TeamsRouterMode {\n const raw = str(v, 'hybrid');\n if (raw === 'chat' || raw === 'manual' || raw === 'hybrid' || raw === 'auto') return raw;\n return 'hybrid';\n}\n\nfunction normalizeSenderType(v: unknown): TeamsSenderType {\n const raw = str(v, 'user');\n if (raw === 'agent' || raw === 'router' || raw === 'system') return raw;\n return 'user';\n}\n\nexport function mapRoom(raw: unknown): TeamsRoom {\n const r = (raw ?? {}) as Raw;\n return {\n id: str(r.id),\n name: str(r.name, '\uC774\uB984 \uC5C6\uB294 \uB300\uD654'),\n description: str(r.description) || undefined,\n routerMode: normalizeRouterMode(r.router_mode),\n isDirect: Boolean(r.is_direct),\n createdAt: str(r.created_at),\n createdBy: num(r.created_by),\n lastMessageAt: str(r.last_message_at) || undefined,\n };\n}\n\nfunction mapMember(raw: unknown): TeamsMember {\n const m = (raw ?? {}) as Raw;\n const role = str(m.role, 'member');\n return {\n userId: num(m.user_id),\n username: str(m.username) || `User-${num(m.user_id)}`,\n fullName: str(m.full_name) || str(m.name) || undefined,\n role: role === 'owner' || role === 'admin' ? role : 'member',\n isOnline: Boolean(m.is_online),\n joinedAt: str(m.joined_at),\n };\n}\n\n/** \uBA64\uBC84 \uBAA9\uB85D\uC5D0\uC11C \uD604\uC7AC \uC0AC\uC6A9\uC790\uB97C \uC81C\uC678\uD55C 1:1 \uC0C1\uB300\uC758 \uD45C\uC2DC \uC774\uB984\uC744 \uCC3E\uB294\uB2E4. */\nexport function directRoomNameForViewer(\n room: TeamsRoom,\n members: TeamsMember[],\n viewerUserId: string,\n): string {\n if (!room.isDirect || !viewerUserId) return room.name;\n const other = members.find((member) => String(member.userId) !== viewerUserId);\n return other ? other.fullName || other.username || room.name : room.name;\n}\n\n/**\n * \uB9AC\uC561\uC158 \uC9D1\uACC4. \uC11C\uBC84\uB294 `[{emoji, count, user_ids, usernames}]` \uB85C \uB0B4\uB824\uC900\uB2E4.\n * \"\uB0B4\uAC00 \uB20C\uB800\uB294\uAC00\" \uB294 userIds \uB85C \uB80C\uB354\uB7EC\uAC00 \uD310\uC815\uD55C\uB2E4 \u2014 \uC11C\uBC84\uAC00 \uADF8 \uD544\uB4DC\uB97C \uC8FC\uC9C0 \uC54A\uACE0,\n * \uB85C\uADF8\uC778 \uC0AC\uC6A9\uC790 id \uB294 \uB80C\uB354\uB7EC\uAC00 \uC774\uBBF8 \uC54C\uACE0 \uC788\uB2E4.\n */\nfunction mapReactions(raw: unknown): TeamsReaction[] | undefined {\n if (!Array.isArray(raw) || raw.length === 0) return undefined;\n return raw.map((item) => {\n const r = (item ?? {}) as Raw;\n return {\n emoji: str(r.emoji),\n count: num(r.count),\n userIds: Array.isArray(r.user_ids) ? r.user_ids.map((x) => num(x)) : [],\n };\n });\n}\n\n/**\n * \uCCA8\uBD80 \uBA54\uD0C0. \uC800\uC7A5 \uD0A4(storage_key)\uAC00 \uB2E4\uC6B4\uB85C\uB4DC \uC8FC\uC18C\uC758 \uB9C8\uC9C0\uB9C9 \uC870\uAC01\uC774\uB2E4.\n *\n * `extracted_text` \uB294 **\uBC84\uB9AC\uC9C0 \uC54A\uB294\uB2E4**. \uC11C\uBC84\uB294 \uC5C5\uB85C\uB4DC \uB54C \uCD94\uCD9C\uD55C \uBCF8\uBB38\uC744 \uB3CC\uB824\uC8FC\uACE0,\n * \uBA54\uC2DC\uC9C0\uB97C \uBCF4\uB0BC \uB54C \uADF8 \uAC12\uC744 \uAC19\uC774 \uC2E4\uC5B4\uC57C\uB9CC \uC5D0\uC774\uC804\uD2B8\uAC00 \uCCA8\uBD80 \uB0B4\uC6A9\uC744 \uBCF8\uB2E4\n * (`message_controller` \uAC00 \uC6CC\uD06C\uD50C\uB85C\uC6B0 \uC785\uB825\uC5D0 prepend \uD55C\uB2E4). \uC5EC\uAE30\uC11C \uD758\uB9AC\uBA74\n * \uD30C\uC77C\uC740 \uBD99\uB294\uB370 \uB0B4\uC6A9\uC740 \uC0AC\uB77C\uC9C4\uB2E4.\n */\nfunction mapAttachment(raw: unknown): TeamsAttachment {\n const a = (raw ?? {}) as Raw;\n const extracted = str(a.extracted_text) || str(a.extractedText);\n return {\n id: str(a.id),\n filename:\n str(a.original_filename) || str(a.display_name) || str(a.name) || str(a.filename, 'file'),\n mime: str(a.mime, 'application/octet-stream'),\n size: num(a.size),\n storageKey: str(a.storage_key) || str(a.storageKey),\n extractedText: extracted || undefined,\n truncated: Boolean(a.truncated) || undefined,\n };\n}\n\n/**\n * \uCCA8\uBD80 \uBAA9\uB85D\uC740 JSONB \uCEEC\uB7FC\uC774\uB77C \uBC30\uC5F4\uB85C\uB3C4, JSON \uBB38\uC790\uC5F4\uB85C\uB3C4 \uB3C4\uCC29\uD55C\uB2E4 (\uB4DC\uB77C\uC774\uBC84/\uACBD\uB85C\uC5D0\n * \uB530\uB77C \uB2E4\uB984). \uC591\uCABD \uBAA8\uB450 \uBC1B\uC544 \uC900\uB2E4.\n *\n * **\uCD94\uCD9C \uBCF8\uBB38(extractedText)\uC740 \uC5EC\uAE30\uC11C \uBC84\uB9B0\uB2E4.** \uC11C\uBC84\uB294 \uBB38\uC11C\uC5D0\uC11C \uBF51\uC740 \uBCF8\uBB38\uC744\n * \uCCA8\uBD80 \uBA54\uD0C0\uC5D0 \uC2E4\uC5B4 \uBCF4\uB0B4\uB294\uB370(\uC0C1\uD55C 50\uB9CC \uC790), \uADF8\uAC74 **\uBCF4\uB0BC \uB54C** \uC11C\uBC84\uB85C \uB418\uB3CC\uB824\uC8FC\uAE30\n * \uC704\uD55C \uAC12\uC774\uC9C0 \uD654\uBA74\uC774 \uC4F0\uB294 \uAC12\uC774 \uC544\uB2C8\uB2E4. \uBC1B\uC740 \uBA54\uC2DC\uC9C0\uB9C8\uB2E4 \uB4E4\uACE0 \uC788\uC73C\uBA74 \uBB38\uC11C\uAC00 \uBA87 \uAC1C\uB9CC\n * \uBD99\uC5B4\uB3C4 \uBC29 \uD558\uB098\uAC00 \uC218 MB \uB97C \uBB3C\uACE0 \uC549\uC544 \uC788\uAC8C \uB418\uACE0, \uADF8 \uBC29\uC744 \uB2EB\uAE30 \uC804\uAE4C\uC9C0 \uB193\uC9C0 \uC54A\uB294\uB2E4.\n * \uC5C5\uB85C\uB4DC \uC751\uB2F5 \uACBD\uB85C(`uploadAttachment`)\uB294 `mapAttachment` \uB97C \uC9C1\uC811 \uC4F0\uBBC0\uB85C \uADF8\uCABD\uC740\n * \uADF8\uB300\uB85C \uC720\uC9C0\uB41C\uB2E4.\n */\nfunction mapAttachments(raw: unknown): TeamsAttachment[] | undefined {\n let arr: unknown[] = [];\n if (Array.isArray(raw)) arr = raw;\n else if (typeof raw === 'string' && raw.trim().startsWith('[')) {\n try {\n const parsed: unknown = JSON.parse(raw);\n if (Array.isArray(parsed)) arr = parsed;\n } catch {\n arr = [];\n }\n }\n if (arr.length === 0) return undefined;\n return arr.map((item) => {\n const { extractedText: _drop, ...meta } = mapAttachment(item);\n return meta;\n });\n}\n\n/**\n * \uBC1C\uC2E0\uC790 \uD45C\uC2DC \uC774\uB984. \uC11C\uBC84\uAC00 \uBE48 \uBB38\uC790\uC5F4\uC774\uB098 \uB204\uB77D\uC744 \uBCF4\uB0B4\uB294 \uACBD\uC6B0\uAC00 \uC788\uC5B4\n * (message_controller \uC8FC\uC11D\uC758 \uC2E4\uAE30 \uC0AC\uB840) \uD0C0\uC785\uBCC4 \uAE30\uBCF8\uAC12\uC73C\uB85C \uBCF4\uC815\uD55C\uB2E4.\n */\nfunction senderName(raw: unknown, type: TeamsSenderType, senderId: string): string {\n const name = str(raw).trim();\n if (name) return name;\n if (type === 'system') return '\uC2DC\uC2A4\uD15C';\n if (type === 'agent') return 'Agent';\n return senderId ? `User-${senderId}` : '\uC54C \uC218 \uC5C6\uC74C';\n}\n\nexport function mapMessage(raw: unknown): TeamsMessage {\n const m = (raw ?? {}) as Raw;\n const type = normalizeSenderType(m.sender_type);\n const senderId = str(m.sender_id);\n return {\n id: str(m.id),\n roomId: str(m.room_id),\n senderType: type,\n senderId,\n senderName: senderName(m.sender_name, type, senderId),\n content: str(m.content),\n createdAt: str(m.created_at),\n reactions: mapReactions(m.reactions),\n attachments: mapAttachments(m.attachments),\n replyToId: str(m.reply_to_id) || undefined,\n replyToSenderName: str(m.reply_to_sender_name) || undefined,\n replyToContent: str(m.reply_to_content) || undefined,\n isEdited: Boolean(m.is_edited),\n editedAt: str(m.edited_at) || undefined,\n };\n}\n\n/** WS \uD504\uB808\uC784/REST \uC751\uB2F5 \uC5B4\uB514\uC11C \uC628 \uBA54\uC2DC\uC9C0\uB4E0 \uAC19\uC740 \uD568\uC218\uB85C \uD1B5\uACFC\uC2DC\uD0A8\uB2E4. \uC2E4\uD328\uB294 null. */\nexport function safeMapMessage(raw: unknown): TeamsMessage | null {\n if (!raw || typeof raw !== 'object') return null;\n try {\n const mapped = mapMessage(raw);\n return mapped.id ? mapped : null;\n } catch {\n return null;\n }\n}\n\nexport { mapReactions as mapTeamsReactions };\n\n/**\n * \uCCA8\uBD80\uB85C \uC62C\uB9B4 \uC218 \uC788\uB294 \uD655\uC7A5\uC790 \u2014 \uC11C\uBC84 `attachment_controller.ALLOWED_EXTENSIONS`\n * \uC640 **\uBC18\uB4DC\uC2DC \uAC19\uC544\uC57C \uD55C\uB2E4**. \uB2E4\uB974\uBA74 \uC0AC\uC6A9\uC790\uAC00 \uACE0\uB97C \uC218\uB294 \uC788\uB294\uB370 \uC11C\uBC84\uAC00 415 \uB85C\n * \uAC70\uC808\uD558\uB294, \uC774\uC720\uB97C \uC54C \uC218 \uC5C6\uB294 \uC2E4\uD328\uAC00 \uB41C\uB2E4.\n */\nexport const TEAMS_ATTACHMENT_EXTENSIONS = [\n // \uBB38\uC11C\n 'pdf',\n 'doc',\n 'docx',\n 'hwp',\n 'hwpx',\n 'ppt',\n 'pptx',\n 'xls',\n 'xlsx',\n 'rtf',\n // \uD14D\uC2A4\uD2B8/\uB370\uC774\uD130\n 'txt',\n 'md',\n 'csv',\n 'tsv',\n 'json',\n // \uC774\uBBF8\uC9C0 (OCR \uC5C6\uC774 \uBA54\uD0C0\uB9CC \uBD99\uB294\uB2E4)\n 'jpg',\n 'jpeg',\n 'png',\n 'gif',\n 'webp',\n 'bmp',\n 'tiff',\n 'svg',\n] as const;\n\n/** \uC11C\uBC84 \uC5C5\uB85C\uB4DC \uC0C1\uD55C. \uB118\uC73C\uBA74 413 \uC774\uBBC0\uB85C \uC62C\uB9AC\uAE30 \uC804\uC5D0 \uC5EC\uAE30\uC11C \uB9C9\uB294\uB2E4. */\nexport const TEAMS_ATTACHMENT_MAX_BYTES = 50 * 1024 * 1024;\n\n/**\n * \uC62C\uB9B4 \uC218 \uC5C6\uB294 \uD30C\uC77C\uC774\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uBCF4\uC5EC\uC904 \uC774\uC720\uB97C, \uC62C\uB9B4 \uC218 \uC788\uC73C\uBA74 null.\n * \uC11C\uBC84\uC5D0 \uB358\uC838 \uBCF4\uACE0 \uC2E4\uD328\uB97C \uBC88\uC5ED\uD558\uB294 \uB300\uC2E0 \uBBF8\uB9AC \uD310\uC815\uD55C\uB2E4 \u2014 50MB \uB97C \uB2E4 \uC62C\uB9B0 \uB4A4\uC5D0\n * \uAC70\uC808\uB2F9\uD558\uB294 \uAC83\uC774 \uAC00\uC7A5 \uB098\uC05C \uACBD\uD5D8\uC774\uB2E4.\n */\nexport function teamsAttachmentRejectReason(filename: string, size: number): string | null {\n const dot = filename.lastIndexOf('.');\n const ext = dot >= 0 ? filename.slice(dot + 1).toLowerCase() : '';\n if (!ext || !(TEAMS_ATTACHMENT_EXTENSIONS as readonly string[]).includes(ext)) {\n return `${filename}: \uC62C\uB9B4 \uC218 \uC5C6\uB294 \uD615\uC2DD\uC785\uB2C8\uB2E4 (${ext ? `.${ext}` : '\uD655\uC7A5\uC790 \uC5C6\uC74C'}).`;\n }\n if (size > TEAMS_ATTACHMENT_MAX_BYTES) {\n return `${filename}: \uD30C\uC77C\uC774 \uB108\uBB34 \uD07D\uB2C8\uB2E4 (\uCD5C\uB300 ${Math.floor(\n TEAMS_ATTACHMENT_MAX_BYTES / 1024 / 1024,\n )}MB).`;\n }\n return null;\n}\n\nexport interface CreateRoomOptions {\n name: string;\n description?: string;\n /**\n * \uAE30\uBCF8\uAC12 'chat' \u2014 \uC0AC\uB78C\uB07C\uB9AC\uB9CC \uB300\uD654. \uCEE4\uB125\uD130\uC758 1\uCC28 \uBAA9\uD45C\uAC00 \uADF8\uAC83\uC774\uACE0, \uC5D0\uC774\uC804\uD2B8\uB97C\n * \uBD99\uC77C \uB54C\uB294 \uB098\uC911\uC5D0 \uBC29 \uC124\uC815\uC5D0\uC11C \uBAA8\uB4DC\uB97C \uC62C\uB9AC\uBA74 \uB41C\uB2E4 (\uC11C\uBC84\uAC00 \uC774\uBBF8 \uC9C0\uC6D0).\n */\n routerMode?: TeamsRouterMode;\n}\n\nexport class TeamsApi {\n constructor(private http: HttpClient) {}\n\n // \u2500\u2500 \uBC29 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /** \uB0B4\uAC00 \uC18D\uD55C \uBC29 \uC804\uCCB4. \uCD5C\uADFC \uBA54\uC2DC\uC9C0 \uC21C \uC815\uB82C\uC740 \uD638\uCD9C\uC790(\uB80C\uB354\uB7EC store)\uAC00 \uD55C\uB2E4. */\n async listRooms(viewerUserId?: string): Promise<TeamsRoom[]> {\n const res = await this.http.get<Envelope<unknown[]>>('/api/teams/rooms/list');\n const rooms = (res.data ?? []).map(mapRoom);\n if (!viewerUserId) return rooms;\n\n // \uC11C\uBC84\uC758 DM room.name \uC740 \uBC29\uC744 \uB9CC\uB4E0 \uC0AC\uB78C\uC774 \uB118\uAE34 target_name \uC774\uB77C \uBAA8\uB4E0 \uCC38\uAC00\uC790\uC5D0\uAC8C\n // \uAC19\uC740 \uAC12\uC73C\uB85C \uBCF4\uC778\uB2E4. \uAC01 1:1 \uBC29\uC758 \uBA64\uBC84\uB97C \uAE30\uC900\uC73C\uB85C **\uB098\uB97C \uC81C\uC678\uD55C \uC0AC\uB78C**\uC758 \uC774\uB984\uC744\n // \uACC4\uC0B0\uD574\uC57C A \uD654\uBA74\uC5D0\uB294 B, B \uD654\uBA74\uC5D0\uB294 A \uB85C \uBCF4\uC778\uB2E4. \uD55C \uBC29 \uC870\uD68C\uAC00 \uC2E4\uD328\uD574\uB3C4 \uC804\uCCB4\n // \uBAA9\uB85D\uC744 \uBC84\uB9AC\uC9C0 \uC54A\uACE0 \uC11C\uBC84 \uC774\uB984\uC73C\uB85C \uBB3C\uB7EC\uB09C\uB2E4.\n return Promise.all(\n rooms.map(async (room) => {\n if (!room.isDirect) return room;\n try {\n const members = await this.listMembers(room.id);\n return { ...room, name: directRoomNameForViewer(room, members, viewerUserId) };\n } catch {\n return room;\n }\n }),\n );\n }\n\n async getRoom(roomId: string): Promise<TeamsRoom | null> {\n const res = await this.http.get<Envelope<unknown>>(\n `/api/teams/rooms/${encodeURIComponent(roomId)}`,\n );\n return res.data ? mapRoom(res.data) : null;\n }\n\n async createRoom(opts: CreateRoomOptions): Promise<TeamsRoom> {\n const res = await this.http.post<Envelope<unknown>>('/api/teams/rooms/create', {\n name: opts.name,\n description: opts.description ?? null,\n router_mode: opts.routerMode ?? 'chat',\n });\n return mapRoom(res.data);\n }\n\n /**\n * 1:1 \uB300\uD654 \u2014 \uC774\uBBF8 \uC788\uC73C\uBA74 \uADF8 \uBC29\uC744, \uC5C6\uC73C\uBA74 \uC0C8\uB85C \uB9CC\uB4E4\uC5B4 \uB3CC\uB824\uC900\uB2E4.\n * \uC11C\uBC84\uAC00 `dm:u{min}:u{max}` \uD0A4\uB85C \uC911\uBCF5\uC744 \uB9C9\uC73C\uBBC0\uB85C \uD074\uB77C\uC774\uC5B8\uD2B8\uAC00 \uCC3E\uC744 \uD544\uC694\uAC00 \uC5C6\uB2E4.\n */\n async openDirectMessage(userId: number, username?: string): Promise<TeamsRoom> {\n const res = await this.http.post<Envelope<{ room?: unknown; room_id?: string }>>(\n '/api/teams/rooms/dm/lookup-or-create',\n {\n target_type: 'user',\n target_id: String(userId),\n target_name: username ?? null,\n target_description: null,\n target_color: null,\n },\n );\n return mapRoom(res.data?.room);\n }\n\n /**\n * \uBC29 \uC815\uBCF4 \uC218\uC815 (\uC774\uB984\u00B7\uC124\uBA85). \uC11C\uBC84\uB294 **\uBA64\uBC84 \uC804\uC6D0**\uC5D0\uAC8C \uD5C8\uC6A9\uD55C\uB2E4(\uBC29\uC7A5 \uC804\uC6A9\uC774 \uC544\uB2C8\uB2E4).\n *\n * \u26A0 \uC11C\uBC84\uAC00 \uC774 \uBCC0\uACBD\uC744 broadcast \uD558\uC9C0 \uC54A\uB294\uB2E4 \u2014 \uB2E4\uB978 \uD074\uB77C\uC774\uC5B8\uD2B8\uB294 \uC0C8\uB85C\uACE0\uCE68 \uC804\uAE4C\uC9C0\n * \uC61B \uC774\uB984\uC744 \uBCF8\uB2E4. \uC6B0\uB9AC \uD654\uBA74\uB9CC \uC989\uC2DC \uAC31\uC2E0\uD560 \uC218 \uC788\uB2E4.\n */\n async updateRoom(\n roomId: string,\n patch: { name?: string; description?: string | null },\n ): Promise<TeamsRoom | null> {\n const res = await this.http.put<Envelope<unknown>>(\n `/api/teams/rooms/${encodeURIComponent(roomId)}`,\n {\n // \uC11C\uBC84\uB294 null \uC744 \"\uBCC0\uACBD \uC5C6\uC74C\" \uC73C\uB85C \uC77D\uB294\uB2E4. \uBCF4\uB0B4\uC9C0 \uC54A\uC744 \uAC12\uC740 \uB123\uC9C0 \uC54A\uB294\uB2E4.\n ...(patch.name !== undefined ? { name: patch.name } : {}),\n ...(patch.description !== undefined ? { description: patch.description } : {}),\n },\n );\n return res.data ? mapRoom(res.data) : null;\n }\n\n /** \uB9C8\uC9C0\uB9C9 \uBA64\uBC84\uC758 \uB098\uAC00\uAE30\uB97C \uBE48 \uBC29 \uC815\uB9AC\uB85C \uBC14\uAFC0 \uB54C\uB9CC \uC4F0\uB294 \uB0B4\uBD80 \uACBD\uB85C. */\n private async deleteRoom(roomId: string): Promise<void> {\n await this.http.del(`/api/teams/rooms/${encodeURIComponent(roomId)}`);\n }\n\n /**\n * \uC0AC\uC6A9\uC790\uAC00 \uBCF4\uB294 \uBC29 \uC885\uB8CC \uB3D9\uC791\uC740 \uD56D\uC0C1 \"\uB098\uAC00\uAE30\" \uD558\uB098\uB2E4. \uB9C8\uC9C0\uB9C9 \uBA64\uBC84\uB77C\uBA74 \uBE48 \uBC29\uC744\n * \uB0A8\uAE30\uC9C0 \uC54A\uB3C4\uB85D \uB0B4\uBD80\uC801\uC73C\uB85C \uBC29\uC744 \uC815\uB9AC\uD55C\uB2E4. \uBA64\uBC84 \uC870\uD68C\uB098 \uC815\uB9AC \uAD8C\uD55C\uC774 \uC5C6\uB294 \uAD6C\uBC84\uC804\n * \uC11C\uBC84\uC5D0\uC11C\uB294 \uAE30\uC874 leave API \uB85C \uD3F4\uBC31\uD574 \uC0AC\uC6A9\uC790\uAC00 \uBC29\uC5D0 \uAC07\uD788\uC9C0 \uC54A\uAC8C \uD55C\uB2E4.\n */\n async leaveRoom(roomId: string): Promise<void> {\n let lastMember = false;\n try {\n lastMember = (await this.listMembers(roomId)).length <= 1;\n } catch {\n // \uBA64\uBC84 \uC870\uD68C\uB97C \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uC11C\uBC84\uC5D0\uC11C\uB3C4 \uB098\uAC00\uAE30\uB294 \uACC4\uC18D \uAC00\uB2A5\uD574\uC57C \uD55C\uB2E4.\n }\n\n if (lastMember) {\n try {\n await this.deleteRoom(roomId);\n return;\n } catch {\n // \uBC29\uC7A5 \uAD8C\uD55C/\uC11C\uBC84 \uBC84\uC804 \uCC28\uC774\uB85C \uC790\uB3D9 \uC815\uB9AC\uAC00 \uC548 \uB418\uBA74 \uC77C\uBC18 \uB098\uAC00\uAE30\uB85C \uD3F4\uBC31\uD55C\uB2E4.\n }\n }\n await this.http.post(`/api/teams/rooms/${encodeURIComponent(roomId)}/leave`);\n }\n\n // \u2500\u2500 \uBA64\uBC84 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async listMembers(roomId: string): Promise<TeamsMember[]> {\n const res = await this.http.get<Envelope<unknown[]>>(\n `/api/teams/rooms/${encodeURIComponent(roomId)}/members`,\n );\n return (res.data ?? []).map(mapMember);\n }\n\n async addMember(roomId: string, userId: number): Promise<void> {\n await this.http.post(`/api/teams/rooms/${encodeURIComponent(roomId)}/members`, {\n user_id: userId,\n role: 'member',\n force_override: false,\n });\n }\n\n /** \uCD08\uB300 \uB300\uC0C1 \uAC80\uC0C9. \uBE48 \uC9C8\uC758\uB294 \uC11C\uBC84\uB97C \uBD80\uB974\uC9C0 \uC54A\uB294\uB2E4. */\n async searchUsers(query: string, limit = 20): Promise<TeamsUser[]> {\n const q = query.trim();\n if (!q) return [];\n const params = new URLSearchParams({ q, limit: String(limit) });\n const res = await this.http.get<Envelope<unknown[]>>(`/api/teams/users/search?${params}`);\n return (res.data ?? []).map((item) => {\n const u = (item ?? {}) as Raw;\n const id = num(u.id);\n return {\n id,\n username: str(u.username) || str(u.user_name) || `user_${id}`,\n fullName: str(u.full_name) || str(u.name) || undefined,\n email: str(u.email) || undefined,\n };\n });\n }\n\n // \u2500\u2500 \uBA54\uC2DC\uC9C0 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * \uBA54\uC2DC\uC9C0 \uC870\uD68C (\uCEE4\uC11C \uD398\uC774\uC9C0\uB124\uC774\uC158). `before` \uB294 \uB354 \uACFC\uAC70\uB97C \uBD80\uB974\uB294 \uCEE4\uC11C\uC774\uACE0,\n * \uC11C\uBC84\uB294 **\uCD5C\uC2E0\uC21C**\uC73C\uB85C \uB3CC\uB824\uC8FC\uBBC0\uB85C \uC2DC\uAC04 \uC624\uB984\uCC28\uC21C \uC815\uB82C\uC740 \uD638\uCD9C\uC790\uAC00 \uD55C\uB2E4\n * (\uB80C\uB354\uB7EC store \uC758 mergeMessages \uAC00 \uB2F4\uB2F9).\n */\n async listMessages(\n roomId: string,\n opts?: { limit?: number; before?: string },\n ): Promise<TeamsMessage[]> {\n const params = new URLSearchParams({ limit: String(opts?.limit ?? 50) });\n if (opts?.before) params.set('before', opts.before);\n const res = await this.http.get<Envelope<unknown[]>>(\n `/api/teams/rooms/${encodeURIComponent(roomId)}/messages?${params}`,\n );\n return (res.data ?? []).flatMap((raw) => {\n const mapped = safeMapMessage(raw);\n return mapped ? [mapped] : [];\n });\n }\n\n /**\n * \uBA54\uC2DC\uC9C0 \uC804\uC1A1. \uC751\uB2F5\uC758 `data.message` \uAC00 \uC11C\uBC84\uAC00 \uD655\uC815\uD55C \uBA54\uC2DC\uC9C0\uB2E4 \u2014\n * \uB099\uAD00\uC801\uC73C\uB85C \uADF8\uB824 \uB454 \uC784\uC2DC \uBA54\uC2DC\uC9C0\uB97C \uC774\uAC83\uC73C\uB85C \uAD50\uCCB4\uD55C\uB2E4.\n *\n * \uB77C\uC6B0\uD305 \uACB0\uACFC(`data.routing`)\uB294 \uC5D0\uC774\uC804\uD2B8 \uC2E4\uD589\uC6A9\uC774\uB77C 1\uCC28 \uBC94\uC704\uC5D0\uC11C\uB294 \uBC84\uB9B0\uB2E4.\n * router_mode='chat' \uBC29\uC5D0\uC11C\uB294 \uC11C\uBC84\uAC00 \uC560\uCD08\uC5D0 \uC5D0\uC774\uC804\uD2B8\uB97C \uBD80\uB974\uC9C0 \uC54A\uB294\uB2E4.\n */\n async sendMessage(\n roomId: string,\n content: string,\n opts?: { replyToId?: string; attachments?: TeamsAttachment[] },\n ): Promise<TeamsMessage> {\n const res = await this.http.post<Envelope<{ message?: unknown }>>(\n `/api/teams/rooms/${encodeURIComponent(roomId)}/messages`,\n {\n content,\n mentioned_agent_ids: null,\n attachments: opts?.attachments?.length\n ? opts.attachments.map((a) => ({\n id: a.id,\n filename: a.filename,\n mime: a.mime,\n size: a.size,\n storage_key: a.storageKey,\n // \uC5C5\uB85C\uB4DC \uC751\uB2F5\uC744 \uADF8\uB300\uB85C \uB418\uB3CC\uB824\uC900\uB2E4 \u2014 null \uB85C \uB36E\uC73C\uBA74 \uCCA8\uBD80 \uB0B4\uC6A9\uC774 \uC0AC\uB77C\uC9C4\uB2E4.\n extracted_text: a.extractedText ?? null,\n truncated: a.truncated ?? false,\n }))\n : null,\n discussion_max_rounds: null,\n reply_to_id: opts?.replyToId ?? null,\n },\n );\n const mapped = safeMapMessage(res.data?.message);\n if (!mapped) throw new Error('\uBA54\uC2DC\uC9C0\uB97C \uBCF4\uB0C8\uC9C0\uB9CC \uC11C\uBC84 \uC751\uB2F5\uC744 \uD574\uC11D\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.');\n return mapped;\n }\n\n /** \uBCF8\uC778 \uBA54\uC2DC\uC9C0 \uD3B8\uC9D1. \uC11C\uBC84\uAC00 `message_updated` \uB97C broadcast \uD55C\uB2E4. */\n async editMessage(\n roomId: string,\n messageId: string,\n content: string,\n ): Promise<TeamsMessage | null> {\n const res = await this.http.patch<Envelope<unknown>>(\n `/api/teams/rooms/${encodeURIComponent(roomId)}/messages/${encodeURIComponent(messageId)}`,\n { content },\n );\n return safeMapMessage(res.data);\n }\n\n // \u2500\u2500 \uCCA8\uBD80 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n /**\n * \uCCA8\uBD80 \uC5C5\uB85C\uB4DC \u2192 \uBA54\uD0C0. \uC774 \uBA54\uD0C0\uB97C `sendMessage` \uC758 `attachments` \uB85C \uB118\uACA8\uC57C\n * \uC2E4\uC81C\uB85C \uBA54\uC2DC\uC9C0\uC5D0 \uBD99\uB294\uB2E4 (\uC5C5\uB85C\uB4DC\uB9CC\uC73C\uB85C\uB294 \uBC29\uC5D0 \uB098\uD0C0\uB098\uC9C0 \uC54A\uB294\uB2E4).\n *\n * \uC11C\uBC84\uAC00 \uAC19\uC740 \uC694\uCCAD \uC548\uC5D0\uC11C \uBB38\uC11C \uBCF8\uBB38\uAE4C\uC9C0 \uCD94\uCD9C\uD574 `extracted_text` \uB85C \uB3CC\uB824\uC8FC\uBBC0\uB85C\n * \uC751\uB2F5\uC744 \uD1B5\uC9F8\uB85C \uB4E4\uACE0 \uB2E4\uB2CC\uB2E4 \u2014 \uADF8\uB798\uC57C \uB098\uC911\uC5D0 \uC5D0\uC774\uC804\uD2B8\uAC00 \uADF8 \uD30C\uC77C\uC758 \uB0B4\uC6A9\uC744 \uBCF8\uB2E4.\n * \uC0C1\uD55C\uC740 \uC11C\uBC84 \uAE30\uC900 50MB, \uD5C8\uC6A9 \uD655\uC7A5\uC790\uB294 `attachment_controller.ALLOWED_EXTENSIONS`.\n */\n async uploadAttachment(\n roomId: string,\n bytes: Uint8Array,\n filename: string,\n mime?: string,\n ): Promise<TeamsAttachment> {\n const form = new FormData();\n // Uint8Array \uAC00 \uB354 \uD070 \uBC84\uD37C \uC704\uC758 \uBDF0\uC77C \uC218 \uC788\uB2E4(IPC \uB97C \uAC74\uB108\uC628 \uACBD\uC6B0) \u2014 \uC815\uD655\uD788 \uC774\n // \uD30C\uC77C\uC758 \uBC14\uC774\uD2B8\uB9CC \uB2F4\uAE30\uB3C4\uB85D \uB3C5\uB9BD ArrayBuffer \uB85C \uBCF5\uC0AC\uD55C\uB2E4 (\uC544\uBC14\uD0C0 \uC5C5\uB85C\uB4DC\uC640 \uAC19\uC740 \uC774\uC720).\n const buf = bytes.buffer.slice(\n bytes.byteOffset,\n bytes.byteOffset + bytes.byteLength,\n ) as ArrayBuffer;\n form.append('file', new Blob([buf], mime ? { type: mime } : undefined), filename);\n const res = await this.http.upload<Envelope<unknown>>(\n `/api/teams/rooms/${encodeURIComponent(roomId)}/attachments/upload`,\n form,\n // 50MB \uAE4C\uC9C0 \uBC1B\uB294 \uC5D4\uB4DC\uD3EC\uC778\uD2B8\uB77C \uAE30\uBCF8 \uD0C0\uC784\uC544\uC6C3\uC73C\uB85C\uB294 \uD070 \uD30C\uC77C\uC774 \uB04A\uAE34\uB2E4.\n { timeoutMs: 300_000 },\n );\n const mapped = mapAttachment(res.data);\n if (!mapped.storageKey) throw new Error('\uCCA8\uBD80\uB97C \uC62C\uB838\uC9C0\uB9CC \uC11C\uBC84 \uC751\uB2F5\uC744 \uD574\uC11D\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4.');\n // \uC11C\uBC84\uB294 \uC800\uC7A5 \uC774\uB984(storage_key)\uB9CC \uD655\uC815\uD558\uACE0 \uD45C\uC2DC \uC774\uB984\uC740 \uC694\uCCAD\uC758 \uAC83\uC744 \uADF8\uB300\uB85C\n // \uB418\uB3CC\uB824\uC900\uB2E4. \uC751\uB2F5\uC5D0\uC11C \uBABB \uC77D\uC5C8\uB2E4\uBA74 \uC6B0\uB9AC\uAC00 \uBCF4\uB0B8 \uC774\uB984\uC774 \uC815\uB2F5\uC774\uB2E4.\n return { ...mapped, filename: mapped.filename || filename };\n }\n\n /**\n * \uCCA8\uBD80 \uC6D0\uBCF8 \uBC14\uC774\uD2B8. \uB2E4\uC6B4\uB85C\uB4DC \uC8FC\uC18C\uC5D0 `filename` \uC744 \uD568\uAED8 \uB118\uACA8\uC57C \uC11C\uBC84\uAC00\n * Content-Disposition \uC5D0 \uC2E4\uC81C \uC774\uB984\uC744 \uC2E4\uC5B4 \uC900\uB2E4 (\uC548 \uB118\uAE30\uBA74 `att-xxx.docx` \uB85C \uB5A8\uC5B4\uC9C4\uB2E4).\n */\n async downloadAttachment(\n roomId: string,\n attachment: Pick<TeamsAttachment, 'storageKey' | 'filename'>,\n ): Promise<Uint8Array> {\n const params = attachment.filename\n ? `?filename=${encodeURIComponent(attachment.filename)}`\n : '';\n const { bytes } = await this.http.getBinary(\n `/api/teams/rooms/${encodeURIComponent(roomId)}/attachments/${encodeURIComponent(\n attachment.storageKey,\n )}${params}`,\n );\n return bytes;\n }\n\n /**\n * \uC774\uBAA8\uC9C0 \uB9AC\uC561\uC158 \uD1A0\uAE00. \uC11C\uBC84\uAC00 \uC9D1\uACC4 \uC804\uCCB4\uB97C \uB3CC\uB824\uC8FC\uACE0 \uB3D9\uC2DC\uC5D0 `reaction_update` \uB97C\n * broadcast \uD558\uBBC0\uB85C, \uBC18\uD658\uAC12\uC740 \uC989\uC2DC \uBC18\uC601\uC6A9 \uBCF4\uC870\uB2E4.\n */\n async toggleReaction(roomId: string, messageId: string, emoji: string): Promise<TeamsReaction[]> {\n const res = await this.http.post<Envelope<{ reactions?: unknown }>>(\n `/api/teams/rooms/${encodeURIComponent(roomId)}/messages/${encodeURIComponent(messageId)}/reactions`,\n { emoji },\n );\n return mapReactions(res.data?.reactions) ?? [];\n }\n}\n", "/**\n * Voice \u2014 STT (speech\u2192text) and TTS (text\u2192speech) over the XGEN backend proxy.\n *\n * The connector NEVER sees the audio provider's base_url/api_key: it sends only\n * text/audio + its Bearer token, and the backend resolves the secrets and calls\n * the real STT/TTS endpoint server-side. This mirrors PreferencesApi/AvatarsApi\n * (same `http` core, same read-modify pattern for the profile endpoint).\n *\n * Endpoints (xgen-core):\n * - POST /api/audio/stt/transcribe multipart(file, language?) \u2192 { text }\n * - POST /api/audio/tts/speak JSON {text, voice_id?, \u2026} \u2192 audio bytes\n * - GET /api/admin/user \u2192 preferences.stt / preferences.tts (hints)\n */\nimport { HttpClient } from './client';\nimport type { VoiceConfig, SttPref, TtsPref, TtsSpeakOptions } from './types';\n\ninterface RawProfile {\n // preferences may arrive as a parsed object OR (defensively) a JSON string.\n user?: { preferences?: Record<string, unknown> | string | null } | null;\n}\n\n/** Pick a filename (backend infers the format from the extension/mime). */\nfunction filenameFor(mime: string): string {\n const m = (mime || '').toLowerCase();\n if (m.includes('webm')) return 'audio.webm';\n if (m.includes('ogg')) return 'audio.ogg';\n if (m.includes('wav')) return 'audio.wav';\n if (m.includes('mp4') || m.includes('m4a') || m.includes('aac')) return 'audio.m4a';\n if (m.includes('mpeg') || m.includes('mp3')) return 'audio.mp3';\n return 'audio.webm';\n}\n\nexport class VoiceApi {\n constructor(private http: HttpClient) {}\n\n /** GET /api/admin/user \u2192 { stt: preferences.stt|null, tts: preferences.tts|null }.\n * UI hints only \u2014 no secrets. THROWS when not yet authenticated (mirrors\n * PreferencesApi.getAvatarConfig) so callers can retry rather than latch a\n * false \"voice off\". */\n async getVoiceConfig(): Promise<VoiceConfig> {\n const res = await this.http.get<RawProfile>('/api/admin/user');\n if (!res || !res.user) {\n throw new Error('voice config: no authenticated profile');\n }\n let prefs: unknown = res.user.preferences ?? {};\n if (typeof prefs === 'string') {\n try {\n prefs = JSON.parse(prefs);\n } catch {\n prefs = {};\n }\n }\n const p = (prefs as Record<string, unknown> | null) ?? {};\n const stt = p.stt && typeof p.stt === 'object' ? (p.stt as SttPref) : null;\n const tts = p.tts && typeof p.tts === 'object' ? (p.tts as TtsPref) : null;\n return { stt, tts };\n }\n\n /** POST an audio clip \u2192 transcript text. Uses the caller's saved STT\n * preference server-side unless `language` overrides it. */\n async transcribe(blob: Blob, language?: string): Promise<string> {\n const form = new FormData();\n form.append('file', blob, filenameFor(blob.type));\n if (language) form.append('language', language);\n const res = await this.http.upload<{ text?: string }>('/api/audio/stt/transcribe', form);\n return res?.text ?? '';\n }\n\n /** POST text \u2192 synthesized audio Blob. Uses the caller's active TTS profile\n * server-side unless `opts` overrides it. */\n async speak(text: string, opts?: TtsSpeakOptions): Promise<Blob> {\n const { bytes, contentType } = await this.http.postBinary(\n '/api/audio/tts/speak',\n { text, ...(opts ?? {}) },\n // Synthesis can take a few seconds for longer replies.\n { timeoutMs: 60_000 },\n );\n const buf = bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength) as ArrayBuffer;\n return new Blob([buf], { type: contentType || 'audio/wav' });\n }\n}\n", "/**\n * XgenClient \u2014 the single entry point for the XGen Dex transport layer.\n *\n * ```ts\n * const xgen = new XgenClient({ baseUrl: 'https://xgen.example.com' });\n * await xgen.login('me@corp.com', 'password'); // stores token in memory\n * const { items } = await xgen.agents.list(); // my agents (agent \uBAA9\uB85D)\n * for await (const ev of xgen.chat.stream({ // chat with one, streamed\n * workflowId: items[0].workflowId,\n * workflowName: items[0].workflowName,\n * input: '\uC548\uB155\uD558\uC138\uC694',\n * interactionId: 'conv-1',\n * })) {\n * if (ev.kind === 'text') process.stdout.write(ev.content);\n * }\n * ```\n *\n * Node-agnostic: the same chat stream drives agent_geny, agent_xgen and\n * agent_harness agents. The class holds tokens in memory only \u2014 persistence\n * (keychain) and base-URL config are the host's concern (Electron main).\n */\nimport { AgentDataApi } from './agent-data';\nimport { FilestoreApi } from './filestore';\nimport { AgentsApi } from './agents';\nimport { AuthApi } from './auth';\nimport { AvatarsApi } from './avatars';\nimport { ChatApi } from './chat';\nimport { HistoryApi } from './history';\nimport { PreferencesApi } from './preferences';\nimport { SshApi } from './ssh';\nimport { TeamsApi } from './teams';\nimport { VoiceApi } from './voice';\nimport { HttpClient, type FetchLike } from './client';\nimport type { CurrentUser, LoginResult } from './types';\n\nexport interface XgenClientOptions {\n baseUrl: string;\n fetch?: FetchLike;\n accessToken?: string;\n refreshToken?: string;\n onAuthFailure?: () => void;\n /**\n * \uC561\uC138\uC2A4 \uD1A0\uD070\uC774 **\uD68C\uC804**\uB420 \uB54C\uB9C8\uB2E4 \uD638\uCD9C\uB41C\uB2E4 (\uB85C\uADF8\uC778 / restore \uC758 validate \uD68C\uC804 /\n * ensureFreshAuth). \uAC8C\uC774\uD2B8\uC6E8\uC774\uB294 \uD68C\uC804 \uC2DC **\uC774\uC804 \uD1A0\uD070\uC758 \uC138\uC158 \uD0A4\uB97C \uC0AD\uC81C**\uD558\uBBC0\uB85C,\n * \uD638\uC2A4\uD2B8\uB294 \uC774 \uCF5C\uBC31\uC73C\uB85C keychain \uC744 \uC989\uC2DC \uAC31\uC2E0\uD574\uC57C \uB2E4\uB978 \uC18C\uBE44\uC790(WS \uBE0C\uB9BF\uC9C0\u00B7\n * \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB3D9\uAE30\uD654)\uAC00 \uD3D0\uAE30\uB41C \uD1A0\uD070\uC73C\uB85C \uC811\uC18D\uD558\uB2E4 403 \uC744 \uB9DE\uC9C0 \uC54A\uB294\uB2E4.\n */\n onTokensRotated?: (accessToken: string, refreshToken?: string) => void;\n}\n\nexport class XgenClient {\n readonly http: HttpClient;\n readonly auth: AuthApi;\n readonly agents: AgentsApi;\n readonly chat: ChatApi;\n readonly history: HistoryApi;\n readonly preferences: PreferencesApi;\n readonly ssh: SshApi;\n readonly teams: TeamsApi;\n readonly avatars: AvatarsApi;\n readonly voice: VoiceApi;\n readonly agentData: AgentDataApi;\n readonly filestore: FilestoreApi;\n\n private refreshToken?: string;\n private readonly onTokensRotated?: (accessToken: string, refreshToken?: string) => void;\n /** ensureFreshAuth \uC758 single-flight \uAC00\uB4DC \u2014 \uB3D9\uC2DC 401 \uB4E4\uC774 refresh \uB97C \uD55C \uBC88\uB9CC \uD0DC\uC6B4\uB2E4. */\n private refreshing: Promise<string | null> | null = null;\n user: CurrentUser | null = null;\n\n constructor(opts: XgenClientOptions) {\n this.http = new HttpClient({\n baseUrl: opts.baseUrl,\n fetch: opts.fetch,\n onAuthFailure: opts.onAuthFailure,\n });\n if (opts.accessToken) this.http.setToken(opts.accessToken);\n this.refreshToken = opts.refreshToken;\n this.onTokensRotated = opts.onTokensRotated;\n this.auth = new AuthApi(this.http);\n this.agents = new AgentsApi(this.http);\n this.chat = new ChatApi(this.http);\n this.history = new HistoryApi(this.http);\n this.preferences = new PreferencesApi(this.http);\n this.ssh = new SshApi(this.http);\n this.teams = new TeamsApi(this.http);\n this.avatars = new AvatarsApi(this.http);\n this.voice = new VoiceApi(this.http);\n this.agentData = new AgentDataApi(this.http);\n this.filestore = new FilestoreApi(this.http);\n }\n\n setBaseUrl(baseUrl: string): void {\n this.http.setBaseUrl(baseUrl);\n }\n\n setTokens(accessToken: string | null, refreshToken?: string): void {\n this.http.setToken(accessToken);\n if (refreshToken !== undefined) this.refreshToken = refreshToken;\n }\n\n /** Log in and adopt the returned tokens. */\n async login(email: string, password: string): Promise<LoginResult> {\n return this.adoptLogin(await this.auth.login(email, password));\n }\n\n /** Adopt tokens returned by an external SSO bridge and resolve full identity. */\n async adoptLogin(res: LoginResult): Promise<LoginResult> {\n this.http.setToken(res.accessToken);\n this.refreshToken = res.refreshToken;\n this.onTokensRotated?.(res.accessToken, res.refreshToken);\n this.user = {\n userId: res.userId,\n username: res.username,\n isSuperuser: false,\n roles: [],\n permissions: [],\n };\n // Resolve full identity/permissions (best-effort).\n try {\n const { user } = await this.auth.validate(res.accessToken, res.refreshToken);\n if (user) this.user = user;\n } catch {\n /* keep the minimal identity */\n }\n return res;\n }\n\n /**\n * Validate the current session, rotating the access token if the gateway\n * returned a fresh one. Returns true if still/again authenticated.\n */\n async restore(accessToken: string, refreshToken?: string): Promise<boolean> {\n return (await this.restoreDetailed(accessToken, refreshToken)) === 'valid';\n }\n\n /**\n * restore() \uC758 \uD310\uC815 \uC138\uBD84\uD654 \u2014 \uD638\uCD9C\uC790\uAC00 \uD1A0\uD070 \uD3D0\uAE30 \uC5EC\uBD80\uB97C \uC62C\uBC14\uB974\uAC8C \uC815\uD560 \uC218\n * \uC788\uAC8C \uD55C\uB2E4 (geny-connector validateAndRefreshAuth \uAC15\uAC74\uC131 \uC774\uC2DD):\n * 'valid' \u2014 \uC778\uC99D \uC131\uACF5 (\uD1A0\uD070 \uD68C\uC804 \uBC18\uC601\uB428)\n * 'invalid' \u2014 \uC11C\uBC84\uAC00 **\uC751\uB2F5\uC73C\uB85C** \uAC70\uBD80 (\uD1A0\uD070 \uD3D0\uAE30\uAC00 \uB9DE\uB2E4)\n * 'network' \u2014 \uC11C\uBC84 \uBBF8\uC751\uB2F5/\uB124\uD2B8\uC6CC\uD06C \uC624\uB958 (\uD1A0\uD070\uC744 \uC9C0\uC6B0\uBA74 \uC548 \uB41C\uB2E4 \u2014 \uC77C\uC2DC\n * \uC7A5\uC560 \uD6C4 \uC7AC\uC2DC\uC791\uC5D0\uC11C \uC7AC\uB85C\uADF8\uC778\uC744 \uAC15\uC694\uD558\uAC8C \uB41C\uB2E4)\n */\n async restoreDetailed(\n accessToken: string,\n refreshToken?: string,\n ): Promise<'valid' | 'invalid' | 'network'> {\n this.http.setToken(accessToken);\n this.refreshToken = refreshToken;\n let sawNetworkError = false;\n try {\n const { user, newAccessToken } = await this.auth.validate(accessToken, refreshToken);\n if (newAccessToken) {\n this.http.setToken(newAccessToken);\n this.onTokensRotated?.(newAccessToken, refreshToken);\n }\n if (user) {\n this.user = user;\n return 'valid';\n }\n } catch {\n sawNetworkError = true;\n }\n // Try an explicit refresh as a fallback.\n if (refreshToken) {\n try {\n const fresh = await this.auth.refresh(refreshToken);\n if (fresh) {\n this.http.setToken(fresh);\n this.onTokensRotated?.(fresh, refreshToken);\n const { user } = await this.auth.validate(fresh, refreshToken);\n if (user) {\n this.user = user;\n return 'valid';\n }\n }\n // \uC11C\uBC84\uAC00 \uC751\uB2F5\uD588\uACE0 \uAC70\uBD80\uD588\uB2E4 \u2014 \uBA85\uC2DC\uC801 invalid.\n sawNetworkError = false;\n } catch {\n sawNetworkError = true;\n }\n }\n return sawNetworkError ? 'network' : 'invalid';\n }\n\n getAccessTokenAfterRotation(): string {\n // The HttpClient holds the current (possibly rotated) token.\n return (this.http as unknown as { accessToken: string }).accessToken ?? '';\n }\n\n /**\n * \uC778\uC99D \uC2E4\uD328(401/403)\uB97C \uB9DE\uC740 \uC18C\uBE44\uC790\uAC00 \uBD80\uB974\uB294 **\uC790\uAC00\uCE58\uC720** \uACBD\uB85C: refresh \uD1A0\uD070\uC73C\uB85C\n * \uC561\uC138\uC2A4 \uD1A0\uD070\uC744 \uD68C\uC804\uC2DC\uD0A4\uACE0 \uC0C8 \uD1A0\uD070\uC744 \uB3CC\uB824\uC900\uB2E4. \uC2E4\uD328(refresh \uD1A0\uD070 \uC5C6\uC74C/\uAC70\uBD80)\uBA74\n * null \u2014 \uADF8\uB54C\uB294 \uC9C4\uC9DC \uC7AC\uB85C\uADF8\uC778 \uB300\uC0C1\uC774\uB2E4.\n *\n * single-flight: WS \uBE0C\uB9BF\uC9C0\u00B7\uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uB3D9\uAE30\uD654\u00B7HTTP \uAC00 \uB3D9\uC2DC\uC5D0 401 \uC744 \uB9DE\uC544\uB3C4\n * refresh \uB294 \uD55C \uBC88\uB9CC \uB098\uAC04\uB2E4 (\uAC8C\uC774\uD2B8\uC6E8\uC774\uB294 refresh \uB9C8\uB2E4 \uC774\uC804 \uC138\uC158\uC744 \uC9C0\uC6B0\uBBC0\uB85C,\n * \uB3D9\uC2DC refresh \uB294 \uC11C\uB85C\uC758 \uC0C8 \uD1A0\uD070\uC744 \uD3D0\uAE30\uD558\uB294 \uACBD\uC7C1\uC774 \uB41C\uB2E4).\n *\n * ``fallbackRefreshToken`` \u2014 \uC778\uBA54\uBAA8\uB9AC\uC5D0 refresh \uD1A0\uD070\uC774 \uC5C6\uC744 \uB54C(\uC7AC\uC2DC\uC791 \uC9C1\uD6C4 \uB4F1)\n * \uD638\uC2A4\uD2B8\uAC00 keychain \uAC12\uC744 \uB118\uACA8\uC904 \uC218 \uC788\uB2E4.\n */\n async ensureFreshAuth(fallbackRefreshToken?: string): Promise<string | null> {\n if (this.refreshing) return this.refreshing;\n const rt = this.refreshToken ?? fallbackRefreshToken;\n if (!rt) return null;\n this.refreshing = (async () => {\n try {\n const fresh = await this.auth.refresh(rt);\n if (!fresh) return null;\n this.http.setToken(fresh);\n this.refreshToken = rt;\n this.onTokensRotated?.(fresh, rt);\n return fresh;\n } catch {\n return null;\n } finally {\n this.refreshing = null;\n }\n })();\n return this.refreshing;\n }\n\n /** The current refresh token, so the host can persist it (e.g. keychain). */\n getRefreshToken(): string | undefined {\n return this.refreshToken;\n }\n\n async logout(): Promise<void> {\n const token = this.getAccessTokenAfterRotation();\n if (token) await this.auth.logout(token);\n this.http.setToken(null);\n this.refreshToken = undefined;\n this.user = null;\n }\n}\n\nexport * from './types';\nexport * from './agent-data';\nexport * from './filestore';\nexport * from './notifications';\nexport { ApiError } from './client';\nexport { SseParser } from './sse';\nexport { frameToChatEvent } from './chat';\nexport { sha256Hex } from './hash';\nexport type { StoreAvatar } from './avatars';\n// Agent \u2194 Teams \uB2E4\uB9AC \u2014 \uCEE8\uD14D\uC2A4\uD2B8 \uBD09\uD22C\uC640 \uACF5\uC720 \uCD9C\uCC98 \uD45C\uC2DD. \uB80C\uB354\uB7EC\uC640 \uBA54\uC778\uC774 \uAC19\uC740\n// \uD615\uC2DD\uC744 \uC368\uC57C \uD558\uBBC0\uB85C core \uB97C \uD1B5\uD574\uC11C\uB9CC \uB178\uCD9C\uD55C\uB2E4.\nexport {\n TEAMS_CONTEXT_START,\n TEAMS_CONTEXT_END,\n TEAMS_CONTEXT_MAX_CHARS,\n toContextEntries,\n buildTeamsContext,\n prependTeamsContext,\n stripTeamsContext,\n formatShareHeader,\n buildSharedMessage,\n parseSharedMessage,\n shareBodyOf,\n} from './teams-bridge';\nexport {\n TEAMS_ATTACHMENT_EXTENSIONS,\n TEAMS_ATTACHMENT_MAX_BYTES,\n teamsAttachmentRejectReason,\n directRoomNameForViewer,\n} from './teams';\n// SSH \u2014 \uC6F9 \uB9C8\uC774\uD398\uC774\uC9C0 [SSH \uC5F0\uB3D9 \uC124\uC815] \uACFC **\uAC19\uC740 \uC800\uC7A5\uC18C**\uB97C \uBCF4\uB294 \uD0C0\uC785\uB4E4.\nexport { EMPTY_SSH_CONFIG } from './ssh';\nexport type { SshServer, SshServerInput, SshConfig, SshTestResult } from './ssh';\nexport type {\n TeamsContextEntry,\n TeamsContextRoom,\n TeamsContextEnvelope,\n TeamsShareRef,\n ParsedSharedMessage,\n} from './teams-bridge';\n\n// \u2500\u2500 \uB2E4\uC6B4\uB85C\uB4DC \uC13C\uD130 \u00B7 \uD074\uB77C\uC6B0\uB4DC \uB9C1\uD06C \u00B7 Teams \uC18C\uCF13 \uC8FC\uC18C \u2500\u2500\n// \uC571 \uC548\uC5D0 \uBC15\uD600 \uC788\uB358 \uACBD\uB85C\uB4E4\uC744 \uC5EC\uAE30\uB85C \uC62C\uB838\uB2E4. \uB370\uC2A4\uD06C\uD1B1\uB9CC \uC4F0\uB294 \uAC83\uB3C4 \uC788\uC9C0\uB9CC, \uACBD\uB85C\uAC00\n// \uC571\uC5D0 \uC788\uC73C\uBA74 \uB2E4\uC74C \uC571\uC774 \uADF8\uAC83\uC744 **\uBCF5\uC0AC**\uD55C\uB2E4 \u2014 \uC774 \uC800\uC7A5\uC18C\uAC00 \uC5C6\uC560\uB824\uB294 \uBC14\uB85C \uADF8 \uC77C\uC774\uB2E4.\nexport { InstallersApi, installerListPath, installerDownloadPath } from './installers';\nexport type { InstallerPackage, InstallerListResponse } from './installers';\nexport { CloudLinksApi, CLOUD_LINKS_PATH, cloudLinkPath } from './cloud-links';\nexport type { CloudLink, CloudLinksResponse } from './cloud-links';\nexport {\n teamsUserSocketUrl,\n teamsRoomSocketUrl,\n TEAMS_USER_SOCKET_PATH,\n teamsRoomSocketPath,\n} from './teams-ws';\n", "/**\n * McpBridge \u2014 the connector side of the XGEN \"connector-hosted Local MCP\" bridge.\n *\n * Opens a WebSocket to the XGEN backend (through the gateway) at\n * `/api/tools/ws/connector-mcp/{user_id}`, advertises the aggregated local MCP\n * tool catalog via a `hello` frame, then answers `mcp_call` frames by invoking\n * the local MCP server through MCPManager and replying with `mcp_result`.\n *\n * The XGEN backend auto-injects these tools into the user's running agents\n * (agent_xgen / agent_harness / agent_geny), so any chat with the logged-in\n * user can call the connector-hosted tools.\n *\n * Lives in the MAIN process: tokens + subprocess spawning stay out of the\n * renderer. Uses `ws` (Node global WebSocket isn't stable on Electron's Node).\n *\n * Reconnect UX is DEBOUNCED so a flapping socket (e.g. the backend endpoint not\n * yet deployed, or an idle proxy timeout) doesn't make the status flicker\n * \"\uC5F0\uACB0 \uB300\uAE30 \uC911 \u2194 \uC5F0\uACB0\uB428\": a connection is reported \"connected\" only after it stays\n * open a beat (settle), and \"disconnected\" only after it stays closed a beat\n * (grace). Reconnect uses exponential backoff; status emits are de-duplicated.\n */\nimport WebSocket from 'ws';\nimport { getMcpManager, type McpServerAdvert } from './mcp-manager';\nimport {\n getLocalToolProvider,\n localToolCallContext,\n LOCAL_SERVER,\n} from './local-tools';\nimport { appendMcpRuntimeLog } from './mcp-runtime-log';\nimport { xgenWebSocketTlsOptions } from './connection-security';\n\nconst HEARTBEAT_MS = 20000;\nconst RECONNECT_MIN_MS = 5000;\nconst RECONNECT_MAX_MS = 60000;\nconst SETTLE_MS = 1200; // stay open this long before we call it \"connected\"\nconst GRACE_MS = 4000; // stay closed this long before we call it \"disconnected\"\n\nexport interface McpBridgeStatus {\n enabled: boolean;\n connected: boolean;\n /** \uCD5C\uC2E0 hello \uCE74\uD0C8\uB85C\uADF8\uB97C workflow\uAC00 catalog_id\uB85C \uD655\uC778\uD588\uB294\uC9C0 \uC5EC\uBD80. */\n catalogSynced: boolean;\n /** workflow\uAC00 ACK\uD55C \uCD5C\uC2E0 \uCE74\uD0C8\uB85C\uADF8\uC758 \uB3C4\uAD6C \uC218. */\n serverToolCount: number;\n error?: string;\n servers: McpServerAdvert[];\n}\n\nexport class McpBridge {\n private ws: WebSocket | null = null;\n private hb: ReturnType<typeof setInterval> | null = null;\n private retry: ReturnType<typeof setTimeout> | null = null;\n private settle: ReturnType<typeof setTimeout> | null = null;\n private grace: ReturnType<typeof setTimeout> | null = null;\n private backoff = RECONNECT_MIN_MS;\n private stopped = true;\n /** Debounced UI state \u2014 NOT the raw socket state, to avoid flicker. */\n private uiConnected = false;\n private catalogSynced = false;\n private serverToolCount = 0;\n private catalogSeq = 0;\n private pendingCatalogId = '';\n private serverUrl = '';\n private userId = '';\n private allowPrivateCertificate = false;\n private getToken: () => Promise<string | null> = async () => null;\n private refreshAuth: () => Promise<string | null> = async () => null;\n private lastServers: McpServerAdvert[] = [];\n private lastError: string | undefined;\n private lastEmit = '';\n private onStatus: (s: McpBridgeStatus) => void = () => {};\n\n setStatusListener(cb: (s: McpBridgeStatus) => void): void {\n this.onStatus = cb;\n }\n\n status(): McpBridgeStatus {\n // Include the connector-hosted built-ins as a synthetic `local` server so the\n // renderer's \"exposed tools\" view lists them alongside external MCP servers\n // (they ride the same agent catalog). Full schemas \u2014 the UI shows per-tool\n // description/inputSchema.\n const localTools = getLocalToolProvider().advertise();\n const localServer: McpServerAdvert[] = localTools.length\n ? [{ name: LOCAL_SERVER, connected: true, tools: localTools }]\n : [];\n return {\n enabled: !this.stopped,\n connected: this.uiConnected,\n catalogSynced: this.catalogSynced,\n serverToolCount: this.serverToolCount,\n error: this.lastError,\n servers: [...localServer, ...this.lastServers],\n };\n }\n\n /** Emit only when the status actually changed (dedupe). */\n private emit(): void {\n const s = this.status();\n const key = JSON.stringify({\n e: s.enabled,\n c: s.connected,\n sync: s.catalogSynced,\n tools: s.serverToolCount,\n err: s.error,\n n: s.servers.map((x) => [x.name, x.connected, x.tools.length]),\n });\n if (key === this.lastEmit) return;\n this.lastEmit = key;\n this.onStatus(s);\n }\n\n start(opts: {\n serverUrl: string;\n userId: string;\n allowPrivateCertificate: boolean;\n getToken: () => Promise<string | null>;\n /** \uD578\uB4DC\uC170\uC774\uD06C 401/403 \uC790\uAC00\uCE58\uC720 \u2014 refresh \uB85C \uD1A0\uD070 \uD68C\uC804(single-flight). */\n refreshAuth?: () => Promise<string | null>;\n }): void {\n // A no-op restart (same target, already running) must NOT tear down a live\n // socket \u2014 that alone would cause a visible flap on every auth refresh.\n const sameTarget =\n this.serverUrl === opts.serverUrl &&\n this.userId === opts.userId &&\n this.allowPrivateCertificate === opts.allowPrivateCertificate;\n this.serverUrl = opts.serverUrl;\n this.userId = opts.userId;\n this.allowPrivateCertificate = opts.allowPrivateCertificate;\n this.getToken = opts.getToken;\n if (opts.refreshAuth) this.refreshAuth = opts.refreshAuth;\n if (!this.stopped && sameTarget && (this.ws || this.retry)) {\n void this.refreshCatalog();\n return;\n }\n this.stopped = false;\n this.backoff = RECONNECT_MIN_MS;\n this.reconnect(true);\n }\n\n stop(): void {\n this.stopped = true;\n this.clearTimers();\n try {\n const ws = this.ws;\n if (ws) {\n ws.removeAllListeners();\n // \uC5F0\uACB0 \uC911\uC778 \uC18C\uCF13\uC744 \uB2EB\uC73C\uBA74 ws \uAC00 'error' \uB97C \uB0B8\uB2E4(\"WebSocket was closed\n // before the connection was established\"). \uB9AC\uC2A4\uB108\uB97C \uBAA8\uB450 \uB5C0 \uB4A4\uB77C \uBC1B\uC744\n // \uD578\uB4E4\uB7EC\uAC00 \uC5C6\uACE0, EventEmitter \uC758 \uBBF8\uCC98\uB9AC 'error' \uB294 **\uB358\uC9C4\uB2E4** \u2014 \uD504\uB85C\uC138\uC2A4\uAC00\n // \uC8FD\uB294\uB2E4. \uADF8\uB798\uC11C \uB2EB\uAE30 \uC9C1\uC804\uC5D0 \uC0BC\uD0AC \uD578\uB4E4\uB7EC \uD558\uB098\uB97C \uB2E4\uC2DC \uB2E8\uB2E4.\n ws.on('error', () => undefined);\n ws.close();\n }\n } catch {\n /* ignore */\n }\n this.ws = null;\n this.uiConnected = false;\n this.catalogSynced = false;\n this.serverToolCount = 0;\n this.pendingCatalogId = '';\n this.lastError = undefined;\n this.emit();\n }\n\n private clearTimers(): void {\n for (const t of [this.retry, this.hb, this.settle, this.grace]) if (t) clearTimeout(t as never);\n this.retry = this.hb = this.settle = this.grace = null;\n }\n\n /**\n * \uC11C\uBC84\uB4E4\uC5D0 \uB2E4\uC2DC \uBD99\uC5B4 \uCE74\uD0C8\uB85C\uADF8\u00B7\uC0C1\uD0DC\uB97C \uAC31\uC2E0\uD55C\uB2E4.\n *\n * \uC18C\uCF13\uC774 \uC5F4\uB824 \uC788\uC9C0 \uC54A\uC544\uB3C4 \uC7AC\uAD11\uACE0\uD55C\uB2E4 \u2014 \uD55C \uBC88 \uC2E4\uD328\uD55C \uC11C\uBC84\uC758 \uC624\uB958 \uBB38\uAD6C\uAC00\n * \uC18C\uCF13 \uC774\uBCA4\uD2B8\uAC00 \uC788\uC744 \uB54C\uAE4C\uC9C0 \uD654\uBA74\uC5D0 \uADF8\uB300\uB85C \uB0A8\uC544 \uC788\uB358 \uBB38\uC81C(\uC0AC\uC6A9\uC790\uAC00 uv \uB97C\n * \uB098\uC911\uC5D0 \uC124\uCE58\uD55C \uACBD\uC6B0)\uB97C \uC5EC\uAE30\uC11C \uB04A\uB294\uB2E4. sendHello() \uB294 \uC5F4\uB824 \uC788\uC744 \uB54C\uB9CC\n * \uC2E4\uC81C\uB85C \uC804\uC1A1\uD558\uACE0, \uC0C1\uD0DC\uB294 \uD56D\uC0C1 emit \uD55C\uB2E4.\n */\n /**\n * \uCE74\uD0C8\uB85C\uADF8\uAC00 \uC11C\uBC84\uC5D0 \uBC18\uC601\uB420 \uB54C\uAE4C\uC9C0 \uAE30\uB2E4\uB9B0\uB2E4 (\uD5E4\uB4DC\uB9AC\uC2A4 \uC2E4\uD589\uC6A9).\n *\n * CLI \uB294 \uD55C \uBC88 \uBB3C\uC5B4\uBCF4\uACE0 \uB05D\uB098\uB294 \uBA85\uB839\uC774 \uB9CE\uB2E4 \u2014 \uBE0C\uB9BF\uC9C0\uAC00 \uBD99\uAE30 \uC804\uC5D0 \uCC44\uD305\uC744 \uC2DC\uC791\uD558\uBA74\n * \uC5D0\uC774\uC804\uD2B8\uC5D0\uAC8C \uB85C\uCEEC \uB3C4\uAD6C\uAC00 \uC5C6\uB294 \uCC44\uB85C \uCCAB \uD134\uC774 \uB3C8\uB2E4. \uB370\uC2A4\uD06C\uD1B1\uC740 \uCC3D\uC774 \uB5A0 \uC788\uC73C\uB2C8\n * \uC0C1\uD0DC \uD45C\uC2DC\uB85C \uCDA9\uBD84\uD558\uC9C0\uB9CC CLI \uC5D0\uB294 \uAE30\uB2E4\uB9B4 \uC790\uB9AC\uAC00 \uD544\uC694\uD558\uB2E4.\n *\n * \uD0C0\uC784\uC544\uC6C3\uC740 \uC2E4\uD328\uAC00 \uC544\uB2C8\uB2E4 \u2014 \uD604\uC7AC \uC0C1\uD0DC\uB97C \uADF8\uB300\uB85C \uB3CC\uB824\uC8FC\uACE0, \uBD80\uB97C \uCABD\uC774 \uD310\uB2E8\uD55C\uB2E4.\n */\n async waitUntilReady(timeoutMs = 3_000): Promise<McpBridgeStatus> {\n if (this.status().catalogSynced) return this.status();\n return new Promise((resolve) => {\n // \uAE30\uC874 \uB9AC\uC2A4\uB108\uB97C \uAC00\uB85C\uCC44\uACE0 \uB05D\uB098\uBA74 \uB418\uB3CC\uB9B0\uB2E4 \u2014 \uB370\uC2A4\uD06C\uD1B1\uC740 \uC0C1\uD0DC \uD45C\uC2DC\uB97C \uC774\n // \uB9AC\uC2A4\uB108\uB85C \uBC1B\uACE0 \uC788\uC5B4\uC11C, \uC7A0\uAE50 \uAE30\uB2E4\uB9B0 \uB300\uAC00\uB85C \uADF8\uAC8C \uC8FD\uC73C\uBA74 \uC548 \uB41C\uB2E4.\n const previous = this.onStatus;\n let done = false;\n const finish = (s: McpBridgeStatus): void => {\n if (done) return;\n done = true;\n this.setStatusListener(previous);\n resolve(s);\n };\n const timer = setTimeout(() => finish(this.status()), Math.max(0, timeoutMs));\n this.setStatusListener((s) => {\n previous(s);\n if (!s.catalogSynced) return;\n clearTimeout(timer);\n finish(s);\n });\n });\n }\n\n async refreshCatalog(): Promise<void> {\n await this.sendHello();\n }\n\n private wsUrl(): string {\n const base = this.serverUrl.replace(/\\/+$/, '').replace(/^http/, 'ws');\n return `${base}/api/tools/ws/connector-mcp/${encodeURIComponent(this.userId)}`;\n }\n\n private scheduleRetry(): void {\n if (this.stopped || this.retry) return;\n const delay = this.backoff;\n this.backoff = Math.min(RECONNECT_MAX_MS, Math.round(this.backoff * 1.8));\n this.retry = setTimeout(() => {\n this.retry = null;\n void this.reconnect(false);\n }, delay);\n }\n\n private async reconnect(immediate: boolean): Promise<void> {\n if (this.stopped) return;\n if (immediate && this.retry) {\n clearTimeout(this.retry);\n this.retry = null;\n }\n if (!immediate && this.retry) return;\n if (this.settle) {\n clearTimeout(this.settle);\n this.settle = null;\n }\n if (this.hb) {\n clearInterval(this.hb);\n this.hb = null;\n }\n try {\n const ws = this.ws;\n if (ws) {\n ws.removeAllListeners();\n // \uC5F0\uACB0 \uC911\uC778 \uC18C\uCF13\uC744 \uB2EB\uC73C\uBA74 ws \uAC00 'error' \uB97C \uB0B8\uB2E4(\"WebSocket was closed\n // before the connection was established\"). \uB9AC\uC2A4\uB108\uB97C \uBAA8\uB450 \uB5C0 \uB4A4\uB77C \uBC1B\uC744\n // \uD578\uB4E4\uB7EC\uAC00 \uC5C6\uACE0, EventEmitter \uC758 \uBBF8\uCC98\uB9AC 'error' \uB294 **\uB358\uC9C4\uB2E4** \u2014 \uD504\uB85C\uC138\uC2A4\uAC00\n // \uC8FD\uB294\uB2E4. \uADF8\uB798\uC11C \uB2EB\uAE30 \uC9C1\uC804\uC5D0 \uC0BC\uD0AC \uD578\uB4E4\uB7EC \uD558\uB098\uB97C \uB2E4\uC2DC \uB2E8\uB2E4.\n ws.on('error', () => undefined);\n ws.close();\n }\n } catch {\n /* ignore */\n }\n this.ws = null;\n const token = await this.getToken();\n if (this.stopped || !this.serverUrl || !this.userId) {\n if (!this.stopped) this.scheduleRetry();\n return;\n }\n let ws: WebSocket;\n try {\n ws = new WebSocket(this.wsUrl(), {\n headers: token ? { Authorization: `Bearer ${token}` } : undefined,\n ...xgenWebSocketTlsOptions(this.allowPrivateCertificate),\n });\n } catch (e) {\n this.lastError = e instanceof Error ? e.message : String(e);\n this.emit();\n this.scheduleRetry();\n return;\n }\n this.ws = ws;\n\n // \uD578\uB4DC\uC170\uC774\uD06C\uAC00 401/403 \uC73C\uB85C \uAC70\uC808\uB410\uB2E4 = \uD1A0\uD070\uC774 \uD68C\uC804\uB410\uAC70\uB098 \uC138\uC158\uC774 \uD68C\uC218\uB41C \uAC83\n // (\uAC8C\uC774\uD2B8\uC6E8\uC774\uB294 \uD68C\uC804/\uC138\uC158\uC81C\uD55C \uB54C \uC774\uC804 \uC138\uC158 \uD0A4\uB97C \uC9C0\uC6B4\uB2E4). \uC774 \uB9AC\uC2A4\uB108\uAC00 \uC5C6\uC73C\uBA74\n // 'error' \uB85C\uB9CC \uB5A8\uC5B4\uC838 **\uD3D0\uAE30\uB41C \uD1A0\uD070\uC73C\uB85C \uBC31\uC624\uD504 \uC7AC\uC2DC\uB3C4\uB9CC \uBC18\uBCF5**\uD558\uACE0, \uBE0C\uB9BF\uC9C0\uB294\n // \uC601\uC601 \uC548 \uBD99\uB294\uB2E4 \u2192 \uC5D0\uC774\uC804\uD2B8\uC5D0 \uB85C\uCEEC \uB3C4\uAD6C\uAC00 \uC808\uB300 \uB178\uCD9C\uB418\uC9C0 \uC54A\uB294\uB2E4 (\uC2E4\uAE30).\n // refresh \uB85C \uD1A0\uD070\uC744 \uD68C\uC804\uC2DC\uD0A4\uBA74(\uB2E4\uC74C reconnect \uAC00 getToken \uC73C\uB85C \uC0C8 \uD1A0\uD070\uC744\n // \uC9D1\uB294\uB2E4) \uC989\uC2DC \uC7AC\uC2DC\uB3C4\uD55C\uB2E4. \uB9AC\uC2A4\uB108\uB97C \uB2E8 \uC21C\uAC04 \uAE30\uBCF8 error/close \uACBD\uB85C\uAC00 \uAEBC\uC9C0\uBBC0\uB85C\n // \uC815\uB9AC\uC640 \uC7AC\uC2DC\uB3C4 \uC2A4\uCF00\uC904\uAE4C\uC9C0 \uC5EC\uAE30\uC11C \uCC45\uC784\uC9C4\uB2E4.\n ws.on('unexpected-response', (_req, res) => {\n const sc = res?.statusCode ?? 0;\n try { res?.resume?.(); } catch { /* drain */ }\n this.lastError = `handshake HTTP ${sc}`;\n const heal =\n sc === 401 || sc === 403\n ? Promise.resolve(this.refreshAuth()).catch(() => null)\n : Promise.resolve<string | null>(null);\n void heal.then((fresh) => {\n if (this.ws === ws) this.ws = null;\n try {\n ws.removeAllListeners();\n ws.close();\n } catch {\n /* ignore */\n }\n if (fresh) this.backoff = RECONNECT_MIN_MS;\n this.emit();\n this.scheduleRetry();\n });\n });\n\n ws.on('open', () => {\n if (this.grace) {\n clearTimeout(this.grace);\n this.grace = null;\n }\n this.lastError = undefined;\n void this.sendHello();\n if (this.hb) clearInterval(this.hb);\n this.hb = setInterval(() => {\n try {\n if (ws.readyState === WebSocket.OPEN) ws.send(JSON.stringify({ type: 'ping' }));\n } catch {\n /* ignore */\n }\n }, HEARTBEAT_MS);\n // Report \"connected\" only after the socket has stayed open a beat \u2014 a\n // socket that opens then immediately closes never flips the UI.\n if (this.settle) clearTimeout(this.settle);\n this.settle = setTimeout(() => {\n this.settle = null;\n if (this.ws === ws && ws.readyState === WebSocket.OPEN) {\n this.uiConnected = true;\n this.backoff = RECONNECT_MIN_MS; // a good connection resets backoff\n this.emit();\n }\n }, SETTLE_MS);\n });\n\n ws.on('message', (raw: WebSocket.RawData) => void this.onMessage(String(raw)));\n\n ws.on('close', () => {\n if (this.settle) {\n clearTimeout(this.settle);\n this.settle = null;\n }\n if (this.hb) {\n clearInterval(this.hb);\n this.hb = null;\n }\n if (this.stopped) return;\n this.catalogSynced = false;\n this.serverToolCount = 0;\n this.pendingCatalogId = '';\n // Only flip the UI to \"disconnected\" after a grace period, so a quick\n // reconnect (settle before grace) keeps the UI steady on \"\uC5F0\uACB0\uB428\".\n if (this.uiConnected && !this.grace) {\n this.grace = setTimeout(() => {\n this.grace = null;\n this.uiConnected = false;\n this.emit();\n }, GRACE_MS);\n }\n this.scheduleRetry();\n });\n ws.on('error', (e: Error) => {\n this.lastError = e?.message;\n try {\n ws.close();\n } catch {\n /* ignore */\n }\n });\n }\n\n private async sendHello(): Promise<void> {\n try {\n const adverts = await getMcpManager().advertise();\n this.lastServers = adverts;\n const tools = adverts\n .filter((a) => a.connected)\n .flatMap((a) =>\n a.tools.map((t) => ({\n server: a.name,\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema,\n })),\n );\n // Connector-hosted built-ins (local shell, \u2026) ride the SAME catalog as\n // configured MCP servers \u2014 the backend/agent can't tell them apart, so\n // local machine control needs no server-side wiring. Prepended so they\n // are stable and visible even before any external server connects.\n const builtins = getLocalToolProvider()\n .advertise()\n .map((t) => ({\n server: LOCAL_SERVER,\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema,\n }));\n tools.unshift(...builtins);\n if (this.ws?.readyState === WebSocket.OPEN) {\n const catalogId = `${Date.now()}-${++this.catalogSeq}`;\n this.pendingCatalogId = catalogId;\n this.catalogSynced = false;\n this.serverToolCount = 0;\n this.ws.send(JSON.stringify({ type: 'hello', catalog_id: catalogId, tools }));\n appendMcpRuntimeLog({\n kind: 'catalog',\n message: `\uB3C4\uAD6C \uCE74\uD0C8\uB85C\uADF8 ${tools.length}\uAC1C \uC7AC\uCD08\uAE30\uD654 \uC694\uCCAD`,\n requestId: catalogId,\n });\n }\n this.emit();\n } catch (e) {\n this.lastError = e instanceof Error ? e.message : String(e);\n this.emit();\n }\n }\n\n private async onMessage(text: string): Promise<void> {\n let msg: {\n type?: string;\n request_id?: string;\n server?: string;\n tool?: string;\n args?: unknown;\n context?: unknown;\n catalog_id?: string;\n tool_count?: number;\n };\n try {\n msg = JSON.parse(text);\n } catch {\n return;\n }\n if (msg.type === 'ready') {\n if (msg.catalog_id !== this.pendingCatalogId) return;\n this.catalogSynced = true;\n this.serverToolCount = Number.isFinite(msg.tool_count)\n ? Math.max(0, Math.trunc(msg.tool_count as number))\n : 0;\n appendMcpRuntimeLog({\n kind: 'catalog',\n message: `workflow \uB3C4\uAD6C ${this.serverToolCount}\uAC1C \uC801\uC6A9 \uC644\uB8CC`,\n requestId: msg.catalog_id,\n ok: true,\n });\n this.emit();\n return;\n }\n if (msg.type === 'mcp_call') {\n const { request_id, server, tool, args } = msg;\n const context = localToolCallContext(msg.context);\n const startedAt = Date.now();\n appendMcpRuntimeLog({\n kind: 'call',\n message: '\uB85C\uCEEC MCP \uB3C4\uAD6C \uD638\uCD9C \uC218\uC2E0',\n requestId: request_id,\n server: String(server),\n tool: String(tool),\n });\n let payload: Record<string, unknown>;\n try {\n // Built-in (connector-hosted) tools dispatch locally; everything else\n // goes to the configured MCP server via MCPManager. Same wire contract.\n const local = getLocalToolProvider();\n const result = local.owns(String(server))\n ? await local.callTool(String(tool), args ?? {}, context)\n : await getMcpManager().callTool(String(server), String(tool), args ?? {});\n payload = { request_id, ok: true, result };\n } catch (e) {\n payload = { request_id, ok: false, error: e instanceof Error ? e.message : String(e) };\n }\n appendMcpRuntimeLog({\n kind: 'result',\n message: payload.ok\n ? '\uB85C\uCEEC MCP \uB3C4\uAD6C \uC2E4\uD589 \uC131\uACF5'\n : String(payload.error || '\uB85C\uCEEC MCP \uB3C4\uAD6C \uC2E4\uD589 \uC2E4\uD328'),\n requestId: request_id,\n server: String(server),\n tool: String(tool),\n ok: payload.ok === true,\n durationMs: Date.now() - startedAt,\n });\n try {\n this.ws?.send(JSON.stringify({ type: 'mcp_result', ...payload }));\n } catch {\n /* socket gone */\n }\n }\n // 'pong' / 'ready' \u2014 nothing to do.\n }\n}\n\nlet _bridge: McpBridge | null = null;\nexport function getMcpBridge(): McpBridge {\n if (!_bridge) _bridge = new McpBridge();\n return _bridge;\n}\n", "/**\n * SecretPort \uC704\uC5D0 \uC5B9\uC740 \uB450 \uAC1C\uC758 \uC881\uC740 \uC800\uC7A5\uC18C \u2014 MCP \uC11C\uBC84 \uC2DC\uD06C\uB9BF\uACFC OAuth \uC0C1\uD0DC.\n *\n * \uC608\uC804\uC5D0\uB294 \uB370\uC2A4\uD06C\uD1B1 keychain.ts \uC548\uC5D0 \uAC12\uC73C\uB85C \uBC15\uD600 \uC788\uC5C8\uB2E4. \uADF8\uB798\uC11C MCP \uB9E4\uB2C8\uC800\uAC00\n * Electron \uC758 safeStorage \uB97C \uB04C\uC5B4\uC654\uACE0, CLI \uB294 \uAC19\uC740 \uAC1C\uB150\uC744 keytar \uB85C \uB2E4\uC2DC \uC37C\uB2E4.\n * \uC774\uC81C \uB458 \uB2E4 \uC774 \uD30C\uC77C\uC744 \uC4F0\uACE0, \uBB34\uC5C7\uC73C\uB85C \uC800\uC7A5\uD558\uB294\uC9C0\uB294 \uD638\uC2A4\uD2B8\uAC00 \uC900 \uD3EC\uD2B8\uAC00 \uC815\uD55C\uB2E4.\n *\n * \uC774\uB984 \uC811\uB450\uC0AC(`xgen_mcp_*`)\uB294 \uADF8\uB300\uB85C \uB454\uB2E4 \u2014 \uC774\uBBF8 \uC0AC\uC6A9\uC790 \uD0A4\uCCB4\uC778\uC5D0 \uC800\uC7A5\uB41C \uD56D\uBAA9\uB4E4\uC774\n * \uC788\uACE0, \uC811\uB450\uC0AC\uB97C \uBC14\uAFB8\uBA74 \uC5C5\uB370\uC774\uD2B8\uD558\uB294 \uC21C\uAC04 \uADF8 \uC0AC\uB78C\uB4E4\uC758 MCP \uC11C\uBC84\uAC00 \uC804\uBD80 \uC778\uC99D\uC744\n * \uC783\uB294\uB2E4.\n */\nimport type { SecretPort } from './ports/index';\nimport type { McpOAuthState, McpServerSecrets } from './mcp-types';\n\nconst MCP_SECRET_PREFIX = 'xgen_mcp_secret_';\nconst MCP_OAUTH_PREFIX = 'xgen_mcp_oauth_';\n\nfunction nonEmpty(o?: Record<string, string>): boolean {\n return !!o && Object.values(o).some((v) => typeof v === 'string' && v.length > 0);\n}\n\nfunction parseObject<T>(raw: string | null, fallback: T): T {\n if (!raw) return fallback;\n try {\n const p: unknown = JSON.parse(raw);\n return p && typeof p === 'object' ? (p as T) : fallback;\n } catch {\n return fallback;\n }\n}\n\nexport interface McpSecretStore {\n save(server: string, secrets: McpServerSecrets): Promise<boolean>;\n get(server: string): Promise<McpServerSecrets | null>;\n clear(server: string): Promise<void>;\n}\n\nexport function createMcpSecretStore(secrets: SecretPort): McpSecretStore {\n return {\n /** \uBE44\uC5B4 \uC788\uC73C\uBA74 \uC800\uC7A5\uC774 \uC544\uB2C8\uB77C **\uC0AD\uC81C**\uB2E4 \u2014 \uBE48 \uAC1D\uCCB4\uB97C \uB0A8\uAE30\uBA74 \uB2E4\uC74C \uB85C\uB4DC\uAC00\n * \"\uC124\uC815\uB428\"\uC73C\uB85C \uC77D\uACE0 \uC0AC\uC6A9\uC790\uB294 \uC65C \uC778\uC99D\uC774 \uC548 \uB418\uB294\uC9C0 \uC54C \uC218 \uC5C6\uB2E4. */\n async save(server, value) {\n if (!nonEmpty(value.env) && !nonEmpty(value.headers)) {\n await secrets.set(MCP_SECRET_PREFIX + server, null);\n return true;\n }\n return secrets.set(MCP_SECRET_PREFIX + server, JSON.stringify(value));\n },\n async get(server) {\n return parseObject<McpServerSecrets | null>(\n await secrets.get(MCP_SECRET_PREFIX + server),\n null,\n );\n },\n async clear(server) {\n await secrets.set(MCP_SECRET_PREFIX + server, null);\n },\n };\n}\n\nexport interface McpOAuthStore {\n load(server: string): Promise<McpOAuthState>;\n save(server: string, state: McpOAuthState): Promise<boolean>;\n patch(server: string, patch: Partial<McpOAuthState>): Promise<boolean>;\n clear(server: string): Promise<void>;\n}\n\nexport function createMcpOAuthStore(secrets: SecretPort): McpOAuthStore {\n // \uC11C\uBC84\uBCC4 \uC4F0\uAE30 \uC9C1\uB82C\uD654 \u2014 patch() \uB294 \uD558\uB098\uC758 \uD56D\uBAA9\uC5D0 \uB300\uD55C load-modify-save \uB77C,\n // \uC870\uC6A9\uD55C \uAC31\uC2E0\uACFC \uB300\uD654\uD615 \uC778\uC99D\uC774 \uACB9\uCE58\uBA74 \uC11C\uB85C\uC758 \uD544\uB4DC\uB97C \uC9C0\uC6B4\uB2E4. \uC2E4\uC81C\uB85C \uB0AC\uB358 \uC77C\uC774\uB2E4.\n const writeChain = new Map<string, Promise<unknown>>();\n\n const store: McpOAuthStore = {\n async load(server) {\n return parseObject<McpOAuthState>(await secrets.get(MCP_OAUTH_PREFIX + server), {});\n },\n async save(server, state) {\n return secrets.set(MCP_OAUTH_PREFIX + server, JSON.stringify(state));\n },\n async patch(server, patch) {\n const prev = writeChain.get(server) ?? Promise.resolve();\n const next = prev.then(async () => {\n const cur = await store.load(server);\n return store.save(server, { ...cur, ...patch });\n });\n writeChain.set(\n server,\n next.catch(() => undefined),\n );\n return next;\n },\n async clear(server) {\n await secrets.set(MCP_OAUTH_PREFIX + server, null);\n writeChain.delete(server);\n },\n };\n return store;\n}\n", "/**\n * \uC5D4\uC9C4\uC774 \uD638\uC2A4\uD2B8\uC5D0\uAC8C \uBC1B\uC740 \uAC83\uC744 \uD55C \uBC88 \uBD99\uC5EC \uB450\uB294 \uC790\uB9AC.\n *\n * \uC571\uC740 \uC2DC\uC791\uD560 \uB54C `bindHost(...)` \uB97C \uD55C \uBC88 \uBD80\uB974\uACE0, \uC5D4\uC9C4 \uC548\uC758 \uBAA8\uB4C8\uB4E4\uC740\n * `hostPorts()` \uB85C \uAEBC\uB0B4 \uC4F4\uB2E4. \uD30C\uB77C\uBBF8\uD130\uB85C \uAFF0\uC9C0 \uC54A\uB294 \uC774\uC720\uB294 \uB450 \uAC00\uC9C0\uB2E4 \u2014 MCP\n * \uB9E4\uB2C8\uC800\uC640 OAuth \uD504\uB85C\uBC14\uC774\uB354\uAC00 \uC774\uBBF8 \uBAA8\uB4C8 \uC2F1\uAE00\uD134\uC774\uB77C \uACB0\uC774 \uAC19\uACE0, \uD3EC\uD2B8\uB97C 12\uAC1C\n * \uD638\uCD9C\uBD80\uC5D0 \uC190\uC73C\uB85C \uB118\uAE30\uBA74 \uD55C \uACF3\uB9CC \uBE60\uB728\uB838\uC744 \uB54C \uADF8 \uACBD\uB85C\uB9CC \uC870\uC6A9\uD788 \uB2E4\uB978 \uC800\uC7A5\uC18C\uB97C\n * \uC4F0\uAC8C \uB41C\uB2E4.\n *\n * **\uBD99\uC774\uC9C0 \uC54A\uACE0 \uC4F0\uBA74 \uB358\uC9C4\uB2E4.** \uBA54\uBAA8\uB9AC \uAD6C\uD604\uC73C\uB85C \uC870\uC6A9\uD788 \uD3F4\uBC31\uD558\uC9C0 \uC54A\uB294\uB2E4 \u2014 \uADF8\uB7EC\uBA74\n * \uC0AC\uC6A9\uC790\uC758 MCP \uC778\uC99D\uC774 \uB9E4\uBC88 \uC0AC\uB77C\uC9C0\uB294\uB370 \uC544\uBB34 \uC624\uB958\uB3C4 \uC548 \uBCF4\uC778\uB2E4. \uD14C\uC2A4\uD2B8\uAC00 \uBA54\uBAA8\uB9AC\uB97C\n * \uC6D0\uD558\uBA74 `bindHost(memoryPorts())` \uB97C \uBA85\uC2DC\uC801\uC73C\uB85C \uBD80\uB978\uB2E4.\n */\nimport { type HostPorts, type InteractionPort } from './ports/index';\nimport { createMcpOAuthStore, createMcpSecretStore, type McpOAuthStore, type McpSecretStore } from './secret-stores';\n\ninterface Bound {\n ports: HostPorts;\n mcpSecrets: McpSecretStore;\n mcpOAuth: McpOAuthStore;\n}\n\nlet bound: Bound | null = null;\n\nexport function bindHost(ports: HostPorts): void {\n bound = {\n ports,\n mcpSecrets: createMcpSecretStore(ports.secrets),\n mcpOAuth: createMcpOAuthStore(ports.secrets),\n };\n}\n\n/** \uD14C\uC2A4\uD2B8 \uACA9\uB9AC\uC6A9 \u2014 \uB2E4\uC74C bindHost \uAE4C\uC9C0 \uBBF8\uBC14\uC778\uB529 \uC0C1\uD0DC\uB85C \uB418\uB3CC\uB9B0\uB2E4. */\nexport function unbindHost(): void {\n bound = null;\n}\n\nexport function isHostBound(): boolean {\n return bound !== null;\n}\n\nfunction need(): Bound {\n if (!bound) {\n throw new Error(\n '@dex/engine: \uD638\uC2A4\uD2B8\uAC00 \uBD99\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4. \uC571 \uC2DC\uC791 \uC2DC bindHost({ secrets, config, paths }) \uB97C \uD55C \uBC88 \uD638\uCD9C\uD558\uC138\uC694.',\n );\n }\n return bound;\n}\n\nexport function hostPorts(): HostPorts {\n return need().ports;\n}\n\n/**\n * \uC0AC\uC6A9\uC790\uC640 \uC8FC\uACE0\uBC1B\uB294 \uB2A5\uB825. \uD638\uC2A4\uD2B8\uAC00 \uC548 \uC92C\uC73C\uBA74 \uBE48 \uAC1D\uCCB4\uB2E4 \u2014 \uAC01 \uB3C4\uAD6C\uAC00 \uC790\uAE30 \uB2A5\uB825\uB9CC\n * \uD655\uC778\uD558\uACE0 \uBBF8\uC9C0\uC6D0\uC744 \uC54C\uB9B0\uB2E4. \uC5EC\uAE30\uC11C \uD1B5\uC9F8\uB85C \uB358\uC9C0\uBA74 \uD074\uB9BD\uBCF4\uB4DC\uAC00 \uC5C6\uB294 \uD638\uC2A4\uD2B8\uC5D0\uC11C\n * \uBB34\uAD00\uD55C \uB3C4\uAD6C\uAE4C\uC9C0 \uC8FD\uB294\uB2E4.\n */\nexport function interaction(): InteractionPort {\n return need().ports.interaction ?? {};\n}\n\n/**\n * MCP \uC2DC\uD06C\uB9BF\u00B7OAuth \uC800\uC7A5\uC18C.\n *\n * \uD568\uC218\uAC00 \uC544\uB2C8\uB77C **\uAC1D\uCCB4**\uB85C \uB178\uCD9C\uD55C\uB2E4 \u2014 \uAE30\uC874 \uD638\uCD9C\uBD80\uAC00 `mcpOAuthStore.patch(...)`\n * \uD615\uD0DC\uB77C, \uC5EC\uAE30\uC11C \uD615\uD0DC\uB97C \uBC14\uAFB8\uBA74 12\uACF3\uC744 \uACE0\uCCD0\uC57C \uD558\uACE0 \uADF8\uC911 \uD558\uB098\uB97C \uBE60\uB728\uB9AC\uAE30 \uC27D\uB2E4.\n * \uC811\uADFC \uC2DC\uC810\uC5D0 \uBC14\uC778\uB529\uC744 \uD655\uC778\uD558\uBBC0\uB85C import \uC21C\uC11C\uC5D0 \uC601\uD5A5\uBC1B\uC9C0 \uC54A\uB294\uB2E4.\n */\nexport const mcpSecretStore: McpSecretStore = {\n save: (s, v) => need().mcpSecrets.save(s, v),\n get: (s) => need().mcpSecrets.get(s),\n clear: (s) => need().mcpSecrets.clear(s),\n};\n\nexport const mcpOAuthStore: McpOAuthStore = {\n load: (s) => need().mcpOAuth.load(s),\n save: (s, v) => need().mcpOAuth.save(s, v),\n patch: (s, p) => need().mcpOAuth.patch(s, p),\n clear: (s) => need().mcpOAuth.clear(s),\n};\n", "/**\n * MCP secret handling (G8a). Secrets in an MCP server config \u2014 stdio `env`\n * values (API tokens) and http/sse `headers` values (Authorization) \u2014 must NOT\n * be written to the plaintext connector.json. Instead we keep the KEY names in\n * config (so the UI can show structure) with the VALUES redacted, and store the\n * real values in the encrypted keychain (see keychain.ts `mcpSecretStore`).\n *\n * All functions here are pure and unit-tested; the async keychain/config wiring\n * lives in index.ts (save) and mcp-manager.ts (connect).\n */\nimport type { McpServerConfig } from './mcp-types';\nimport type { McpServerSecrets } from './mcp-types';\n\ntype Kv = Record<string, string>;\n\n/** Keep keys, blank every value \u2014 the config-safe (non-secret) shape. */\nexport function redactKv(o?: Kv): Kv | undefined {\n if (!o) return undefined;\n const out: Kv = {};\n for (const k of Object.keys(o)) out[k] = '';\n return Object.keys(out).length ? out : undefined;\n}\n\n/** Merge UI input over stored secrets: a non-empty incoming value is a new/\n * changed secret; an empty incoming value keeps the stored one. Keys absent\n * from `incoming` are dropped (the user removed them). */\nexport function mergeSecretKv(stored?: Kv, incoming?: Kv): Kv | undefined {\n if (!incoming) return undefined;\n const out: Kv = {};\n for (const [k, v] of Object.entries(incoming)) {\n out[k] = typeof v === 'string' && v.length ? v : stored?.[k] ?? '';\n }\n return Object.keys(out).length ? out : undefined;\n}\n\n/** Resolve real values for connect/test. A NON-EMPTY config value wins \u2014 it is\n * either a freshly-typed value (test-before-save) or pre-migration plaintext;\n * an empty ('') config value is a redaction, so the stored secret is used. */\nexport function resolveSecretKv(configVals?: Kv, secretVals?: Kv): Kv | undefined {\n const keys = new Set([...Object.keys(configVals || {}), ...Object.keys(secretVals || {})]);\n if (!keys.size) return undefined;\n const out: Kv = {};\n for (const k of keys) {\n const cfg = configVals?.[k];\n const v = cfg && cfg.length ? cfg : secretVals?.[k] || '';\n if (v) out[k] = v;\n }\n return Object.keys(out).length ? out : undefined;\n}\n\nexport interface SplitResult {\n /** Config-safe copy (secret values redacted to ''). */\n redacted: McpServerConfig;\n /** Real secrets to persist in the keychain. */\n secrets: McpServerSecrets;\n}\n\n/** Split one server config into (config-safe, secrets), merging with previously\n * stored secrets so unchanged (redacted) fields keep their stored values. */\nexport function splitServerSecrets(\n incoming: McpServerConfig,\n stored?: McpServerSecrets | null,\n): SplitResult {\n const env = mergeSecretKv(stored?.env ?? undefined, incoming.env);\n const headers = mergeSecretKv(stored?.headers ?? undefined, incoming.headers);\n const redacted: McpServerConfig = { ...incoming };\n if (env) redacted.env = redactKv(env);\n else delete redacted.env;\n if (headers) redacted.headers = redactKv(headers);\n else delete redacted.headers;\n return { redacted, secrets: { env, headers } };\n}\n\n/** Rehydrate a config with real secret values for a live connect. */\nexport function withResolvedSecrets(cfg: McpServerConfig, secrets?: McpServerSecrets | null): McpServerConfig {\n const env = resolveSecretKv(cfg.env, secrets?.env ?? undefined);\n const headers = resolveSecretKv(cfg.headers, secrets?.headers ?? undefined);\n const out: McpServerConfig = { ...cfg };\n if (env) out.env = env;\n else delete out.env;\n if (headers) out.headers = headers;\n else delete out.headers;\n return out;\n}\n", "/**\n * MCP OAuth 2.1 (PKCE) support (G8b).\n *\n * An http/sse MCP server with `auth: 'oauth'` gets an OAuthClientProvider wired\n * into its transport. The provider stores tokens/client-info/PKCE-verifier in the\n * encrypted keychain (mcpOAuthStore). Two modes:\n *\n * \u2022 SILENT (regular connect, mcp-manager): loads existing tokens and lets the\n * SDK auto-refresh them. If interactive authorization is required, it does\n * NOT pop a browser \u2014 the connect simply fails with \"authorization required\"\n * and the UI shows an \"Authorize\" button.\n * \u2022 INTERACTIVE (authorizeMcpServer, user clicks Authorize): opens the system\n * browser to the authorization URL, runs a loopback HTTP server to catch the\n * redirect, and completes the code exchange via transport.finishAuth().\n *\n * The SDK (@modelcontextprotocol/sdk) does discovery, dynamic client\n * registration, PKCE, code exchange and refresh; we only provide storage + the\n * browser/loopback glue.\n */\nimport http from 'node:http';\nimport { randomBytes } from 'node:crypto';\nimport type { AddressInfo } from 'node:net';\nimport type { McpServerConfig } from './mcp-types';\nimport { interaction, mcpOAuthStore } from './host';\nimport type { McpHttpFetch } from './mcp-manager';\n\n// The SDK's OAuth types are structural; we keep them loose to avoid a hard\n// compile dep on deep SDK type paths (the runtime shapes are what matter).\ntype OAuthClientProviderLike = {\n get redirectUrl(): string;\n get clientMetadata(): Record<string, unknown>;\n state?(): string | undefined;\n clientInformation(): Promise<unknown> | unknown;\n saveClientInformation(info: unknown): Promise<void> | void;\n tokens(): Promise<unknown> | unknown;\n saveTokens(tokens: unknown): Promise<void> | void;\n redirectToAuthorization(url: URL): Promise<void> | void;\n saveCodeVerifier(v: string): Promise<void> | void;\n codeVerifier(): Promise<string> | string;\n invalidateCredentials?(scope: 'all' | 'client' | 'tokens' | 'verifier'): Promise<void> | void;\n};\n\nconst CLIENT_NAME = 'XGen Dex';\nconst AUTH_TIMEOUT_MS = 5 * 60_000;\n\n/** An OAuthClientProvider backed by the encrypted keychain. */\nexport class ConnectorOAuthProvider implements OAuthClientProviderLike {\n constructor(\n private readonly server: string,\n private readonly port: number,\n private readonly interactive: boolean,\n private readonly onRedirect?: (url: URL) => Promise<void> | void,\n private readonly stateValue?: string,\n ) {}\n\n get redirectUrl(): string {\n return `http://127.0.0.1:${this.port}/callback`;\n }\n\n /** OAuth2 state (CSRF) \u2014 only the interactive flow sets one; verified at the\n * loopback callback before the code is accepted. */\n state(): string | undefined {\n return this.stateValue;\n }\n\n get clientMetadata(): Record<string, unknown> {\n return {\n client_name: CLIENT_NAME,\n redirect_uris: [this.redirectUrl],\n grant_types: ['authorization_code', 'refresh_token'],\n response_types: ['code'],\n token_endpoint_auth_method: 'none',\n };\n }\n\n async clientInformation(): Promise<unknown> {\n return (await mcpOAuthStore.load(this.server)).clientInformation;\n }\n async saveClientInformation(info: unknown): Promise<void> {\n await mcpOAuthStore.patch(this.server, { clientInformation: info });\n }\n async tokens(): Promise<unknown> {\n return (await mcpOAuthStore.load(this.server)).tokens;\n }\n async saveTokens(tokens: unknown): Promise<void> {\n await mcpOAuthStore.patch(this.server, { tokens });\n }\n async saveCodeVerifier(v: string): Promise<void> {\n await mcpOAuthStore.patch(this.server, { codeVerifier: v });\n }\n async codeVerifier(): Promise<string> {\n const s = await mcpOAuthStore.load(this.server);\n if (!s.codeVerifier) throw new Error('PKCE code_verifier \uAC00 \uC5C6\uC2B5\uB2C8\uB2E4 (\uC7AC\uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4).');\n return s.codeVerifier;\n }\n\n async redirectToAuthorization(url: URL): Promise<void> {\n // Silent connects must never pop a browser; failing with UnauthorizedError\n // (thrown by the SDK after this returns) is the intended \"needs auth\" signal.\n if (!this.interactive) return;\n if (this.onRedirect) await this.onRedirect(url);\n }\n\n /** SDK self-heal: on InvalidGrant/registration errors the SDK asks us to drop\n * the stale credential so the next attempt re-registers / re-authorizes. */\n async invalidateCredentials(scope: 'all' | 'client' | 'tokens' | 'verifier'): Promise<void> {\n if (scope === 'all') {\n await mcpOAuthStore.clear(this.server);\n return;\n }\n await mcpOAuthStore.patch(this.server, {\n ...(scope === 'client' ? { clientInformation: undefined } : {}),\n ...(scope === 'tokens' ? { tokens: undefined } : {}),\n ...(scope === 'verifier' ? { codeVerifier: undefined } : {}),\n });\n }\n}\n\n/** A silent provider for regular connects (loads tokens, auto-refresh, no UI). */\nexport function makeSilentOAuthProvider(server: string): ConnectorOAuthProvider {\n return new ConnectorOAuthProvider(server, 0, false);\n}\n\nfunction escapeHtml(s: string): string {\n return String(s).replace(/[&<>\"']/g, (c) =>\n ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '\"': '&quot;', \"'\": '&#39;' })[c] as string,\n );\n}\n\ninterface Loopback {\n server: http.Server;\n port: number;\n code: Promise<string>;\n cancel: () => void;\n}\n\n/** Loopback listener that resolves with the authorization code once the browser\n * redirects back. `expectedState` is verified (CSRF / code-injection guard). */\nfunction startLoopback(expectedState: string): Promise<Loopback> {\n return new Promise((resolve, reject) => {\n let resolveCode!: (c: string) => void;\n let rejectCode!: (e: Error) => void;\n let settled = false;\n const code = new Promise<string>((res, rej) => {\n resolveCode = (c) => {\n settled = true;\n res(c);\n };\n rejectCode = (e) => {\n settled = true;\n rej(e);\n };\n });\n // Never leave this promise unhandled (early-return paths don't await it).\n code.catch(() => {});\n const server = http.createServer((req, res) => {\n let u: URL;\n try {\n u = new URL(req.url || '/', 'http://127.0.0.1');\n } catch {\n res.writeHead(400);\n res.end();\n return;\n }\n if (u.pathname !== '/callback') {\n res.writeHead(404);\n res.end();\n return;\n }\n const authCode = u.searchParams.get('code');\n const err = u.searchParams.get('error');\n const gotState = u.searchParams.get('state') || '';\n // CSRF / auth-code injection: the state must match what we issued.\n const stateOk = gotState === expectedState;\n const ok = !!authCode && !err && stateOk;\n const message = ok\n ? '\uC774 \uCC3D\uC744 \uB2EB\uACE0 XGen Dex\uB85C \uB3CC\uC544\uAC00\uC138\uC694.'\n : !stateOk\n ? '\uBCF4\uC548 \uAC80\uC99D(state)\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4. \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.'\n : escapeHtml(err || '\uC778\uAC00 \uCF54\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.');\n res.writeHead(200, { 'content-type': 'text/html; charset=utf-8' });\n res.end(\n `<!doctype html><html><body style=\"font-family:system-ui,sans-serif;padding:3rem;text-align:center\">` +\n `<h2>${ok ? '\uC778\uC99D \uC644\uB8CC' : '\uC778\uC99D \uC2E4\uD328'}</h2><p>${message}</p></body></html>`,\n );\n if (ok) resolveCode(authCode as string);\n else if (!stateOk) rejectCode(new Error('OAuth state \uBD88\uC77C\uCE58 (CSRF \uBC29\uC9C0).'));\n else rejectCode(new Error(err || '\uC778\uAC00 \uCF54\uB4DC\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.'));\n });\n server.on('error', reject);\n server.listen(0, '127.0.0.1', () => {\n const addr = server.address() as AddressInfo | null;\n const port = addr && typeof addr === 'object' ? addr.port : 0;\n const timer = setTimeout(() => {\n if (!settled) rejectCode(new Error('\uC778\uC99D \uC2DC\uAC04 \uCD08\uACFC (5\uBD84).'));\n }, AUTH_TIMEOUT_MS);\n void code.finally(() => clearTimeout(timer));\n const cancel = () => {\n clearTimeout(timer);\n if (!settled) rejectCode(new Error('\uC778\uC99D\uC774 \uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.'));\n };\n resolve({ server, port, code, cancel });\n });\n });\n}\n\nfunction withTimeout<T>(p: Promise<T>, ms: number, label: string): Promise<T> {\n return new Promise<T>((resolve, reject) => {\n const timer = setTimeout(() => reject(new Error(`${label} \uC2DC\uAC04 \uCD08\uACFC (${Math.round(ms / 1000)}\uCD08).`)), ms);\n p.then(\n (v) => {\n clearTimeout(timer);\n resolve(v);\n },\n (e) => {\n clearTimeout(timer);\n reject(e);\n },\n );\n });\n}\n\nasync function loadTransportsAndError(): Promise<{\n SSEClientTransport: any;\n StreamableHTTPClientTransport: any;\n UnauthorizedError: any;\n Client: any;\n}> {\n const [{ Client }, { StreamableHTTPClientTransport }, sse, auth] = await Promise.all([\n import('@modelcontextprotocol/sdk/client/index.js'),\n import('@modelcontextprotocol/sdk/client/streamableHttp.js'),\n import('@modelcontextprotocol/sdk/client/sse.js').catch(() => null),\n import('@modelcontextprotocol/sdk/client/auth.js').catch(() => null),\n ]);\n return {\n Client,\n StreamableHTTPClientTransport,\n SSEClientTransport: (sse as { SSEClientTransport?: unknown } | null)?.SSEClientTransport ?? null,\n UnauthorizedError: (auth as { UnauthorizedError?: unknown } | null)?.UnauthorizedError ?? Error,\n };\n}\n\n/** Attach an OAuth provider to a transport option bag when the server uses OAuth. */\nexport function oauthTransportOptions(\n cfg: McpServerConfig,\n base: { requestInit?: RequestInit; fetch?: McpHttpFetch },\n): { requestInit?: RequestInit; fetch?: McpHttpFetch; authProvider?: unknown } {\n if (cfg.auth !== 'oauth') return base;\n return { ...base, authProvider: makeSilentOAuthProvider(cfg.name) as unknown };\n}\n\n/** Run the interactive OAuth authorization for a server. Opens the browser, runs\n * a loopback listener, and completes the token exchange. */\n/** Map cryptic SDK OAuth errors to actionable Korean guidance (no-DCR, discovery). */\nfunction friendlyOAuthError(e: unknown): string {\n const msg = e instanceof Error ? e.message : String(e);\n const low = msg.toLowerCase();\n if (low.includes('registration') || low.includes('register client') || low.includes('dynamic client')) {\n return `\uC774 \uC11C\uBC84\uB294 \uB3D9\uC801 \uD074\uB77C\uC774\uC5B8\uD2B8 \uB4F1\uB85D(DCR)\uC744 \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 \uAC83\uC73C\uB85C \uBCF4\uC785\uB2C8\uB2E4. \uC0AC\uC804 \uB4F1\uB85D\uB41C client_id \uB97C \uC694\uAD6C\uD558\uB294 \uC11C\uBC84\uB294 \uD604\uC7AC \uCEE4\uB125\uD130\uB85C \uC790\uB3D9 \uC778\uAC00\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. (\uC6D0\uC778: ${msg})`;\n }\n if (low.includes('.well-known') || low.includes('metadata') || low.includes('discover')) {\n return `OAuth \uBA54\uD0C0\uB370\uC774\uD130(.well-known)\uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4 \u2014 \uC774 URL \uC774 OAuth \uB97C \uC9C0\uC6D0\uD558\uB294 MCP \uC5D4\uB4DC\uD3EC\uC778\uD2B8\uAC00 \uB9DE\uB294\uC9C0 \uD655\uC778\uD558\uC138\uC694. (\uC6D0\uC778: ${msg})`;\n }\n return msg;\n}\n\nexport async function authorizeMcpServer(\n cfg: McpServerConfig,\n opts: { fetch?: McpHttpFetch; openExternal?: (url: string) => Promise<void> } = {},\n): Promise<{ ok: boolean; error?: string }> {\n if (cfg.transport !== 'sse' && cfg.transport !== 'http') {\n return { ok: false, error: 'OAuth \uB294 http/sse \uC11C\uBC84\uC5D0\uC11C\uB9CC \uC9C0\uC6D0\uB429\uB2C8\uB2E4.' };\n }\n if (!cfg.url) return { ok: false, error: '\uC11C\uBC84 URL \uC774 \uC5C6\uC2B5\uB2C8\uB2E4.' };\n\n // \uD638\uCD9C\uBD80\uAC00 \uBA85\uC2DC\uC801\uC73C\uB85C \uC900 \uAC83 > \uD638\uC2A4\uD2B8 \uD3EC\uD2B8. \uD3EC\uD2B8\uB3C4 \uC5C6\uC73C\uBA74 \uC778\uC99D\uC744 \uC2DC\uC791\uD558\uC9C0\n // \uC54A\uB294\uB2E4 \u2014 \uBE0C\uB77C\uC6B0\uC800\uB97C \uBABB \uC5EC\uB294\uB370 \uC2DC\uC791\uD558\uBA74 \uC0AC\uC6A9\uC790\uB294 \uC544\uBB34 \uC77C\uB3C4 \uC548 \uC77C\uC5B4\uB098\uB294 \uD654\uBA74\uC744\n // \uBCF4\uBA70 \uAE30\uB2E4\uB9AC\uAC8C \uB41C\uB2E4.\n const openExternal = opts.openExternal ?? interaction().openExternal;\n if (!openExternal) {\n return { ok: false, error: '\uC774 \uD638\uC2A4\uD2B8\uC5D0\uC11C\uB294 \uBE0C\uB77C\uC6B0\uC800\uB97C \uC5F4 \uC218 \uC5C6\uC5B4 OAuth \uC778\uC99D\uC744 \uC2DC\uC791\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.' };\n }\n\n const state = randomBytes(24).toString('base64url');\n const loop = await startLoopback(state);\n try {\n const { SSEClientTransport, StreamableHTTPClientTransport, UnauthorizedError, Client } =\n await loadTransportsAndError();\n // Fresh authorization: drop old PKCE/tokens AND client registration so DCR\n // re-runs with THIS session's loopback redirect_uri (avoids a stale port).\n await mcpOAuthStore.patch(cfg.name, {\n codeVerifier: undefined,\n tokens: undefined,\n clientInformation: undefined,\n });\n\n const provider = new ConnectorOAuthProvider(\n cfg.name,\n loop.port,\n true,\n (url) => openExternal(url.toString()),\n state,\n );\n const url = new URL(cfg.url);\n const mkTransport = () =>\n cfg.transport === 'sse'\n ? SSEClientTransport\n ? new SSEClientTransport(url, { authProvider: provider, fetch: opts.fetch })\n : null\n : new StreamableHTTPClientTransport(url, { authProvider: provider, fetch: opts.fetch });\n\n const transport = mkTransport();\n if (!transport) return { ok: false, error: '\uC774 \uBE4C\uB4DC\uC5D0\uC11C SSE \uC804\uC1A1\uC744 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.' };\n\n const client = new Client({ name: 'xgen-dex', version: '1.0.0' }, { capabilities: {} });\n try {\n // If we already have valid/refreshable tokens, this connects straight away.\n // Bounded so a stalled discovery/registration can't hang forever.\n await withTimeout(client.connect(transport), 60_000, 'OAuth \uC5F0\uACB0');\n await client.close();\n return { ok: true };\n } catch (e) {\n if (!(e instanceof UnauthorizedError)) {\n return { ok: false, error: friendlyOAuthError(e) };\n }\n // The browser was opened by redirectToAuthorization \u2014 wait for the code.\n }\n\n const code = await loop.code; // already bounded by the loopback 5-min timer\n await withTimeout(transport.finishAuth(code), 60_000, 'OAuth \uD1A0\uD070 \uAD50\uD658');\n // Verify with a fresh transport that now has tokens.\n const verifyTransport = mkTransport();\n const verifyClient = new Client({ name: 'xgen-dex', version: '1.0.0' }, { capabilities: {} });\n try {\n if (verifyTransport) {\n await withTimeout(verifyClient.connect(verifyTransport), 60_000, 'OAuth \uAC80\uC99D');\n await verifyClient.close();\n }\n } catch {\n /* token saved; a verify hiccup shouldn't fail the authorization */\n }\n return { ok: true };\n } catch (e) {\n return { ok: false, error: friendlyOAuthError(e) };\n } finally {\n loop.cancel();\n loop.server.close();\n }\n}\n\n/** Whether a server currently has stored OAuth tokens. */\nexport async function hasOAuthTokens(server: string): Promise<boolean> {\n const s = await mcpOAuthStore.load(server);\n return !!s.tokens;\n}\n\n/** Forget a server's OAuth tokens/registration (user \"sign out\"). */\nexport async function clearOAuth(server: string): Promise<void> {\n await mcpOAuthStore.clear(server);\n}\n", "/**\n * MCPManager \u2014 hosts MCP CLIENTS to the user's LOCAL MCP servers so the XGEN\n * agents can use them THROUGH this connector (the conduit). Lives in the\n * Electron MAIN process (only main can spawn stdio subprocesses). The bridge\n * (mcp-bridge.ts) advertises the aggregated tool catalog to the XGEN backend over\n * the `/api/tools/ws/connector-mcp/{user_id}` WebSocket and answers `mcp_call`\n * frames by dispatching to `callTool` here.\n *\n * The @modelcontextprotocol/sdk is lazy-imported so a build that can't resolve\n * it still boots \u2014 MCP just reports unavailable. Ported from geny-connector.\n */\nimport type { McpServerConfig } from './mcp-types';\nimport { mcpSecretStore } from './host';\nimport { withResolvedSecrets } from './mcp-secrets';\nimport { oauthTransportOptions } from './mcp-oauth';\nimport { homedir } from 'os';\nimport {\n augmentedPath,\n buildChildEnv,\n diagnoseMissing,\n ExecNotFoundError,\n resetPathCache,\n resolveExecutable,\n} from './exec-resolve';\n\nexport interface McpToolSchema {\n name: string;\n description?: string;\n inputSchema?: Record<string, unknown>;\n}\n\n/** What we advertise to the backend (per configured, enabled server). */\nexport interface McpServerAdvert {\n name: string;\n connected: boolean;\n error?: string;\n tools: McpToolSchema[];\n}\n\n/** A flat tool entry advertised in the bridge `hello` frame. */\nexport interface AdvertisedTool {\n server: string;\n name: string;\n description?: string;\n inputSchema?: Record<string, unknown>;\n}\n\n/** MCP SDK\uC758 Streamable HTTP \uC804\uC1A1\uC5D0 \uC8FC\uC785\uD558\uB294 fetch \uD615\uD0DC. */\nexport type McpHttpFetch = (url: string | URL, init?: RequestInit) => Promise<Response>;\n\nexport interface McpNetworkOptions {\n httpFetch?: McpHttpFetch;\n allowPrivateCertificate?: boolean;\n}\n\ninterface ServerState {\n config: McpServerConfig;\n /** \uAE30\uB3D9 \uC911 \uC9C4\uD589 \uC0C1\uD669\uC744 \uD758\uB824\uBCF4\uB0BC \uACF3 (\uD14C\uC2A4\uD2B8 \uD654\uBA74 \uC804\uC6A9). */\n onProgress?: (lines: string[]) => void;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n client: any | null;\n tools: McpToolSchema[];\n error?: string;\n connecting?: Promise<void>;\n}\n\n/** Quote-aware split of a command line into [command, ...args]. */\nfunction tokenize(cmd: string): string[] {\n const m = cmd.match(/(?:[^\\s\"']+|\"[^\"]*\"|'[^']*')+/g) || [];\n return m.map((t) => t.replace(/^[\"']|[\"']$/g, ''));\n}\n\n/**\n * \uC790\uC2DD \uD504\uB85C\uC138\uC2A4 stderr \uC758 \uB9C8\uC9C0\uB9C9 \uBA87 \uC904\uC744 \uBF51\uB294\uB2E4.\n *\n * MCP \uC11C\uBC84\uAC00 \uAE30\uB3D9\uC5D0 \uC2E4\uD328\uD558\uBA74 SDK \uB294 `MCP error -32000: Connection closed` \uB9CC\n * \uC900\uB2E4 \u2014 \uC9C4\uC9DC \uC6D0\uC778(\uD328\uD0A4\uC9C0 \uC5C6\uC74C, ImportError, \uC778\uC99D \uC2E4\uD328\u2026)\uC740 \uC804\uBD80 \uC790\uC2DD\uC758\n * stderr \uC5D0 \uC788\uB2E4. \uADF8\uAC78 \uBC84\uB9AC\uBA74 \uC0AC\uC6A9\uC790\uB294 \uACE0\uCE60 \uBC29\uBC95\uC774 \uC5C6\uB2E4.\n */\nexport function tailLines(text: string, maxLines = 12, maxChars = 200): string[] {\n return text\n .split(/\\r?\\n/)\n .map((l) => l.replace(/\\s+$/, ''))\n .filter(Boolean)\n .slice(-maxLines)\n .map((l) => (l.length > maxChars ? `${l.slice(0, maxChars)}\u2026` : l));\n}\n\n/** \uAE30\uB3D9 \uC2E4\uD328 \uC6D0\uC778\uC744 UI \uB85C \uC2E4\uC5B4 \uB098\uB974\uB294 \uC624\uB958 (stderr \uAF2C\uB9AC\uB97C hints \uB85C). */\nexport class McpStartError extends Error {\n readonly hints: string[];\n constructor(message: string, hints: string[]) {\n super(message);\n this.name = 'McpStartError';\n this.hints = hints;\n }\n}\n\n/** stderr \uC218\uC9D1\uAE30 \u2014 \uAF2C\uB9AC \uBB38\uC790\uC5F4 + \uB9C8\uC9C0\uB9C9 \uCD9C\uB825 \uC2DC\uAC01(\uC0B4\uC544\uC788\uC74C\uC758 \uC99D\uAC70). */\ninterface StderrTap {\n text: () => string;\n /** \uB9C8\uC9C0\uB9C9\uC73C\uB85C \uBB54\uAC00 \uCD9C\uB825\uD55C \uC2DC\uAC01 (Date.now). \uCD9C\uB825\uC774 \uC5C6\uC73C\uBA74 \uC0DD\uC131 \uC2DC\uAC01. */\n lastAt: () => number;\n}\n\n/**\n * transport.stderr \uB97C \uC0C1\uD55C\uC120(\uAE30\uBCF8 64KB)\uAE4C\uC9C0\uB9CC \uBAA8\uC544 \uB454\uB2E4.\n *\n * \uAF2C\uB9AC \uBB38\uC790\uC5F4\uBFD0 \uC544\uB2C8\uB77C **\uB9C8\uC9C0\uB9C9 \uCD9C\uB825 \uC2DC\uAC01**\uB3C4 \uB0A8\uAE34\uB2E4. \uCCAB \uC2E4\uD589 \uB54C uvx/npx \uB294\n * \uC778\uD130\uD504\uB9AC\uD130\u00B7\uC758\uC874\uC131\uC744 \uC218\uC2ED MB \uB0B4\uB824\uBC1B\uB290\uB77C \uC624\uB798 \uAC78\uB9AC\uB294\uB370, \uADF8\uB3D9\uC548 \uC9C4\uD589 \uC0C1\uD669\uC744\n * stderr \uB85C \uACC4\uC18D \uBC49\uB294\uB2E4 \u2014 \uC774 \uC2DC\uAC01\uC774 \"\uBA48\uCD98 \uAC8C \uC544\uB2C8\uB77C \uC77C\uD558\uB294 \uC911\"\uC758 \uC99D\uAC70\uB2E4.\n */\nfunction collectStderr(\n transport: { stderr?: NodeJS.ReadableStream | null },\n onData?: (tail: string[]) => void,\n cap = 64 * 1024,\n): StderrTap {\n let buf = '';\n let last = Date.now();\n const stream = transport.stderr;\n stream?.on?.('data', (chunk: Buffer | string) => {\n buf += String(chunk);\n if (buf.length > cap) buf = buf.slice(-cap); // \uBB34\uD55C \uB85C\uADF8 \uC11C\uBC84 \uBC29\uC5B4\n last = Date.now();\n onData?.(tailLines(buf));\n });\n return { text: () => buf, lastAt: () => last };\n}\n\n/** \uC9C4\uD589 \uC0C1\uD669\uC744 \uCD5C\uB300 `everyMs` \uAC04\uACA9\uC73C\uB85C\uB9CC \uD758\uB824\uBCF4\uB0B8\uB2E4 (\uB2E4\uC6B4\uB85C\uB4DC \uB85C\uADF8\uB294 \uBE60\uB974\uB2E4). */\nexport function throttle<T>(fn: (v: T) => void, everyMs: number): (v: T) => void {\n let at = 0;\n let pending: ReturnType<typeof setTimeout> | null = null;\n let latest: T;\n return (v: T) => {\n latest = v;\n const now = Date.now();\n if (now - at >= everyMs) {\n at = now;\n fn(latest);\n return;\n }\n if (pending) return;\n pending = setTimeout(() => {\n pending = null;\n at = Date.now();\n fn(latest);\n }, everyMs - (now - at));\n };\n}\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nlet _sdk: any = null;\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nasync function loadSdk(): Promise<any> {\n if (_sdk) return _sdk;\n const [{ Client }, { StdioClientTransport }, { StreamableHTTPClientTransport }, sse, types] =\n await Promise.all([\n import('@modelcontextprotocol/sdk/client/index.js'),\n import('@modelcontextprotocol/sdk/client/stdio.js'),\n import('@modelcontextprotocol/sdk/client/streamableHttp.js'),\n // \uB808\uAC70\uC2DC HTTP+SSE \uC804\uC1A1 \u2014 \uC5C6\uB294 \uBE4C\uB4DC\uC77C \uC218 \uC788\uC5B4 \uC548\uC804\uD558\uAC8C \uAC10\uC2FC\uB2E4.\n import('@modelcontextprotocol/sdk/client/sse.js').catch(() => null),\n // tools/list_changed \uC54C\uB9BC \uC2A4\uD0A4\uB9C8 \u2014 \uAD6C\uBC84\uC804 SDK \uC5D0 \uC5C6\uC744 \uC218 \uC788\uC5B4 \uC548\uC804\uD558\uAC8C \uAC10\uC2FC\uB2E4.\n import('@modelcontextprotocol/sdk/types.js').catch(() => null),\n ]);\n _sdk = {\n Client,\n StdioClientTransport,\n StreamableHTTPClientTransport,\n SSEClientTransport:\n (sse as { SSEClientTransport?: unknown } | null)?.SSEClientTransport ?? null,\n ToolListChangedNotificationSchema:\n (types as { ToolListChangedNotificationSchema?: unknown } | null)\n ?.ToolListChangedNotificationSchema ?? null,\n };\n return _sdk;\n}\n\nasync function withTimeout<T>(p: Promise<T>, ms: number, label: string): Promise<T> {\n let t: ReturnType<typeof setTimeout>;\n const timeout = new Promise<never>((_, rej) => {\n t = setTimeout(() => rej(new Error(`${label} timed out after ${ms}ms`)), ms);\n });\n try {\n return await Promise.race([p, timeout]);\n } finally {\n clearTimeout(t!);\n }\n}\n\n/** \uC9C4\uD589\uC774 \uBA48\uCD98 \uAC83\uC73C\uB85C \uBCFC \uBB34\uCD9C\uB825 \uC2DC\uAC04, \uADF8\uB9AC\uACE0 \uADF8\uB798\uB3C4 \uB118\uC9C0 \uC54A\uC744 \uC0C1\uD55C. */\nexport const IDLE_TIMEOUT_MS = 90_000;\nexport const MAX_START_MS = 15 * 60_000;\n\n/**\n * *\uC77C\uD558\uB294 \uC911\uC774\uBA74 \uAE30\uB2E4\uB9AC\uACE0, \uBA48\uCDC4\uC73C\uBA74 \uB04A\uB294\uB2E4.*\n *\n * \uACE0\uC815 20\uCD08\uB85C\uB294 \uCCAB \uC2E4\uD589\uC774 \uC808\uB300 \uC131\uACF5\uD560 \uC218 \uC5C6\uC5C8\uB2E4 \u2014 `uvx mcp-atlassian` \uC740\n * CPython(24MB)\uACFC lxml\u00B7cryptography \uB4F1\uC744 \uCC98\uC74C \uD55C \uBC88 \uB0B4\uB824\uBC1B\uB294\uB2E4. \uADF8\uB807\uB2E4\uACE0\n * \uC0C1\uC218\uB9CC \uD0A4\uC6B0\uBA74 \uC9C4\uC9DC \uBA48\uCD98 \uC11C\uBC84\uB97C \uBA87 \uBD84\uC529 \uBD99\uB4E4\uAC8C \uB41C\uB2E4.\n *\n * \uADF8\uB798\uC11C **\uB9C8\uC9C0\uB9C9 \uCD9C\uB825 \uC2DC\uAC01**\uC744 \uAE30\uC900\uC73C\uB85C \uD310\uB2E8\uD55C\uB2E4: \uBB54\uAC00 \uCC0D\uD788\uACE0 \uC788\uC73C\uBA74 \uACC4\uC18D\n * \uAE30\uB2E4\uB9AC\uACE0(\uC0C1\uD55C `maxMs`), `idleMs` \uB3D9\uC548 \uC544\uBB34 \uCD9C\uB825\uC774 \uC5C6\uC73C\uBA74 \uBA48\uCD98 \uAC83\uC73C\uB85C \uBCF8\uB2E4.\n */\nexport async function waitWhileProgressing<T>(\n p: Promise<T>,\n lastAt: () => number,\n label: string,\n idleMs = IDLE_TIMEOUT_MS,\n maxMs = MAX_START_MS,\n): Promise<T> {\n const started = Date.now();\n let timer: ReturnType<typeof setTimeout>;\n const guard = new Promise<never>((_, rej) => {\n const tick = () => {\n const idle = Date.now() - lastAt();\n const total = Date.now() - started;\n if (total >= maxMs) {\n return rej(new Error(`${label}: ${Math.round(maxMs / 60000)}\uBD84\uC744 \uB118\uACA8 \uC911\uB2E8\uD588\uC2B5\uB2C8\uB2E4`));\n }\n if (idle >= idleMs) {\n return rej(\n new Error(`${label}: ${Math.round(idleMs / 1000)}\uCD08 \uB3D9\uC548 \uC544\uBB34 \uC751\uB2F5\uC774 \uC5C6\uC5B4 \uC911\uB2E8\uD588\uC2B5\uB2C8\uB2E4`),\n );\n }\n // \uB0A8\uC740 \uC720\uD734 \uC2DC\uAC04\uB9CC\uD07C\uB9CC \uC790\uACE0 \uB2E4\uC2DC \uD655\uC778\uD55C\uB2E4 (\uCD9C\uB825\uC774 \uC624\uBA74 \uADF8\uB9CC\uD07C \uBC00\uB9B0\uB2E4).\n timer = setTimeout(tick, Math.max(250, Math.min(idleMs - idle, maxMs - total)));\n };\n timer = setTimeout(tick, Math.min(idleMs, maxMs));\n });\n try {\n return await Promise.race([p, guard]);\n } finally {\n clearTimeout(timer!);\n }\n}\n\nexport class MCPManager {\n private states = new Map<string, ServerState>();\n private httpFetch: McpHttpFetch | undefined;\n private allowPrivateCertificate = false;\n /** \uC11C\uBC84\uAC00 \uB3C4\uAD6C \uBAA9\uB85D\uC744 \uBC14\uAFB8\uAC70\uB098(list_changed) \uC8FD\uC5C8\uC744 \uB54C(onclose) \uCE74\uD0C8\uB85C\uADF8\uB97C \uB2E4\uC2DC\n * \uAD11\uACE0\uD558\uB3C4\uB85D \uD638\uCD9C\uB41C\uB2E4 (index \uAC00 bridge.refreshCatalog \uB85C \uBC30\uC120). */\n private onCatalogChange: (() => void) | undefined;\n\n /** \uCE74\uD0C8\uB85C\uADF8 \uBCC0\uACBD(\uB3C4\uAD6C \uCD94\uAC00/\uC81C\uAC70/\uC11C\uBC84 \uC885\uB8CC) \uC2DC \uC7AC\uAD11\uACE0\uD560 \uB9AC\uC2A4\uB108\uB97C \uB4F1\uB85D\uD55C\uB2E4. */\n setCatalogChangeListener(fn: (() => void) | undefined): void {\n this.onCatalogChange = fn;\n }\n\n /** Reconcile the configured server list into live state (drops removed,\n * reconnects changed configs lazily). Does NOT connect yet. */\n configure(servers: McpServerConfig[] | undefined, options: McpNetworkOptions = {}): void {\n const certificatePolicyChanged =\n this.allowPrivateCertificate !== (options.allowPrivateCertificate === true);\n this.httpFetch = options.httpFetch;\n this.allowPrivateCertificate = options.allowPrivateCertificate === true;\n const next = new Map<string, McpServerConfig>();\n for (const s of servers || []) if (s && s.name) next.set(s.name, s);\n for (const [name, st] of [...this.states]) {\n const cfg = next.get(name);\n if (\n !cfg ||\n JSON.stringify(cfg) !== JSON.stringify(st.config) ||\n (certificatePolicyChanged && (st.config.transport === 'http' || st.config.transport === 'sse'))\n ) {\n void this.disconnect(name);\n this.states.delete(name);\n }\n }\n for (const [name, cfg] of next) {\n if (!this.states.has(name)) this.states.set(name, { config: cfg, client: null, tools: [] });\n }\n }\n\n private async connect(name: string): Promise<void> {\n const st = this.states.get(name);\n if (!st) throw new Error(`unknown MCP server: ${name}`);\n if (st.client) return;\n // \uC624\uB958 \uBB38\uAD6C\uC5D0 \uB0B4\uBD80 \uC784\uC2DC \uC774\uB984(__test__\u2026)\uC774 \uC0C8\uC5B4 \uB098\uAC00\uBA74 \uC548 \uB41C\uB2E4.\n const label = name.replace(/^__test__/, '');\n if (st.connecting) return st.connecting;\n st.connecting = (async () => {\n const {\n Client,\n StdioClientTransport,\n StreamableHTTPClientTransport,\n SSEClientTransport,\n ToolListChangedNotificationSchema,\n } = await loadSdk();\n // G8a: rehydrate secret env/headers from the encrypted keychain (config.json\n // only holds redacted values). Falls back to config for pre-migration users.\n const secrets = await mcpSecretStore.get(name).catch(() => null);\n const cfg = withResolvedSecrets(st.config, secrets);\n let transport;\n let tap: StderrTap | null = null;\n if (cfg.transport === 'stdio') {\n if (!cfg.command) throw new Error('stdio server has no command');\n // args \uAC00 \uC788\uC73C\uBA74(\uD45C\uC900 JSON \uAC00\uC838\uC624\uAE30) command \uB294 \uC2E4\uD589 \uD30C\uC77C \uADF8 \uC790\uCCB4\uB2E4 \u2014\n // \uC7AC\uBD84\uD574\uD558\uC9C0 \uC54A\uC544\uC57C \uACF5\uBC31/\uB530\uC634\uD45C\uAC00 \uB4E0 \uC778\uC790\uAC00 \uADF8\uB300\uB85C \uC804\uB2EC\uB41C\uB2E4.\n const [command, ...args] = cfg.args?.length\n ? [cfg.command.trim(), ...cfg.args]\n : tokenize(cfg.command);\n if (!command) throw new Error('empty command');\n // GUI \uB85C \uC2E4\uD589\uB41C \uC571\uC740 \uB85C\uADF8\uC778 \uC178 PATH \uB97C \uC0C1\uC18D\uD558\uC9C0 \uC54A\uB294\uB2E4 \u2192 uvx/npx \uB97C\n // \uBABB \uCC3E\uC544 'spawn uvx ENOENT'. PATH \uB97C \uBCF4\uAC15\uD558\uACE0 \uC2E4\uD589 \uD30C\uC77C\uC744 **\uC808\uB300\n // \uACBD\uB85C\uB85C \uD574\uC11D**\uD574 \uB118\uAE34\uB2E4 (Windows \uB294 .cmd/.exe \uD655\uC7A5\uC790\uAE4C\uC9C0).\n let pathStr = await augmentedPath();\n let resolved = resolveExecutable(command, pathStr);\n if (!resolved) {\n // \uBC29\uAE08 \uC124\uCE58\uD588\uC744 \uC218 \uC788\uB2E4 \u2014 \uCE90\uC2DC\uB97C \uBC84\uB9AC\uACE0 \uD55C \uBC88 \uB354 (\uC571 \uC7AC\uC2DC\uC791 \uBD88\uD544\uC694).\n resetPathCache();\n pathStr = await augmentedPath();\n resolved = resolveExecutable(command, pathStr);\n }\n if (!resolved) throw new ExecNotFoundError(diagnoseMissing(command, pathStr));\n transport = new StdioClientTransport({\n command: resolved,\n args,\n env: buildChildEnv(pathStr, cfg.env),\n // \uC791\uC5C5 \uB514\uB809\uD130\uB9AC\uB97C \uD648\uC73C\uB85C \uACE0\uC815\uD55C\uB2E4. \uC548 \uC815\uD558\uBA74 \uC571\uC744 \uC5B4\uB5BB\uAC8C \uB744\uC6E0\uB294\uC9C0\uC5D0\n // \uB530\uB77C(\uD130\uBBF8\uB110 vs Finder/\uC2DC\uC791 \uBA54\uB274) `/` \uB098 `C:\\Windows\\System32` \uAC00\n // \uB418\uC5B4 \uC0C1\uB300 \uACBD\uB85C \uC778\uC790\uC640 \uCE90\uC2DC \uC704\uCE58\uAC00 \uD50C\uB7AB\uD3FC\uB9C8\uB2E4 \uB2EC\uB77C\uC9C4\uB2E4.\n cwd: homedir(),\n // \uAE30\uB3D9 \uC2E4\uD328 \uC6D0\uC778\uC744 \uC77D\uC73C\uB824\uBA74 \uD30C\uC774\uD504\uC5EC\uC57C \uD55C\uB2E4 (\uAE30\uBCF8 'inherit' \uB294\n // Electron \uCF58\uC194\uB85C \uD758\uB824\uBCF4\uB0B4 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uC548 \uBCF4\uC778\uB2E4).\n stderr: 'pipe',\n });\n // \uCCAB \uC2E4\uD589\uC740 \uC778\uD130\uD504\uB9AC\uD130\u00B7\uC758\uC874\uC131 \uB0B4\uB824\uBC1B\uAE30\uB85C \uC624\uB798 \uAC78\uB9B0\uB2E4 \u2014 \uC9C4\uD589 \uC0C1\uD669\uC744\n // \uD654\uBA74\uC73C\uB85C \uD758\uB824\uBCF4\uB0B4\uC57C \uC0AC\uC6A9\uC790\uAC00 '\uBA48\uCDC4\uB098?' \uD558\uC9C0 \uC54A\uB294\uB2E4.\n const notify = st.onProgress ? throttle(st.onProgress, 300) : undefined;\n tap = collectStderr(transport as { stderr?: NodeJS.ReadableStream | null }, notify);\n } else if (cfg.transport === 'sse') {\n if (!cfg.url) throw new Error('sse server has no url');\n if (!SSEClientTransport) throw new Error('\uC774 \uBE4C\uB4DC\uC5D0\uC11C SSE \uC804\uC1A1\uC744 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.');\n transport = new SSEClientTransport(\n new URL(cfg.url),\n oauthTransportOptions(cfg, {\n requestInit: cfg.headers ? { headers: cfg.headers } : undefined,\n fetch: this.httpFetch,\n }) as ConstructorParameters<typeof SSEClientTransport>[1],\n );\n } else {\n if (!cfg.url) throw new Error('http server has no url');\n transport = new StreamableHTTPClientTransport(\n new URL(cfg.url),\n oauthTransportOptions(cfg, {\n requestInit: cfg.headers ? { headers: cfg.headers } : undefined,\n fetch: this.httpFetch,\n }) as ConstructorParameters<typeof StreamableHTTPClientTransport>[1],\n );\n }\n const client = new Client({ name: 'xgen-dex', version: '1.0.0' }, { capabilities: {} });\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n let listed: any;\n try {\n if (tap) {\n // stdio: \uCD9C\uB825\uC774 \uACC4\uC18D \uB098\uC624\uBA74 \uACC4\uC18D \uAE30\uB2E4\uB9B0\uB2E4 (\uCCAB \uC2E4\uD589 \uB2E4\uC6B4\uB85C\uB4DC).\n await waitWhileProgressing(client.connect(transport), tap.lastAt, `${label} \uC5F0\uACB0`);\n } else {\n await withTimeout(client.connect(transport), 20000, `${label} \uC5F0\uACB0`);\n }\n listed = await withTimeout(client.listTools(), 30000, `${label} \uB3C4\uAD6C \uBAA9\uB85D`);\n } catch (e) {\n // 'Connection closed' \uB9CC\uC73C\uB85C\uB294 \uACE0\uCE60 \uC218 \uC5C6\uB2E4 \u2014 \uC11C\uBC84\uAC00 stderr \uC5D0 \uB0A8\uAE34\n // \uC9C4\uC9DC \uC6D0\uC778\uC744 \uD568\uAED8 \uC62C\uB9B0\uB2E4.\n const tail = tap ? tailLines(tap.text()) : [];\n if (tail.length) {\n throw new McpStartError(\n `${(e as Error).message} \u2014 \uC11C\uBC84\uAC00 \uAE30\uB3D9\uD558\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. \uC544\uB798 \uCD9C\uB825\uC744 \uD655\uC778\uD558\uC138\uC694.`,\n tail,\n );\n }\n throw e;\n }\n st.client = client;\n st.tools = (listed?.tools || []).map((t: McpToolSchema) => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema,\n }));\n st.error = undefined;\n\n // G5 \u2014 \uC11C\uBC84\uAC00 \uB7F0\uD0C0\uC784\uC5D0 \uB3C4\uAD6C\uB97C \uBC14\uAFB8\uBA74(list_changed) \uC7AC\uC870\uD68C \uD6C4 \uCE74\uD0C8\uB85C\uADF8\uB97C \uB2E4\uC2DC\n // \uAD11\uACE0\uD55C\uB2E4. \uC774\uC804\uC5D0\uB294 \uD55C \uBC88 \uBD99\uC73C\uBA74 \uB3C4\uAD6C \uBAA9\uB85D\uC774 \uC601\uAD6C \uCE90\uC2DC\uB77C \uBC18\uC601\uB418\uC9C0 \uC54A\uC558\uB2E4.\n if (ToolListChangedNotificationSchema) {\n try {\n client.setNotificationHandler(\n ToolListChangedNotificationSchema,\n async (): Promise<void> => {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const relisted: any = await withTimeout(client.listTools(), 30000, `${label} \uB3C4\uAD6C \uC7AC\uC870\uD68C`);\n if (st.client !== client) return;\n st.tools = (relisted?.tools || []).map((t: McpToolSchema) => ({\n name: t.name,\n description: t.description,\n inputSchema: t.inputSchema,\n }));\n this.onCatalogChange?.();\n } catch {\n /* \uC7AC\uC870\uD68C \uC2E4\uD328\uB294 \uB2E4\uC74C \uD638\uCD9C\uC5D0\uC11C \uD68C\uBCF5\uB41C\uB2E4. */\n }\n },\n );\n } catch {\n /* \uC54C\uB9BC \uD578\uB4E4\uB7EC\uB97C \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 SDK \u2014 \uBB34\uC2DC. */\n }\n }\n\n // G11 \u2014 \uC11C\uBC84\uAC00 \uC870\uC6A9\uD788 \uC8FD\uC73C\uBA74(stdio \uD504\uB85C\uC138\uC2A4 exit / http \uC885\uB8CC) \uC0C1\uD0DC\uB97C \uBB34\uD6A8\uD654\uD558\uACE0\n // \uCE74\uD0C8\uB85C\uADF8\uB97C \uB2E4\uC2DC \uAD11\uACE0\uD55C\uB2E4. \uC720\uB839 \uB3C4\uAD6C\uAC00 \uCE74\uD0C8\uB85C\uADF8\uC5D0 \uB0A8\uC544 \uACC4\uC18D \uAD11\uACE0\uB418\uB358 \uBB38\uC81C\uB97C \uB9C9\uB294\uB2E4.\n client.onclose = (): void => {\n if (st.client !== client) return;\n st.client = null;\n st.tools = [];\n this.onCatalogChange?.();\n };\n })();\n try {\n await st.connecting;\n } catch (e) {\n st.error = String((e as Error).message);\n st.client = null;\n throw e;\n } finally {\n st.connecting = undefined;\n }\n }\n\n private async disconnect(name: string): Promise<void> {\n const st = this.states.get(name);\n if (!st) return;\n const c = st.client;\n st.client = null;\n st.tools = [];\n try {\n await c?.close?.();\n } catch {\n /* ignore */\n }\n }\n\n /**\n * Connect every enabled server + return their tool catalogs.\n *\n * **\uBCD1\uB82C\uB85C** \uBD99\uB294\uB2E4. \uC21C\uCC28\uB85C \uBD99\uC774\uBA74 \uCCAB \uC2E4\uD589\uC774\uB77C \uC758\uC874\uC131\uC744 \uB0B4\uB824\uBC1B\uB294 \uC11C\uBC84 \uD558\uB098\uAC00\n * \uB098\uBA38\uC9C0 \uC804\uBD80\uB97C \uB9C9\uB294\uB2E4 (\uAE30\uB3D9 \uB300\uAE30\uAC00 \uC9C4\uD589 \uC0C1\uD669 \uAE30\uBC18\uC774\uB77C \uBA87 \uBD84\uAE4C\uC9C0 \uAC08 \uC218 \uC788\uB2E4).\n * \uACB0\uACFC \uC21C\uC11C\uB294 \uC124\uC815 \uC21C\uC11C\uB97C \uC720\uC9C0\uD55C\uB2E4.\n */\n async advertise(): Promise<McpServerAdvert[]> {\n const targets = [...this.states].filter(([, st]) => st.config.enabled !== false);\n return Promise.all(\n targets.map(async ([name, st]): Promise<McpServerAdvert> => {\n try {\n await this.connect(name);\n return { name, connected: true, tools: st.tools };\n } catch (e) {\n return { name, connected: false, error: String((e as Error).message), tools: [] };\n }\n }),\n );\n }\n\n /** Flat catalog for the bridge `hello` frame (only connected servers' tools). */\n async advertisedTools(): Promise<AdvertisedTool[]> {\n const adverts = await this.advertise();\n const flat: AdvertisedTool[] = [];\n for (const a of adverts) {\n if (!a.connected) continue;\n for (const t of a.tools) {\n flat.push({ server: a.name, name: t.name, description: t.description, inputSchema: t.inputSchema });\n }\n }\n return flat;\n }\n\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n async callTool(name: string, tool: string, args: any): Promise<any> {\n // \uD134\uC774 \uB3C4\uB294 \uB3C4\uC911\uC5D0 \uC0AC\uC6A9\uC790\uAC00 \uC774 \uC11C\uBC84\uB97C \uC9C0\uC6E0\uAC70\uB098 \uB85C\uCEEC MCP \uC2A4\uC704\uCE58\uB97C \uAED0\uC744 \uC218 \uC788\uB2E4\n // (\uADF8\uB7EC\uBA74 configure() \uAC00 \uC0C1\uD0DC\uB97C \uD1B5\uC9F8\uB85C \uBE44\uC6B4\uB2E4). \uADF8\uB54C connect() \uC758\n // \"unknown MCP server\" \uB294 \uBAA8\uB378\uC5D0\uAC8C **\uC790\uAE30\uAC00 \uC774\uB984\uC744 \uC798\uBABB \uC9C0\uC5B4\uB0C8\uB2E4**\uB294 \uC2E0\uD638\uB85C\n // \uC77D\uD78C\uB2E4 \u2014 \uAC19\uC740 \uD638\uCD9C\uC744 \uC774\uB984\uB9CC \uBC14\uAFD4 \uBC18\uBCF5\uD558\uAC8C \uB9CC\uB4E0\uB2E4. \uBB34\uC5C7\uC774 \uBC14\uB00C\uC5C8\uB294\uC9C0 \uB9D0\uD574 \uC900\uB2E4.\n if (!this.states.has(name)) {\n throw new Error(\n `\uB85C\uCEEC MCP \uC11C\uBC84 '${name}' \uAC00 \uC9C0\uAE08\uC740 \uCEE4\uB125\uD130\uC5D0 \uC5C6\uC2B5\uB2C8\uB2E4 ` +\n `(\uC124\uC815\uC5D0\uC11C \uC81C\uAC70\uB410\uAC70\uB098 \uB85C\uCEEC MCP \uC2A4\uC704\uCE58\uAC00 \uAEBC\uC84C\uC2B5\uB2C8\uB2E4). \uB3C4\uAD6C \uC774\uB984 \uBB38\uC81C\uAC00 \uC544\uB2C8\uBBC0\uB85C ` +\n `\uC7AC\uC2DC\uB3C4\uD574\uB3C4 \uAC19\uC2B5\uB2C8\uB2E4 \u2014 \uC774 \uC11C\uBC84 \uC5C6\uC774 \uC9C4\uD589\uD558\uAC70\uB098 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uD655\uC778\uD558\uC138\uC694.`,\n );\n }\n await this.connect(name);\n const st = this.states.get(name);\n if (!st?.client) throw new Error(`MCP server ${name} not connected`);\n try {\n return await withTimeout(\n st.client.callTool({ name: tool, arguments: args || {} }),\n 120000,\n `callTool ${name}.${tool}`,\n );\n } catch (e) {\n // The server may have died mid-call; drop the client so the NEXT call\n // reconnects fresh instead of hanging on a stale transport.\n await this.disconnect(name);\n throw e;\n }\n }\n\n /** One-shot connect \u2192 list \u2192 disconnect, for the settings \"\uD14C\uC2A4\uD2B8\" button. */\n async test(\n config: McpServerConfig,\n onProgress?: (lines: string[]) => void,\n ): Promise<{ ok: boolean; tools?: McpToolSchema[]; error?: string; hints?: string[] }> {\n const tmp = `__test__${config.name || 'srv'}`;\n this.states.set(tmp, { config: { ...config, name: tmp }, client: null, tools: [], onProgress });\n try {\n await this.connect(tmp);\n const tools = this.states.get(tmp)?.tools || [];\n return { ok: true, tools };\n } catch (e) {\n const err = e as Error & { hints?: string[] };\n // \uB7F0\uD0C0\uC784 \uBBF8\uC124\uCE58(ExecNotFoundError) \uB294 \uC124\uCE58 \uC548\uB0B4\uB97C, \uAE30\uB3D9 \uC2E4\uD328\n // (McpStartError) \uB294 \uC11C\uBC84 stderr \uAF2C\uB9AC\uB97C \uD568\uAED8 \uB3CC\uB824\uC900\uB2E4.\n const hints = Array.isArray(err.hints) && err.hints.length ? err.hints : undefined;\n return { ok: false, error: String(err.message), hints };\n } finally {\n await this.disconnect(tmp);\n this.states.delete(tmp);\n }\n }\n\n listServers(): McpServerConfig[] {\n return [...this.states.values()].map((s) => s.config);\n }\n\n async closeAll(): Promise<void> {\n for (const name of [...this.states.keys()]) await this.disconnect(name);\n }\n}\n\nlet _manager: MCPManager | null = null;\nexport function getMcpManager(): MCPManager {\n if (!_manager) _manager = new MCPManager();\n return _manager;\n}\n", "/**\n * \uC2E4\uD589 \uD30C\uC77C \uD574\uC11D + \uBBF8\uC124\uCE58 \uB7F0\uD0C0\uC784 \uC9C4\uB2E8 \u2014 `spawn uvx ENOENT` \uBD80\uB958\uC758 \uADFC\uBCF8 \uCC98\uB9AC.\n *\n * \uB450 \uAC00\uC9C0 \uC11C\uB85C \uB2E4\uB978 \uC2E4\uD328\uB97C \uAD6C\uBD84\uD574\uC11C \uB2E4\uB8EC\uB2E4:\n *\n * 1) **\uC124\uCE58\uB294 \uB410\uB294\uB370 \uC571\uC774 \uBABB \uCC3E\uB294 \uACBD\uC6B0** \u2014 GUI(Finder/Dock/\uC2DC\uC791 \uBA54\uB274)\uB85C \uC2E4\uD589\uB41C\n * \uC571\uC740 \uB85C\uADF8\uC778 \uC178\uC758 PATH \uB97C \uC0C1\uC18D\uD558\uC9C0 \uC54A\uB294\uB2E4. macOS \uB294 `/usr/bin:/bin` \uC218\uC900\uB9CC\n * \uBB3C\uB824\uBC1B\uC544 `~/.local/bin/uvx`, `/opt/homebrew/bin/npx` \uB97C \uC804\uBD80 \uB193\uCE5C\uB2E4.\n * \u2192 \uB85C\uADF8\uC778 \uC178 PATH \uB97C \uC77D\uC5B4 \uBCF4\uAC15\uD558\uACE0 **\uC808\uB300 \uACBD\uB85C\uB85C \uD574\uC11D**\uD574\uC11C \uB118\uAE34\uB2E4.\n *\n * 2) **\uC560\uCD08\uC5D0 \uC124\uCE58\uAC00 \uC548 \uB41C \uACBD\uC6B0** (\uC0AC\uC6A9\uC790\uC758 mac: `zsh: command not found: uvx`)\n * \u2192 \uBB34\uC5C7\uC774 \uC5C6\uB294\uC9C0, \uC5B4\uB5BB\uAC8C \uC124\uCE58\uD558\uB294\uC9C0 \uD50C\uB7AB\uD3FC\uBCC4\uB85C \uC815\uD655\uD788 \uC548\uB0B4\uD55C\uB2E4.\n *\n * Windows \uC758 `.cmd`/`.exe` \uD655\uC7A5\uC790 \uD574\uC11D\uB3C4 \uC5EC\uAE30\uC11C \uD55C\uB2E4 (`npx` \uB294 \uC2E4\uC81C\uB85C\n * `npx.cmd` \u2014 \uD655\uC7A5\uC790 \uC5C6\uC774 spawn \uD558\uBA74 \uC2E4\uD328). \uC2E4\uC81C spawn \uC740 MCP SDK \uAC00\n * cross-spawn \uC73C\uB85C \uD558\uBBC0\uB85C `.cmd` \uB97C \uC808\uB300 \uACBD\uB85C\uB85C \uB118\uACA8\uB3C4 \uC548\uC804\uD558\uB2E4.\n */\n\nimport { execFile } from 'child_process';\nimport { accessSync, constants, statSync } from 'fs';\nimport { homedir } from 'os';\nimport { basename, delimiter, isAbsolute, join, sep } from 'path';\n\nconst IS_WIN = process.platform === 'win32';\nconst IS_MAC = process.platform === 'darwin';\n\n// \u2500\u2500 \uC54C\uB824\uC9C4 \uB7F0\uD0C0\uC784: \uC5C6\uC744 \uB54C \uBB34\uC5C7\uC744 \uC5B4\uB5BB\uAC8C \uC124\uCE58\uD574\uC57C \uD558\uB294\uC9C0 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface RuntimeInfo {\n /** \uC0AC\uB78C\uC774 \uC77D\uB294 \uC774\uB984 */\n label: string;\n /** \uC774 \uB7F0\uD0C0\uC784\uC774 \uC81C\uACF5\uD558\uB294 \uBA85\uB839\uB4E4 (\uCCAB \uD56D\uBAA9\uC774 \uB300\uD45C) */\n commands: string[];\n /** \uC774 \uBA85\uB839\uC774 \uBB34\uC5C7\uC778\uC9C0 \uD55C \uC904 \uC124\uBA85 */\n what: string;\n /** \uD50C\uB7AB\uD3FC\uBCC4 \uC124\uCE58 \uBC29\uBC95 (\uD45C\uC2DC \uC21C\uC11C\uB300\uB85C) */\n install: () => string[];\n /** \uACF5\uC2DD \uC548\uB0B4 \uB9C1\uD06C */\n url: string;\n}\n\nconst RUNTIMES: RuntimeInfo[] = [\n {\n label: 'uv',\n commands: ['uvx', 'uv'],\n what: 'Python MCP \uC11C\uBC84\uB97C \uC2E4\uD589\uD558\uB294 uv \uD328\uD0A4\uC9C0 \uB9E4\uB2C8\uC800\uC758 \uC2E4\uD589\uAE30\uC785\uB2C8\uB2E4.',\n url: 'https://docs.astral.sh/uv/getting-started/installation/',\n install: () =>\n IS_WIN\n ? ['powershell -c \"irm https://astral.sh/uv/install.ps1 | iex\"', 'winget install astral-sh.uv']\n : IS_MAC\n ? ['curl -LsSf https://astral.sh/uv/install.sh | sh', 'brew install uv']\n : ['curl -LsSf https://astral.sh/uv/install.sh | sh'],\n },\n {\n label: 'Node.js',\n commands: ['npx', 'npm', 'node'],\n what: 'JavaScript MCP \uC11C\uBC84\uB97C \uC2E4\uD589\uD558\uB294 Node.js \uB7F0\uD0C0\uC784\uC785\uB2C8\uB2E4.',\n url: 'https://nodejs.org/',\n install: () =>\n IS_WIN\n ? ['winget install OpenJS.NodeJS.LTS', 'https://nodejs.org \uC5D0\uC11C LTS \uC124\uCE58 \uD504\uB85C\uADF8\uB7A8 \uB0B4\uB824\uBC1B\uAE30']\n : IS_MAC\n ? ['brew install node', 'https://nodejs.org \uC5D0\uC11C LTS \uC124\uCE58 \uD504\uB85C\uADF8\uB7A8 \uB0B4\uB824\uBC1B\uAE30']\n : ['sudo apt install nodejs npm (Debian/Ubuntu)', 'https://nodejs.org \uC5D0\uC11C LTS \uB0B4\uB824\uBC1B\uAE30'],\n },\n {\n label: 'Python',\n commands: ['python3', 'python', 'pip', 'pip3', 'pipx'],\n what: 'Python \uB7F0\uD0C0\uC784/\uD328\uD0A4\uC9C0 \uB3C4\uAD6C\uC785\uB2C8\uB2E4.',\n url: 'https://www.python.org/downloads/',\n install: () =>\n IS_WIN\n ? ['winget install Python.Python.3.12']\n : IS_MAC\n ? ['brew install python']\n : ['sudo apt install python3 python3-pip (Debian/Ubuntu)'],\n },\n {\n label: 'Docker',\n commands: ['docker'],\n what: '\uCEE8\uD14C\uC774\uB108\uB85C \uBC30\uD3EC\uB41C MCP \uC11C\uBC84\uB97C \uC2E4\uD589\uD569\uB2C8\uB2E4.',\n url: 'https://docs.docker.com/get-started/get-docker/',\n install: () => ['https://docs.docker.com/get-started/get-docker/ \uC5D0\uC11C Docker Desktop \uC124\uCE58'],\n },\n {\n label: 'Bun',\n commands: ['bun', 'bunx'],\n what: 'Bun \uB7F0\uD0C0\uC784\uC785\uB2C8\uB2E4.',\n url: 'https://bun.sh/',\n install: () =>\n IS_WIN ? ['powershell -c \"irm bun.sh/install.ps1 | iex\"'] : ['curl -fsSL https://bun.sh/install | bash'],\n },\n {\n label: 'Deno',\n commands: ['deno'],\n what: 'Deno \uB7F0\uD0C0\uC784\uC785\uB2C8\uB2E4.',\n url: 'https://deno.land/',\n install: () =>\n IS_WIN ? ['irm https://deno.land/install.ps1 | iex'] : ['curl -fsSL https://deno.land/install.sh | sh'],\n },\n];\n\n/** \uBA85\uB839 \uC774\uB984\uC73C\uB85C \uC54C\uB824\uC9C4 \uB7F0\uD0C0\uC784\uC744 \uCC3E\uB294\uB2E4 (\uD655\uC7A5\uC790\u00B7\uACBD\uB85C \uC81C\uAC70 \uD6C4 \uBE44\uAD50). */\nexport function runtimeFor(command: string): RuntimeInfo | null {\n const base = basename(command.trim()).toLowerCase().replace(/\\.(exe|cmd|bat|com)$/i, '');\n return RUNTIMES.find((r) => r.commands.includes(base)) ?? null;\n}\n\n// \u2500\u2500 PATH \uBCF4\uAC15 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/** \uC0AC\uC6A9\uC790 \uB3C4\uAD6C\uAC00 \uD754\uD788 \uC124\uCE58\uB418\uB294 \uC704\uCE58 (\uD50C\uB7AB\uD3FC\uBCC4). */\nexport function commonBinDirs(home = homedir()): string[] {\n if (IS_WIN) {\n const appData = process.env.APPDATA || join(home, 'AppData', 'Roaming');\n const localAppData = process.env.LOCALAPPDATA || join(home, 'AppData', 'Local');\n return [\n join(localAppData, 'Microsoft', 'WindowsApps'),\n join(appData, 'npm'),\n join(localAppData, 'Programs', 'nodejs'),\n join(home, '.local', 'bin'),\n join(home, '.cargo', 'bin'),\n join(home, '.bun', 'bin'),\n join(localAppData, 'Programs', 'Python', 'Scripts'),\n 'C:\\\\Program Files\\\\nodejs',\n ];\n }\n return [\n '/opt/homebrew/bin', // Apple Silicon homebrew\n '/usr/local/bin',\n '/opt/local/bin', // MacPorts\n join(home, '.local', 'bin'), // uv / pipx\n join(home, '.cargo', 'bin'),\n join(home, '.bun', 'bin'),\n join(home, '.deno', 'bin'),\n join(home, 'bin'),\n '/usr/bin',\n '/bin',\n '/snap/bin',\n ];\n}\n\ninterface PathCache {\n value: string | null;\n at: number;\n}\nlet loginPathCache: PathCache | null = null;\n//: \uB85C\uADF8\uC778 \uC178 PATH \uCE90\uC2DC \uC218\uBA85 \u2014 \uC0AC\uC6A9\uC790\uAC00 \uB3C4\uAD6C\uB97C \uC124\uCE58\uD55C \uC9C1\uD6C4 [\uD14C\uC2A4\uD2B8]\uB97C \uB2E4\uC2DC\n//: \uB204\uB974\uBA74 \uC571 \uC7AC\uC2DC\uC791 \uC5C6\uC774 \uC778\uC2DD\uB418\uC5B4\uC57C \uD55C\uB2E4.\nconst PATH_CACHE_TTL_MS = 20_000;\n\n/** \uCE90\uC2DC\uB97C \uC989\uC2DC \uBC84\uB9B0\uB2E4 (\uC124\uCE58 \uC9C1\uD6C4 \uC7AC\uC2DC\uB3C4 \uACBD\uB85C). */\nexport function resetPathCache(): void {\n loginPathCache = null;\n}\n\n/**\n * `env` \uCD9C\uB825\uC5D0\uC11C PATH \uC904\uC744 \uBF51\uB294\uB2E4.\n *\n * `printf \"$PATH\"` \uB300\uC2E0 `env` \uB97C \uC4F0\uB294 \uC774\uC720: **fish \uB294 $PATH \uAC00 \uB9AC\uC2A4\uD2B8**\uB77C\n * \uBB38\uC790\uC5F4 \uBCF4\uAC04\uD558\uBA74 `/a /b` \uCC98\uB7FC \uACF5\uBC31\uC73C\uB85C \uC774\uC5B4\uC838 PATH \uB85C \uBABB \uC4F4\uB2E4. \uBC18\uBA74 `env`\n * \uB294 \uD504\uB85C\uC138\uC2A4 \uD658\uACBD \uBE14\uB85D\uC744 \uADF8\uB300\uB85C \uCC0D\uC73C\uBBC0\uB85C \uC5B4\uB290 \uC178\uC774\uB4E0 \uD56D\uC0C1 `:` \uB85C \uC774\uC5B4\uC9C4\n * \uC62C\uBC14\uB978 \uAC12\uC774 \uB098\uC628\uB2E4.\n *\n * rc \uD30C\uC77C\uC774 \uC55E\uC5D0 \uC7A1\uC74C\uC744 \uBC49\uC744 \uC218 \uC788\uC5B4 **\uB9C8\uC9C0\uB9C9** PATH= \uC904\uC744 \uD0DD\uD55C\uB2E4.\n */\nexport function parseEnvPath(stdout: string): string | null {\n let found: string | null = null;\n for (const line of stdout.split(/\\r?\\n/)) {\n const m = /^PATH=(.*)$/.exec(line);\n if (m) found = m[1];\n }\n if (!found) return null;\n const value = found.trim();\n if (!value) return null;\n // \uC628\uC804\uC131 \uAC80\uC0AC \u2014 \uC2E4\uC7AC\uD558\uB294 \uB514\uB809\uD130\uB9AC\uAC00 \uD558\uB098\uB3C4 \uC5C6\uC73C\uBA74 \uC798\uBABB \uD30C\uC2F1\uD55C \uAC83\uC73C\uB85C \uBCF8\uB2E4.\n const usable = value.split(delimiter).some((d) => {\n if (!d) return false;\n try {\n return statSync(d).isDirectory();\n } catch {\n return false;\n }\n });\n return usable ? value : null;\n}\n\n/** \uB85C\uADF8\uC778 \uC178\uC758 PATH \uB97C \uC77D\uB294\uB2E4 (darwin/linux). \uC2E4\uD328/\uC9C0\uC5F0\uC740 \uBB34\uC2DC. */\nexport async function loginShellPath(): Promise<string | null> {\n if (IS_WIN) return null;\n if (loginPathCache && Date.now() - loginPathCache.at < PATH_CACHE_TTL_MS) {\n return loginPathCache.value;\n }\n const shell = process.env.SHELL || '/bin/bash';\n const value = await new Promise<string | null>((resolve) => {\n // -i(\uB300\uD654\uD615)\uAE4C\uC9C0 \uC918\uC57C ~/.zshrc\u00B7~/.bashrc \uC758 PATH \uCD94\uAC00\uAC00 \uBC18\uC601\uB41C\uB2E4. \uB2E4\uB9CC\n // \uC77C\uBD80 \uD658\uACBD\uC5D0\uC11C \uBA48\uCD9C \uC218 \uC788\uC5B4 \uD0C0\uC784\uC544\uC6C3\uC73C\uB85C \uB04A\uB294\uB2E4 (\uC2E4\uD328\uD574\uB3C4 commonBinDirs\n // \uBCF4\uAC15\uB9CC\uC73C\uB85C \uB3D9\uC791\uD55C\uB2E4).\n execFile(shell, ['-ilc', 'env'], { timeout: 4000, windowsHide: true, maxBuffer: 1 << 20 }, (_err, stdout) => {\n // \uC885\uB8CC \uCF54\uB4DC\uAC00 0\uC774 \uC544\uB2C8\uC5B4\uB3C4 \uCD9C\uB825\uC774 \uC788\uC73C\uBA74 \uC4F4\uB2E4 (rc \uC2A4\uD06C\uB9BD\uD2B8\uAC00 \uBE44\uC815\uC0C1\n // \uC885\uB8CC \uCF54\uB4DC\uB85C \uB05D\uB098\uB294 \uD658\uACBD\uC774 \uD754\uD558\uB2E4).\n resolve(stdout ? parseEnvPath(stdout) : null);\n });\n });\n loginPathCache = { value, at: Date.now() };\n return value;\n}\n\n/** \uC911\uBCF5 \uC81C\uAC70 + \uBE48 \uD56D\uBAA9 \uC81C\uAC70\uB85C PATH \uBB38\uC790\uC5F4\uC744 \uB9CC\uB4E0\uB2E4. */\nexport function mergePaths(...sources: Array<string | null | undefined>): string {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const src of sources) {\n if (!src) continue;\n for (const p of src.split(delimiter)) {\n const dir = p.trim().replace(new RegExp(`${sep === '\\\\' ? '\\\\\\\\' : sep}+$`), '');\n if (!dir || seen.has(dir)) continue;\n seen.add(dir);\n out.push(dir);\n }\n }\n return out.join(delimiter);\n}\n\n/** GUI \uC2E4\uD589\uC5D0\uC11C\uB3C4 \uC0AC\uC6A9\uC790 \uB3C4\uAD6C\uB97C \uCC3E\uC744 \uC218 \uC788\uB294 PATH. */\nexport async function augmentedPath(): Promise<string> {\n const login = await loginShellPath();\n return mergePaths(login, process.env.PATH, commonBinDirs().join(delimiter));\n}\n\n/**\n * \uC790\uC2DD \uD504\uB85C\uC138\uC2A4 \uD658\uACBD\uC744 \uB9CC\uB4E0\uB2E4.\n *\n * Windows \uC758 \uD658\uACBD\uBCC0\uC218\uB294 **\uB300\uC18C\uBB38\uC790\uB97C \uAD6C\uBD84\uD558\uC9C0 \uC54A\uB294\uB370**, spawn \uC5D0 \uB118\uAE30\uB294 \uAC1D\uCCB4\uB294\n * \uAD6C\uBD84\uD55C\uB2E4. `{...process.env}` \uB294 \uBCF4\uD1B5 `Path` \uB85C \uB4E4\uC5B4\uC624\uBBC0\uB85C \uC5EC\uAE30\uC5D0 `PATH` \uB97C\n * \uB354\uD558\uBA74 \uD0A4\uAC00 \uB458\uC774 \uB418\uACE0, \uC870\uD68C\uD558\uB294 \uCABD(cross-spawn \uC758 path-key)\uC774 \uC5B4\uB290 \uCABD\uC744\n * \uC9D1\uC744\uC9C0 \uC54C \uC218 \uC5C6\uB2E4 \u2014 \uC0AC\uC6A9\uC790\uAC00 \uC11C\uBC84 \uC124\uC815\uC5D0\uC11C PATH \uB97C \uC9C1\uC811 \uC9C0\uC815\uD588\uB294\uB370 \uBB34\uC2DC\uB418\uB294\n * \uC0AC\uACE0\uAC00 \uC5EC\uAE30\uC11C \uB09C\uB2E4. \uADF8\uB798\uC11C Windows \uC5D0\uC11C\uB294 path \uACC4\uC5F4 \uD0A4\uB97C \uD558\uB098\uB85C \uD569\uCE5C\uB2E4.\n *\n * \uC6B0\uC120\uC21C\uC704: \uC11C\uBC84 \uC124\uC815 env > \uBCF4\uAC15\uB41C PATH > \uD604\uC7AC \uD504\uB85C\uC138\uC2A4 env.\n */\nexport function buildChildEnv(\n pathStr: string,\n cfgEnv?: Record<string, string>,\n base: Record<string, string | undefined> = process.env as Record<string, string>,\n): Record<string, string> {\n const isPathKey = (k: string) => k.toLowerCase() === 'path';\n const out: Record<string, string> = {};\n for (const [k, v] of Object.entries(base)) {\n if (v === undefined) continue;\n if (IS_WIN && isPathKey(k)) continue; // \uC544\uB798\uC5D0\uC11C \uD55C \uBC88\uB9CC \uB123\uB294\uB2E4\n out[k] = v;\n }\n // \uC0AC\uC6A9\uC790\uAC00 PATH \uB97C \uC9C1\uC811 \uC9C0\uC815\uD588\uC73C\uBA74 \uADF8 \uAC12\uC744 \uC874\uC911\uD55C\uB2E4.\n const userPathKey = Object.keys(cfgEnv || {}).find(isPathKey);\n const effectivePath = userPathKey ? (cfgEnv as Record<string, string>)[userPathKey] : pathStr;\n for (const [k, v] of Object.entries(cfgEnv || {})) {\n if (isPathKey(k)) continue; // PATH \uB294 \uC544\uB798\uC5D0\uC11C \uC77C\uAD04 \uCC98\uB9AC\n out[k] = v;\n }\n if (IS_WIN) {\n out.Path = effectivePath; // Windows \uD45C\uAE30 \uAD00\uB840 \u2014 \uB2E8 \uD558\uB098\uB9CC \uC874\uC7AC\uD558\uAC8C \uD55C\uB2E4\n } else {\n out.PATH = effectivePath;\n }\n return out;\n}\n\n// \u2500\u2500 \uC2E4\uD589 \uD30C\uC77C \uD0D0\uC0C9 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nfunction isExecutableFile(p: string): boolean {\n try {\n if (!statSync(p).isFile()) return false;\n if (IS_WIN) return true; // Windows \uB294 \uD655\uC7A5\uC790\uB85C \uC2E4\uD589 \uAC00\uB2A5\uC131\uC744 \uD310\uB2E8\n accessSync(p, constants.X_OK);\n return true;\n } catch {\n return false;\n }\n}\n\n/** Windows \uD655\uC7A5\uC790 \uD6C4\uBCF4 (PATHEXT). npx/uvx \uB294 \uC2E4\uC81C\uB85C npx.cmd/uvx.exe \uB2E4. */\nfunction winExtensions(): string[] {\n const raw = process.env.PATHEXT || '.COM;.EXE;.BAT;.CMD';\n // PATHEXT \uB294 \uAD00\uB840\uC0C1 \uB300\uBB38\uC790\uC9C0\uB9CC \uC2E4\uC81C \uD30C\uC77C\uC740 uvx.cmd/node.exe \uCC98\uB7FC \uC18C\uBB38\uC790\uB2E4.\n // Windows \uC870\uD68C\uB294 \uB300\uC18C\uBB38\uC790 \uBB34\uC2DC\uB77C \uC5B4\uB290 \uCABD\uC774\uB4E0 \uCC3E\uC9C0\uB9CC, \uBC18\uD658 \uACBD\uB85C\uAC00 \uC2E4\uC81C\n // \uD30C\uC77C\uBA85\uACFC \uAC19\uC544\uC57C \uB85C\uADF8/\uC624\uB958\uAC00 \uD5F7\uAC08\uB9AC\uC9C0 \uC54A\uB294\uB2E4.\n return raw.split(';').map((e) => e.trim().toLowerCase()).filter(Boolean);\n}\n\n/**\n * *command* \uC758 \uC808\uB300 \uACBD\uB85C\uB97C \uCC3E\uB294\uB2E4. \uACBD\uB85C \uAD6C\uBD84\uC790\uAC00 \uB4E4\uC5B4 \uC788\uC73C\uBA74 \uADF8\uB300\uB85C \uAC80\uC99D\uB9CC\n * \uD55C\uB2E4. \uBABB \uCC3E\uC73C\uBA74 null.\n */\nexport function resolveExecutable(command: string, pathStr: string): string | null {\n const cmd = command.trim();\n if (!cmd) return null;\n const hasSep = cmd.includes('/') || (IS_WIN && cmd.includes('\\\\'));\n const candidates = (base: string): string[] =>\n IS_WIN ? [base, ...winExtensions().map((e) => base + e)] : [base];\n\n if (hasSep || isAbsolute(cmd)) {\n for (const c of candidates(cmd)) if (isExecutableFile(c)) return c;\n return null;\n }\n for (const dir of pathStr.split(delimiter)) {\n if (!dir) continue;\n for (const c of candidates(join(dir, cmd))) {\n if (isExecutableFile(c)) return c;\n }\n }\n return null;\n}\n\n// \u2500\u2500 \uC9C4\uB2E8 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport interface ExecDiagnosis {\n /** \uBABB \uCC3E\uC740 \uBA85\uB839 */\n command: string;\n /** \uD55C \uC904 \uC694\uC57D (UI \uBCF8\uBB38) */\n summary: string;\n /** \uD574\uACB0 \uBC29\uBC95 \u2014 \uC124\uCE58 \uBA85\uB839/\uB9C1\uD06C \uB4F1 (UI \uBAA9\uB85D) */\n hints: string[];\n}\n\n/**\n * \uBABB \uCC3E\uC740 \uBA85\uB839\uC744 \uC9C4\uB2E8\uD55C\uB2E4. \uC54C\uB824\uC9C4 \uB7F0\uD0C0\uC784\uC774\uBA74 \uC124\uCE58 \uBC29\uBC95\uC744, \uC544\uB2C8\uBA74 \uACBD\uB85C \uC9C0\uC815\n * \uBC29\uBC95\uC744 \uC548\uB0B4\uD55C\uB2E4. \uAC19\uC740 \uB7F0\uD0C0\uC784\uC758 **\uD615\uC81C \uBA85\uB839\uC774 \uC788\uB294\uC9C0**\uAE4C\uC9C0 \uBD10\uC11C \uB354 \uC815\uD655\uD55C\n * \uC6D0\uC778\uC744 \uC9DA\uB294\uB2E4 (\uC608: uv \uB294 \uC788\uB294\uB370 uvx \uAC00 \uC5C6\uB2E4 \u2192 uv \uBC84\uC804\uC774 \uB0AE\uB2E4).\n */\nexport function diagnoseMissing(command: string, pathStr: string): ExecDiagnosis {\n const rt = runtimeFor(command);\n const hints: string[] = [];\n const dirCount = pathStr.split(delimiter).filter(Boolean).length;\n\n const base = basename(command);\n if (rt) {\n const sibling = rt.commands.find((c) => c !== base && resolveExecutable(c, pathStr));\n if (sibling) {\n hints.push(\n `\uCC38\uACE0: \uAC19\uC740 \uB7F0\uD0C0\uC784\uC758 \"${sibling}\" \uB294 \uCC3E\uC558\uC2B5\uB2C8\uB2E4 \u2014 ${rt.label} \uBC84\uC804\uC774 \uB0AE\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4. ` +\n `\uCD5C\uC2E0\uC73C\uB85C \uC62C\uB9AC\uBA74 \"${base}\" \uB3C4 \uD568\uAED8 \uC124\uCE58\uB429\uB2C8\uB2E4.`,\n );\n }\n hints.push(...rt.install().map((cmd) => `\uC124\uCE58: ${cmd}`));\n hints.push(`\uC548\uB0B4: ${rt.url}`);\n }\n // \uC808\uB300 \uACBD\uB85C \uC608\uC2DC\uB294 **\uC774 \uCEF4\uD4E8\uD130\uC758 \uC2E4\uC81C \uD648 \uACBD\uB85C**\uB85C \uBCF4\uC5EC\uC900\uB2E4 (\uD50C\uB7AB\uD3FC\uB9C8\uB2E4 \uB2E4\uB984).\n const example = IS_WIN\n ? join(homedir(), '.local', 'bin', `${base}.exe`)\n : join(homedir(), '.local', 'bin', base);\n hints.push(\n IS_WIN\n ? `\uB610\uB294 \uC2E4\uD589 \uBA85\uB839\uC5D0 \uC808\uB300 \uACBD\uB85C\uB97C \uC801\uC73C\uC138\uC694 (\uC608: ${example}). PowerShell \uC5D0\uC11C ` +\n `\\`Get-Command ${base}\\` \uB85C \uD655\uC778\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.`\n : `\uB610\uB294 \uC2E4\uD589 \uBA85\uB839\uC5D0 \uC808\uB300 \uACBD\uB85C\uB97C \uC801\uC73C\uC138\uC694 (\uC608: ${example}). \uD130\uBBF8\uB110\uC5D0\uC11C ` +\n `\\`which ${base}\\` \uB85C \uD655\uC778\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.`,\n );\n hints.push('\uC124\uCE58\uD55C \uB4A4 [\uD14C\uC2A4\uD2B8]\uB97C \uB2E4\uC2DC \uB204\uB974\uBA74 \uC7AC\uAC80\uC0C9\uD569\uB2C8\uB2E4 (\uC571 \uC7AC\uC2DC\uC791 \uBD88\uD544\uC694).');\n\n // \uD55C\uAD6D\uC5B4 \uC870\uC0AC(\uC774/\uAC00\u00B7\uC744/\uB97C)\uB294 \uBA85\uB839 \uC774\uB984\uC5D0 \uB530\uB77C \uB2EC\uB77C\uC9C4\uB2E4 \u2014 \uC870\uC0AC \uC5C6\uB294 \uBB38\uC7A5\uC73C\uB85C\n // \uC4F4\uB2E4.\n const summary = rt\n ? `\"${command}\" \u2014 \uC2E4\uD589 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4. ${rt.label} \uC124\uCE58\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4. ${rt.what}`\n : `\"${command}\" \u2014 \uC2E4\uD589 \uD30C\uC77C\uC744 \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4 (PATH ${dirCount}\uAC1C \uACBD\uB85C \uAC80\uC0C9).`;\n return { command, summary, hints };\n}\n\n/** \uC9C4\uB2E8 \uACB0\uACFC\uB97C \uB2F4\uC544 \uB358\uC9C0\uB294 \uC624\uB958 \u2014 \uD638\uCD9C\uC790\uAC00 hints \uB97C UI \uB85C \uC804\uB2EC\uD55C\uB2E4. */\nexport class ExecNotFoundError extends Error {\n readonly hints: string[];\n readonly command: string;\n constructor(d: ExecDiagnosis) {\n super(d.summary);\n this.name = 'ExecNotFoundError';\n this.command = d.command;\n this.hints = d.hints;\n }\n}\n", "/**\n * LocalTools \u2014 built-in tools the connector hosts ITSELF, advertised into the\n * SAME per-user MCP catalog as the user's configured MCP servers.\n *\n * Why not \"just add an MCP server\": the point is out-of-the-box local control.\n * Asking every user to wire an external shell-MCP server is exactly the friction\n * this removes. These tools ride the existing bridge rails (`hello` advertises\n * them, `mcp_call` dispatches to them), so the XGEN backend + agents need ZERO\n * changes \u2014 a built-in tool is indistinguishable from an MCP-server tool in the\n * catalog. The reserved server namespace is {@link LOCAL_SERVER}.\n *\n * Two tools, both operating the USER'S OWN PHYSICAL COMPUTER (not the cloud\n * workspace / sandbox):\n * \u00B7 {@link SHELL_TOOL} `Shell` \u2014 run ONE command in the native shell\n * (PowerShell on Windows, the user's `$SHELL`/bash elsewhere). Robustness\n * is the whole game here (2026-08 field report \u2014 the tool \"worked then\n * stopped\"): stdin is CLOSED so interactive programs get EOF instead of\n * hanging to the timeout; `background:true` launches GUI apps / long-running\n * processes detached and returns immediately (so they aren't SIGKILLed at\n * the timeout); a foreground timeout kills the whole process TREE.\n * \u00B7 {@link OPEN_TOOL} `Open` \u2014 open a file / URL / folder with the OS default\n * app (Windows `start`, macOS `open`, Linux `xdg-open`), non-blocking. This\n * is the unambiguous \"\uC5F4\uC5B4\uC918\" primitive so the agent doesn't have to guess\n * xdg-open vs gedit vs kate.\n *\n * Everything is GATED by config and hidden unless the user turned the capability\n * on \u2014 running arbitrary local commands from a chat is powerful, so it must be\n * visible and revocable. Lives in the MAIN process (only main may spawn\n * subprocesses). Pure helpers are exported for unit tests that never spawn.\n */\nimport { spawn, type ChildProcess } from 'node:child_process';\nimport { homedir, platform } from 'node:os';\nimport {\n readFile as fsReadFile,\n writeFile as fsWriteFile,\n appendFile as fsAppendFile,\n readdir,\n stat,\n mkdir,\n} from 'node:fs/promises';\nimport {\n resolve as pathResolve,\n relative as pathRelative,\n isAbsolute,\n join as pathJoin,\n dirname,\n extname,\n} from 'node:path';\nimport { augmentedPath, buildChildEnv } from './exec-resolve';\nimport { interaction } from './host';\n\n/** Reserved MCP \"server\" name for connector-hosted built-ins. Agents see the\n * tool as `mcp_local_<Tool>` after backend sanitization \u2014 keep it stable. */\nexport const LOCAL_SERVER = 'local';\nexport const SHELL_TOOL = 'Shell';\n/** \uB85C\uCEEC MCP \uC11C\uBC84 \uC790\uAE30\uAD00\uB9AC \uB3C4\uAD6C \u2014 \uB85C\uCEEC MCP(cfg.mcp) \uAC00 \uCF1C\uC838 \uC788\uC744 \uB54C\uB9CC \uB178\uCD9C\uB41C\uB2E4.\n * \uC5D0\uC774\uC804\uD2B8\uAC00 \uC774 PC(\uCEE4\uB125\uD130 \uB85C\uCEEC)\uC5D0\uC11C \uB3C4\uB294 MCP \uC11C\uBC84\uB97C \uC2A4\uC2A4\uB85C \uCD94\uAC00/\uC81C\uAC70/\uC870\uD68C\uD55C\uB2E4. */\nexport const MCP_ADD_TOOL = 'McpAddServer';\nexport const MCP_REMOVE_TOOL = 'McpRemoveServer';\nexport const MCP_LIST_TOOL = 'McpListServers';\nexport const OPEN_TOOL = 'Open';\n// \uBC18\uAD6C\uC870\uD654 1\uAE09 \uB85C\uCEEC \uB3C4\uAD6C \u2014 \uC178 \uBB38\uC790\uC5F4 \uC6B0\uD68C \uC5C6\uC774 \uD30C\uC77C/\uD074\uB9BD\uBCF4\uB4DC/\uC54C\uB9BC\uC744 \uB2E4\uB8EC\uB2E4.\n// \uD30C\uC77C \uACC4\uC5F4\uC740 allowedRoots \uACBD\uB85C \uC2A4\uCF54\uD504 \uC548\uC5D0\uC11C\uB9CC \uB3D9\uC791\uD55C\uB2E4(\uBC29\uC5B4\uC801 \uAC00\uB4DC).\nexport const READ_FILE_TOOL = 'ReadFile';\nexport const WRITE_FILE_TOOL = 'WriteFile';\nexport const LIST_DIR_TOOL = 'ListDir';\nexport const SEARCH_TOOL = 'Search';\nexport const CLIPBOARD_TOOL = 'Clipboard';\nexport const NOTIFY_TOOL = 'Notify';\n/** Manage long-running background jobs started with Shell(background:true):\n * list / poll (status + captured output, paginated) / kill. */\nexport const SHELL_JOB_TOOL = 'ShellJob';\n\n/** Device-local shell capability config (persisted under ConnectorConfig.localShell). */\nexport interface LocalShellConfig {\n /** Master switch for the built-in local tools. Default OFF (opt-in) \u2014 running\n * arbitrary local commands from the cloud must be turned on explicitly. */\n enabled?: boolean;\n /** Default working directory for commands. Empty \u2192 the user's home. */\n cwd?: string;\n /** Per-command wall-clock cap (ms). Default 120s; clamped to [1s, 1h]. */\n timeoutMs?: number;\n /**\n * Commands whose first token matches any of these (case-insensitive, exact\n * on the resolved program name) are refused. Empty \u2192 nothing blocked. This is\n * a light guardrail for the owner's own convenience (e.g. block `rm`), NOT a\n * security boundary \u2014 the agent runs as the logged-in user either way.\n */\n blocked?: string[];\n /**\n * Directory roots the file tools (ReadFile/WriteFile/ListDir/Search) may touch.\n * A path outside every root is refused. Empty \u2192 defaults to the user's home\n * directory. This is a real scope for the STRUCTURED file tools (unlike Shell,\n * which is unrestricted once enabled). Paths may use `~` for home.\n */\n allowedRoots?: string[];\n}\n\nexport interface LocalToolSchema {\n name: string;\n description?: string;\n inputSchema?: Record<string, unknown>;\n}\n\nexport interface LocalToolResult {\n content: Array<{ type: 'text'; text: string }>;\n isError?: boolean;\n}\n\n/** Server-attested identity of the workflow that initiated a local tool call. */\nexport interface LocalToolCallContext {\n workflowId?: string;\n workflowName?: string;\n interactionId?: string;\n}\n\n/** Normalize snake/camel-case caller identity from an authenticated bridge frame. */\nexport function localToolCallContext(raw: unknown): LocalToolCallContext {\n const value = raw && typeof raw === 'object' ? (raw as Record<string, unknown>) : {};\n const text = (input: unknown): string | undefined => {\n const normalized = String(input ?? '').trim();\n return normalized || undefined;\n };\n return {\n workflowId: text(value.workflow_id ?? value.workflowId),\n workflowName: text(value.workflow_name ?? value.workflowName),\n interactionId: text(value.interaction_id ?? value.interactionId),\n };\n}\n\nexport interface LocalToolDelegate {\n advertise(): LocalToolSchema[];\n owns(tool: string): boolean;\n callTool(\n tool: string,\n args: unknown,\n context?: LocalToolCallContext,\n ): Promise<LocalToolResult>;\n}\n\nexport type LocalNotificationHandler = (\n title: string,\n body: string,\n context?: LocalToolCallContext,\n) => Promise<boolean> | boolean;\n\nconst DEFAULT_TIMEOUT_MS = 600_000; // 10\uBD84 \u2014 \uAE34 \uC124\uCE58/\uBE4C\uB4DC/\uC2A4\uD06C\uB9BD\uD2B8 \uAE30\uBCF8 \uD5C8\uC6A9\nconst MIN_TIMEOUT_MS = 1_000;\nconst MAX_TIMEOUT_MS = 60 * 60_000;\nconst OUTPUT_CAP = 200_000; // chars kept from stdout+stderr\n/** Grace we wait for a background launch to fail (ENOENT) before reporting success. */\nconst BG_SETTLE_MS = 350;\n/** Per-stream ring-buffer cap for a background job (chars). */\nconst JOB_STREAM_CAP = 262_144; // 256KB stdout + 256KB stderr per job\n/** Max background jobs retained; oldest FINISHED jobs are evicted past this. */\nconst MAX_JOBS = 50;\n/** Max concurrently-RUNNING background jobs (finished ones don't count). */\nconst MAX_RUNNING_JOBS = 25;\n\nconst IS_WIN = platform() === 'win32';\nconst IS_MAC = platform() === 'darwin';\n\nexport function shellConfig(cfg: LocalShellConfig | undefined): Required<LocalShellConfig> {\n const c = cfg || {};\n const t = typeof c.timeoutMs === 'number' && c.timeoutMs > 0 ? c.timeoutMs : DEFAULT_TIMEOUT_MS;\n const cwd = (c.cwd || '').trim();\n const listed = Array.isArray(c.allowedRoots)\n ? c.allowedRoots.map((r) => String(r).trim()).filter(Boolean)\n : [];\n // \uAE30\uBCF8 \uC791\uC5C5 \uD3F4\uB354\uB294 **\uD56D\uC0C1** \uD30C\uC77C \uB3C4\uAD6C\uC758 \uD5C8\uC6A9 \uBC94\uC704\uC5D0 \uB4E0\uB2E4 \u2014 \uC5D0\uC774\uC804\uD2B8\n // \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uAC00 \uADF8 \uC544\uB798\uB85C \uB3D9\uAE30\uD654\uB418\uB294\uB370(local-sync) \uD5C8\uC6A9 \uD3F4\uB354 \uBAA9\uB85D\uC774 \uD648\uC774\uB098\n // \uB2E4\uB978 \uACF3\uB9CC \uAC00\uB9AC\uD0A4\uBA74, \uC5D0\uC774\uC804\uD2B8\uB294 \uC790\uAE30 \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uC870\uCC28 \uBABB \uC77D\uB294\uB2E4. \uBAA9\uB85D\uC774\n // \uBE44\uC5B4 \uC788\uC73C\uBA74 \uAE30\uBCF8(\uD648)\uB3C4 \uC720\uC9C0\uD55C\uB2E4 \u2014 cwd \uD558\uB098\uB85C \uC881\uD788\uBA74 \uD648\uC774 \uB9C9\uD78C\uB2E4.\n const allowedRoots = cwd ? [...(listed.length ? listed : ['~']), cwd] : listed;\n return {\n enabled: c.enabled === true, // opt-in (default OFF) \u2014 \uB85C\uCEEC \uC178\uC740 \uBA85\uC2DC\uC801\uC73C\uB85C \uCF1C\uC57C \uD55C\uB2E4\n cwd,\n timeoutMs: Math.max(MIN_TIMEOUT_MS, Math.min(MAX_TIMEOUT_MS, Math.round(t))),\n blocked: Array.isArray(c.blocked) ? c.blocked.map((b) => String(b).trim()).filter(Boolean) : [],\n allowedRoots,\n };\n}\n\nexport function shellEnabled(cfg: LocalShellConfig | undefined): boolean {\n return shellConfig(cfg).enabled;\n}\n\n/** Human label for the OS's native shell (shown in the tool description). */\nexport function nativeShellLabel(): string {\n if (IS_WIN) return 'PowerShell';\n if (IS_MAC) return 'zsh/bash';\n return 'bash/sh';\n}\n\n/**\n * Build the argv that runs `command` through the native shell.\n *\n * Windows \u2192 PowerShell (`-NoProfile -NonInteractive -Command <cmd>`): it is the\n * shell users expect on Windows and handles GUI-launch (`notepad`) and pipes.\n * POSIX \u2192 the user's `$SHELL` binary if given, else bash, with `-lc <cmd>` so\n * PATH/rc are loaded (GUI-launched apps otherwise miss `open`, `xdg-open`,\n * brew paths, \u2026).\n *\n * `userShellBin` is the shell EXECUTABLE (e.g. `/bin/zsh` from `$SHELL`), NOT a\n * PATH string. `explicitShell` overrides detection: 'powershell'|'cmd'|'bash'|'sh'.\n */\nexport function shellInvocation(\n command: string,\n userShellBin: string | null,\n explicitShell?: string,\n): { file: string; args: string[] } {\n const want = (explicitShell || 'default').toLowerCase();\n if (want === 'powershell' || (want === 'default' && IS_WIN)) {\n return {\n file: 'powershell.exe',\n args: ['-NoProfile', '-NonInteractive', '-Command', command],\n };\n }\n if (want === 'cmd') {\n return { file: 'cmd.exe', args: ['/d', '/s', '/c', command] };\n }\n if (want === 'bash') return { file: 'bash', args: ['-lc', command] };\n if (want === 'sh') return { file: 'sh', args: ['-lc', command] };\n // POSIX default: the user's own shell ($SHELL) if it looks like a path, else bash.\n const bin = (userShellBin || '').trim();\n const file = bin.startsWith('/') ? bin : 'bash';\n return { file, args: ['-lc', command] };\n}\n\n/** Argv that opens a file / URL / folder with the OS default handler. Returns\n * fast \u2014 the opener forks the real app and exits. */\nexport function openerInvocation(target: string): { file: string; args: string[] } {\n const t = String(target || '').trim();\n if (IS_WIN) return { file: 'cmd.exe', args: ['/d', '/s', '/c', 'start', '', t] };\n if (IS_MAC) return { file: 'open', args: [t] };\n return { file: 'xdg-open', args: [t] };\n}\n\n/** First program token of a command line (for the blocklist check). */\nexport function firstToken(command: string): string {\n const m = String(command || '')\n .trim()\n .match(/^(?:\"([^\"]+)\"|'([^']+)'|(\\S+))/);\n const raw = (m && (m[1] || m[2] || m[3])) || '';\n const base = raw.split(/[\\\\/]/).pop() || raw;\n return base.replace(/\\.(exe|cmd|bat|com|ps1)$/i, '').toLowerCase();\n}\n\nexport function isBlocked(command: string, blocked: string[]): boolean {\n if (!blocked.length) return false;\n const tok = firstToken(command);\n return blocked.some((b) => firstToken(b) === tok || b.trim().toLowerCase() === tok);\n}\n\n/**\n * \uB418\uB3CC\uB9AC\uAE30 \uC5B4\uB824\uC6B4(\uD30C\uAD34\uC801) \uBA85\uB839 \uD328\uD134. \uC77C\uBC18 \uBA85\uB839\uC740 \uC2B9\uC778 \uC5C6\uC774 \uC2E4\uD589\uD558\uB418, \uC774 \uD328\uD134\uC5D0\n * \uAC78\uB9AC\uB294 \uBA85\uB839\uB9CC \uC0AC\uC6A9\uC790 \uD655\uC778\uC744 \uBC1B\uB294\uB2E4 \u2014 \uB9C8\uCC30\uC744 \uCD5C\uC18C\uD654\uD558\uBA74\uC11C \uC0AC\uACE0\uB97C \uB9C9\uB294\uB2E4.\n * \uBCF4\uC548 \uACBD\uACC4\uAC00 \uC544\uB2C8\uB77C \"\uC2E4\uC218 \uBC29\uC9C0 \uAC8C\uC774\uD2B8\"\uB2E4 (\uC5D0\uC774\uC804\uD2B8\uB294 \uC5B4\uCC28\uD53C \uB85C\uADF8\uC778 \uC0AC\uC6A9\uC790 \uAD8C\uD55C).\n */\nconst DANGEROUS_PATTERNS: RegExp[] = [\n /\\brm\\s+-[a-z]*[rf]/i, // rm -rf / -r / -f\n /(^|[;&|`(])\\s*rm\\s+\\//i, // rm on an absolute path\n /\\bRemove-Item\\b[^\\n]*-Recurse/i,\n /\\brmdir\\s+\\/s/i,\n /\\bdel\\s+\\/[a-z]*[sf]/i,\n /\\b(mkfs|fdisk|format)\\b/i,\n /\\bdd\\b[^\\n]*\\b(of|if)=/i,\n /\\b(shutdown|reboot|halt|poweroff)\\b/i,\n /\\bchmod\\s+-R\\b/i,\n /\\bchown\\s+-R\\b/i,\n />\\s*\\/dev\\/(sd|nvme|disk|hd)/i,\n /:\\s*\\(\\s*\\)\\s*\\{\\s*:\\s*\\|\\s*:/, // fork bomb\n /\\bgit\\s+push\\b[^\\n]*--force/i,\n /\\b(curl|wget)\\b[^\\n]*\\|\\s*(sudo\\s+)?(sh|bash|zsh)\\b/i, // curl \u2026 | sh\n /\\bsudo\\s+rm\\b/i,\n];\n\n/** True if the command matches a destructive pattern that warrants confirmation. */\nexport function isDangerousShellCommand(command: string): boolean {\n const c = String(command || '');\n return DANGEROUS_PATTERNS.some((re) => re.test(c));\n}\n\n// \uC138\uC158 \uB3D9\uC548 \uC704\uD5D8 \uBA85\uB839\uC744 \uD55C \uBC88 \uC2B9\uC778\uD558\uBA74 \uC774\uD6C4 \uB418\uBB3B\uC9C0 \uC54A\uB294\uB2E4 (\uC0AC\uC6A9\uC790 \uC120\uD0DD).\nlet sessionApprovedDangerous = false;\n\n/** \uC704\uD5D8 \uD328\uD134\uC774\uBA74 \uC0AC\uC6A9\uC790\uC5D0\uAC8C \uD655\uC778. false = \uAC70\uBD80. dialog \uB294 main \uD504\uB85C\uC138\uC2A4\uC5D0\uC11C\uB9CC. */\nasync function ensureDangerousApproval(command: string): Promise<boolean> {\n if (!isDangerousShellCommand(command)) return true;\n if (sessionApprovedDangerous) return true;\n const ask = interaction().confirmDangerous;\n // \uBB3C\uC744 \uBC29\uBC95\uC774 \uC5C6\uC73C\uBA74 **\uAC70\uBD80**\uD55C\uB2E4. \"\uBB3C\uC744 \uD544\uC694\uAC00 \uC5C6\uB2E4\"\uAC00 \uC544\uB2C8\uB77C \"\uB3D9\uC758\uB97C \uBC1B\uC744 \uC218\n // \uC5C6\uB2E4\"\uC774\uACE0, \uADF8\uB54C \uC2E4\uD589\uD558\uBA74 \uC0AC\uC6A9\uC790\uAC00 \uBAA8\uB974\uB294 \uC0AC\uC774\uC5D0 \uD30C\uAD34\uC801\uC778 \uBA85\uB839\uC774 \uB3C8\uB2E4.\n if (!ask) return false;\n try {\n const answer = await ask(command);\n if (answer === 'session') {\n sessionApprovedDangerous = true;\n return true;\n }\n return answer === 'once';\n } catch {\n // \uBB3C\uB2E4\uAC00 \uC2E4\uD328\uD558\uBA74(\uCC3D\uC774 \uC5C6\uB2E4\uAC70\uB098) \uC548\uC804\uD558\uAC8C \uAC70\uBD80\uD55C\uB2E4.\n return false;\n }\n}\n\n/** \uD655\uC778 \uCC3D\uC5D0 \uC4F8 \uBB38\uAD6C \u2014 \uD638\uC2A4\uD2B8\uAC00 \uC5B4\uB5A4 UI \uB85C \uBB3B\uB4E0 **\uAC19\uC740 \uB9D0**\uC744 \uD558\uB3C4\uB85D \uC5EC\uAE30 \uB454\uB2E4.\n * \uB370\uC2A4\uD06C\uD1B1\uC740 \uB2E4\uC774\uC5BC\uB85C\uADF8\uB85C, \uD130\uBBF8\uB110\uC740 \uD504\uB86C\uD504\uD2B8\uB85C \uBB3B\uC9C0\uB9CC \uC0AC\uC6A9\uC790\uAC00 \uC77D\uB294 \uACBD\uACE0\uB294 \uD558\uB098\uC5EC\uC57C \uD55C\uB2E4. */\nexport const DANGEROUS_COMMAND_PROMPT = {\n title: '\uC704\uD5D8\uD560 \uC218 \uC788\uB294 \uBA85\uB839 \uC2E4\uD589 \uD655\uC778',\n message: 'XGEN \uC5D0\uC774\uC804\uD2B8\uAC00 \uC774 PC \uC5D0\uC11C \uB418\uB3CC\uB9AC\uAE30 \uC5B4\uB824\uC6B4 \uBA85\uB839\uC744 \uC2E4\uD589\uD558\uB824 \uD569\uB2C8\uB2E4.',\n detail: (command: string) => command,\n} as const;\n\n/** Clamp + label combined stdout/stderr into an MCP text result. */\nexport function shapeResult(\n stdout: string,\n stderr: string,\n code: number | null,\n signal: string | null,\n): LocalToolResult {\n const parts: string[] = [];\n const out = stdout.length > OUTPUT_CAP ? stdout.slice(0, OUTPUT_CAP) + '\\n\u2026(truncated)' : stdout;\n const err = stderr.length > OUTPUT_CAP ? stderr.slice(0, OUTPUT_CAP) + '\\n\u2026(truncated)' : stderr;\n if (out.trim()) parts.push(out.replace(/\\s+$/, ''));\n if (err.trim()) parts.push(`STDERR:\\n${err.replace(/\\s+$/, '')}`);\n const failed = signal != null || (code != null && code !== 0);\n if (signal) parts.push(`(terminated by signal ${signal})`);\n else if (code != null && code !== 0) parts.push(`(exit code ${code})`);\n return {\n content: [{ type: 'text', text: parts.join('\\n\\n') || '(no output)' }],\n isError: failed,\n };\n}\n\n/**\n * \uB3D9\uAE30\uD654\uB41C \uC5D0\uC774\uC804\uD2B8 \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uC548\uB0B4 \u2014 \uB3C4\uAD6C \uC124\uBA85\uC5D0 \uBD99\uB294 \uACF5\uD1B5 \uBB38\uC7A5.\n *\n * \uCEE4\uB125\uD130 \uC138\uC158\uC758 \uC5D0\uC774\uC804\uD2B8\uB294 \uC11C\uBC84 sandbox \uAC00 \uC544\uB2C8\uB77C **\uC774 PC \uC758 \uD3F4\uB354**\uB97C \uC790\uAE30\n * \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4\uB85C \uC4F4\uB2E4(local-sync \uAC00 \uC11C\uBC84 \uC800\uC7A5\uC18C\uC640 \uB9DE\uCD98\uB2E4). \uBAA8\uB378\uC774 \uC5B4\uB290 \uB3C4\uAD6C\uB97C\n * \uACE0\uB97C\uC9C0\uB294 \uC774 \uC124\uBA85\uC774 \uC804\uBD80\uC774\uBBC0\uB85C, \uC5EC\uAE30\uC11C \uBA85\uC2DC\uC801\uC73C\uB85C \uC54C\uB824\uC57C \uB85C\uCEEC \uB3C4\uAD6C\uB97C \uC4F4\uB2E4.\n */\nexport const SYNCED_WORKSPACE_NOTE =\n `\\nAGENT WORKSPACE ON THIS COMPUTER: when connected through this desktop connector, ` +\n `your own agent workspace is synced to a LOCAL folder \u2014 under the configured default ` +\n `working folder, one subfolder per connected agent (named after the agent). PREFER ` +\n `working there with these local tools; every change syncs back to your server ` +\n `workspace automatically, so web sessions and the sandbox see the same files.`;\n\n/** The Shell tool schema advertised to the agent. */\n/** McpAddServer \u2014 \uC774 PC(\uCEE4\uB125\uD130 \uB85C\uCEEC)\uC5D0 MCP \uC11C\uBC84\uB97C \uB4F1\uB85D/\uAC31\uC2E0\uD558\uACE0 \uADF8 \uB3C4\uAD6C\uB97C \uC9C0\uAE08 \uC138\uC158\n * \uC5D0\uC774\uC804\uD2B8\uB4E4\uC5D0 \uBD99\uC778\uB2E4. \uD45C\uC900 mcp.json \uC11C\uBC84 \uD56D\uBAA9\uACFC \uAC19\uC740 \uD544\uB4DC\uB97C \uBC1B\uB294\uB2E4. */\nexport function mcpAddServerToolSchema(): LocalToolSchema {\n return {\n name: MCP_ADD_TOOL,\n description:\n \"Register (or update) a local MCP server ON THE USER'S OWN COMPUTER (this connector) and attach \" +\n 'its tools to the current session agents. Same fields as a standard mcp.json server entry. ' +\n 'stdio: give `command` (+ optional `args`, `env`); http/sse: give `url` (+ optional `headers`, ' +\n \"`auth`). If a server with the same `name` exists it is replaced. Runs in the connector's local \" +\n 'environment only \u2014 NOT the cloud. Example (Atlassian, local uvx): {name:\"atlassian\", ' +\n 'command:\"uvx\", args:[\"mcp-atlassian\"], env:{ATLASSIAN_BASE_URL:\"https://your-site.atlassian.net\", ' +\n 'ATLASSIAN_USERNAME:\"you@example.com\", ATLASSIAN_API_TOKEN:\"...\"}}. Example (remote proxy): ' +\n '{name:\"atlassian\", command:\"npx\", args:[\"-y\",\"mcp-remote\",\"https://mcp.atlassian.com/v1/mcp/authv2\"]}. ' +\n 'auth:\"oauth\" servers need a one-time browser authorization by the user before tools connect. ' +\n 'Returns the connection result and the discovered tool names.',\n inputSchema: {\n type: 'object',\n properties: {\n name: { type: 'string', description: 'Unique server id (namespaces its tools).' },\n transport: {\n type: 'string',\n enum: ['stdio', 'http', 'sse'],\n description: 'Optional. Inferred from url (http) / command (stdio) when omitted.',\n },\n command: { type: 'string', description: 'stdio: launch command (e.g. \"uvx\", \"npx\").' },\n args: {\n type: 'array',\n items: { type: 'string' },\n description: 'stdio: argv for the command.',\n },\n env: {\n type: 'object',\n additionalProperties: { type: 'string' },\n description: 'stdio: extra env (API tokens etc.) merged over the connector env.',\n },\n url: { type: 'string', description: 'http/sse: the MCP endpoint URL.' },\n headers: {\n type: 'object',\n additionalProperties: { type: 'string' },\n description: 'http/sse: extra request headers (e.g. Authorization).',\n },\n auth: {\n type: 'string',\n enum: ['none', 'oauth'],\n description: \"http/sse auth. Default 'none'.\",\n },\n },\n required: ['name'],\n },\n };\n}\n\n/** McpRemoveServer \u2014 \uB85C\uCEEC MCP \uC11C\uBC84\uB97C \uD574\uC81C\uD558\uACE0(\uD504\uB85C\uC138\uC2A4 \uC885\uB8CC) \uADF8 \uB3C4\uAD6C\uB97C \uB5C0\uB2E4. */\nexport function mcpRemoveServerToolSchema(): LocalToolSchema {\n return {\n name: MCP_REMOVE_TOOL,\n description:\n 'Remove a local MCP server (by name) from this connector: stop its process/connection and detach ' +\n 'its tools from the session agents. Also clears its stored secrets. No-op if it does not exist.',\n inputSchema: {\n type: 'object',\n properties: { name: { type: 'string', description: 'The server id to remove.' } },\n required: ['name'],\n },\n };\n}\n\n/** McpListServers \u2014 \uC774 PC \uC5D0 \uB4F1\uB85D\uB41C \uB85C\uCEEC MCP \uC11C\uBC84\uC640 \uC5F0\uACB0 \uC0C1\uD0DC\u00B7\uB3C4\uAD6C \uC218\uB97C \uC870\uD68C\uD55C\uB2E4. */\nexport function mcpListServersToolSchema(): LocalToolSchema {\n return {\n name: MCP_LIST_TOOL,\n description:\n 'List the local MCP servers registered on this connector with their transport, enabled/connected ' +\n 'state, and the tools each exposes. Secret values are never returned. Use before add/remove.',\n inputSchema: { type: 'object', properties: {} },\n };\n}\n\nexport function shellToolSchema(): LocalToolSchema {\n return {\n name: SHELL_TOOL,\n description:\n `Run ONE command on the USER'S OWN COMPUTER (the local desktop where this connector runs), ` +\n `through its native shell (${nativeShellLabel()}), as the logged-in user. This is the ` +\n `physical machine \u2014 NOT the cloud workspace/sandbox. Use it to operate that computer: run ` +\n `scripts, read/write local files, inspect the system, launch apps.` +\n SYNCED_WORKSPACE_NOTE +\n `\\n` +\n `IMPORTANT for reliability:\\n` +\n `\u2022 Non-interactive only \u2014 stdin is closed, so REPLs/prompts (bash, python with no args, ` +\n `\\`read\\`, pagers) return immediately instead of hanging. Pass the full command each call.\\n` +\n `\u2022 To launch a GUI app or a long-running/never-exiting process (editors like notepad/gedit, ` +\n `servers, watchers) \u2014 or ANY job that may run longer than a couple of minutes \u2014 set ` +\n `background:true. It starts detached, returns a job_id at once, and is NOT killed at the ` +\n `timeout; its output is captured. Poll it later with the ShellJob tool (action:'poll', job_id).\\n` +\n `\u2022 To just open a file/URL/folder with its default app, prefer the Open tool.\\n` +\n `Returns combined stdout/stderr and the exit code (foreground); a job_id (background). For huge ` +\n `output, pass head/tail (lines) or max_bytes to page it.`,\n inputSchema: {\n type: 'object',\n properties: {\n command: { type: 'string', description: 'The shell command line to execute.' },\n background: {\n type: 'boolean',\n description:\n 'Launch detached and return a job_id immediately (output captured, pollable via ShellJob). ' +\n 'Use for GUI apps and long-running jobs so they keep running and are not killed at the timeout.',\n },\n cwd: {\n type: 'string',\n description:\n 'Working directory (absolute). Defaults to the configured directory or home.',\n },\n shell: {\n type: 'string',\n enum: ['default', 'powershell', 'cmd', 'bash', 'sh'],\n description: \"Shell to use. 'default' picks the OS native shell.\",\n },\n timeout_ms: {\n type: 'integer',\n description: 'Optional per-command timeout override (ms). Ignored when background=true.',\n },\n tail: {\n type: 'integer',\n description: 'Return only the last N lines of output (for chatty commands).',\n },\n head: { type: 'integer', description: 'Return only the first N lines of output.' },\n max_bytes: {\n type: 'integer',\n description: `Cap returned output bytes (default/cap ${OUTPUT_CAP}).`,\n },\n },\n required: ['command'],\n },\n };\n}\n\n/** The ShellJob tool schema \u2014 manage background jobs (G6/G13). */\nexport function shellJobToolSchema(): LocalToolSchema {\n return {\n name: SHELL_JOB_TOOL,\n description:\n `Manage long-running background jobs started with Shell(background:true) on the USER'S OWN ` +\n `COMPUTER. This is how you run work that outlives a single tool call: start it in the ` +\n `background, then poll it here until it finishes.\\n` +\n `\u2022 action:'list' \u2014 show all recent/running jobs (id, status, pid, duration, command).\\n` +\n `\u2022 action:'poll' (job_id) \u2014 status + captured stdout/stderr (paginated: tail default, or head/max_bytes).\\n` +\n `\u2022 action:'kill' (job_id) \u2014 terminate a running job (whole process tree).`,\n inputSchema: {\n type: 'object',\n properties: {\n action: { type: 'string', enum: ['list', 'poll', 'kill'], description: \"Default 'list'.\" },\n job_id: {\n type: 'string',\n description: 'The job id returned by Shell(background:true). Required for poll/kill.',\n },\n tail: { type: 'integer', description: 'poll: last N lines (default 200).' },\n head: { type: 'integer', description: 'poll: first N lines.' },\n max_bytes: { type: 'integer', description: 'poll: cap returned output bytes.' },\n },\n },\n };\n}\n\n/** The Open tool schema. */\nexport function openToolSchema(): LocalToolSchema {\n return {\n name: OPEN_TOOL,\n description:\n `Open a file, folder, or URL on the USER'S OWN COMPUTER with its default application. ` +\n `Non-blocking \u2014 the app launches and this returns immediately. Use this for \"open <file>\", ` +\n `\"show me <folder>\", \"open <url>\". Safe by construction: only http/https/mailto/tel/ftp URLs ` +\n `and filesystem paths within the allowed folders are opened (javascript:/data: and unknown ` +\n `schemes are refused). To launch an app by name or run a command, use Shell(background:true).`,\n inputSchema: {\n type: 'object',\n properties: {\n target: {\n type: 'string',\n description:\n 'A file/folder path (within allowed folders) or an http/https/mailto/tel/ftp URL.',\n },\n },\n required: ['target'],\n },\n };\n}\n\n/** Coerce loose MCP args (agents send strings/objects) into a shell request. */\nexport function coerceShellArgs(args: unknown): {\n command: string;\n cwd?: string;\n shell?: string;\n timeoutMs?: number;\n background: boolean;\n} {\n const a = (args && typeof args === 'object' ? args : {}) as Record<string, unknown>;\n const command = typeof a.command === 'string' ? a.command : String(a.command ?? '');\n const cwd = typeof a.cwd === 'string' && a.cwd.trim() ? a.cwd.trim() : undefined;\n const shell = typeof a.shell === 'string' ? a.shell : undefined;\n const t = a.timeout_ms ?? a.timeoutMs;\n const timeoutMs = typeof t === 'number' && t > 0 ? t : undefined;\n const bg = a.background ?? a.detach ?? a.detached;\n const background = bg === true || bg === 'true' || bg === 1;\n return { command, cwd, shell, timeoutMs, background };\n}\n\nexport function coerceOpenArgs(args: unknown): { target: string } {\n const a = (args && typeof args === 'object' ? args : {}) as Record<string, unknown>;\n const raw = a.target ?? a.path ?? a.url ?? a.file;\n return { target: typeof raw === 'string' ? raw : String(raw ?? '') };\n}\n\ninterface SpawnCaptured {\n code: number | null;\n signal: string | null;\n stdout: string;\n stderr: string;\n error?: Error;\n timedOut?: boolean;\n}\n\n/** Kill a child and, on POSIX, its whole process group (so a shell's children \u2014\n * a foreground GUI, a subprocess tree \u2014 go with it and don't orphan). */\nfunction killTree(child: ChildProcess, detachedGroup: boolean): void {\n try {\n if (IS_WIN) {\n if (child.pid)\n spawn('taskkill', ['/pid', String(child.pid), '/t', '/f'], { windowsHide: true });\n else child.kill('SIGKILL');\n } else if (detachedGroup && child.pid) {\n process.kill(-child.pid, 'SIGKILL'); // negative pid = the process group\n } else {\n child.kill('SIGKILL');\n }\n } catch {\n try {\n child.kill('SIGKILL');\n } catch {\n /* already gone */\n }\n }\n}\n\n// \u2500\u2500 Background job registry (G6/G13) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n// Shell(background:true) registers a job here so the agent can later poll its\n// output / status and kill it \u2014 decoupling long jobs from any single tool-call\n// timeout (the connector allows up to 1h; the backend tool call is ~125s).\n\ntype JobStatus = 'running' | 'exited' | 'killed' | 'error';\n\ninterface BgJob {\n id: string;\n command: string;\n pid?: number;\n child: ChildProcess;\n detachedGroup: boolean;\n status: JobStatus;\n code: number | null;\n signal: string | null;\n startedAt: number;\n endedAt?: number;\n stdout: string;\n stderr: string;\n errorMsg?: string;\n}\n\nconst bgJobs = new Map<string, BgJob>();\nlet bgJobSeq = 0;\n\nfunction newJobId(): string {\n bgJobSeq += 1;\n return `job-${Date.now().toString(36)}-${bgJobSeq}`;\n}\n\n/** Drop the oldest FINISHED jobs once the registry exceeds MAX_JOBS. Running\n * jobs are never evicted. */\nfunction evictFinishedJobs(): void {\n if (bgJobs.size <= MAX_JOBS) return;\n const finished = [...bgJobs.values()]\n .filter((j) => j.status !== 'running')\n .sort((a, b) => (a.endedAt ?? a.startedAt) - (b.endedAt ?? b.startedAt));\n for (const j of finished) {\n if (bgJobs.size <= MAX_JOBS) break;\n bgJobs.delete(j.id);\n }\n}\n\n/** Append to a job stream buffer, keeping only the last JOB_STREAM_CAP chars. */\nfunction appendCapped(prev: string, chunk: string): string {\n const next = prev + chunk;\n return next.length > JOB_STREAM_CAP ? next.slice(-JOB_STREAM_CAP) : next;\n}\n\n/** Paginate text by lines (head/tail) then clamp to a byte cap. Tail-biased so\n * logs show their most recent output when clamped. Returns the shaped text plus\n * whether it was truncated and the full byte length. */\nexport function paginate(\n text: string,\n opts: { head?: number; tail?: number; maxBytes?: number },\n): { text: string; truncated: boolean; totalBytes: number } {\n const totalBytes = Buffer.byteLength(text);\n let out = text;\n let truncated = false;\n const head = Number(opts.head) > 0 ? Math.floor(Number(opts.head)) : 0;\n const tail = Number(opts.tail) > 0 ? Math.floor(Number(opts.tail)) : 0;\n if (head || tail) {\n const lines = text.split('\\n');\n if (head) out = lines.slice(0, head).join('\\n');\n else out = lines.slice(-tail).join('\\n');\n if (out.length < text.length) truncated = true;\n }\n const cap = Math.max(\n 1,\n Math.min(\n OUTPUT_CAP,\n Number(opts.maxBytes) > 0 ? Math.floor(Number(opts.maxBytes)) : OUTPUT_CAP,\n ),\n );\n if (Buffer.byteLength(out) > cap) {\n // Cap on BYTES (not UTF-16 units), snapping to a UTF-8 character boundary so\n // we never split a multibyte char (no U+FFFD) and never exceed `cap` bytes.\n // Continuation bytes are 0b10xxxxxx (0x80\u20130xBF).\n const buf = Buffer.from(out, 'utf8');\n if (head) {\n let end = cap;\n while (end > 0 && (buf[end] & 0xc0) === 0x80) end--; // back up before a partial char\n out = buf.subarray(0, end).toString('utf8');\n } else {\n let start = buf.length - cap;\n while (start < buf.length && (buf[start] & 0xc0) === 0x80) start++; // advance to a lead byte\n out = buf.subarray(start).toString('utf8');\n }\n truncated = true;\n }\n return { text: out, truncated, totalBytes };\n}\n\n/** Allowed URL schemes for the Open tool. Everything else (javascript:, data:,\n * vbscript:, unknown app schemes) is refused. `file:` is treated as a path. */\nconst SAFE_OPEN_SCHEMES = new Set(['http', 'https', 'mailto', 'tel', 'ftp', 'ftps']);\n\n/** Classify an Open target into a safe URL, a filesystem path, or blocked. Pure\n * (unit-tested) \u2014 the actual open uses Electron's shell API (no shell string). */\nexport function classifyOpenTarget(\n target: string,\n):\n | { kind: 'url'; value: string }\n | { kind: 'path'; value: string }\n | { kind: 'blocked'; reason: string } {\n const t = String(target || '').trim();\n if (!t) return { kind: 'blocked', reason: 'target \uC774 \uBE44\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.' };\n const m = t.match(/^([a-zA-Z][a-zA-Z0-9+.-]*):/);\n if (m) {\n const scheme = m[1].toLowerCase();\n if (scheme === 'file')\n return { kind: 'path', value: t.replace(/^file:\\/\\//i, '').replace(/^file:/i, '') };\n if (SAFE_OPEN_SCHEMES.has(scheme)) return { kind: 'url', value: t };\n // Windows drive letters (\"C:\\\u2026\") look like a scheme \u2014 treat as a path.\n if (IS_WIN && /^[a-zA-Z]:[\\\\/]/.test(t)) return { kind: 'path', value: t };\n return {\n kind: 'blocked',\n reason: `\uD5C8\uC6A9\uB418\uC9C0 \uC54A\uC740 \uC2A4\uD0B4 '${scheme}:' (javascript/data \uB4F1\uC740 \uCC28\uB2E8).`,\n };\n }\n return { kind: 'path', value: t };\n}\n\n/** Extensions we never text-search (binary/asset). */\nconst BINARY_EXT = new Set([\n '.png',\n '.jpg',\n '.jpeg',\n '.gif',\n '.webp',\n '.ico',\n '.bmp',\n '.pdf',\n '.zip',\n '.gz',\n '.tar',\n '.7z',\n '.rar',\n '.mp3',\n '.mp4',\n '.mov',\n '.avi',\n '.wav',\n '.ogg',\n '.woff',\n '.woff2',\n '.ttf',\n '.eot',\n '.so',\n '.dll',\n '.dylib',\n '.exe',\n '.bin',\n '.class',\n '.o',\n]);\n\nfunction resolveOne(p: string, base: string): string {\n const home = homedir();\n const expanded = p.startsWith('~') ? home + p.slice(1) : p;\n return isAbsolute(expanded) ? pathResolve(expanded) : pathResolve(base, expanded);\n}\n\n/**\n * Resolve a user-supplied path and confirm it stays within an allowed root.\n * Empty roots \u2192 the user's home directory. Returns the absolute path, or null\n * when it escapes every root. `~` expands to home; relative paths resolve\n * against home.\n */\nexport function resolveWithinRoots(input: string, roots: string[]): string | null {\n const home = homedir();\n const effective = (roots && roots.length ? roots : [home]).map((r) => resolveOne(r, home));\n const abs = resolveOne(String(input || ''), home);\n for (const root of effective) {\n const rel = pathRelative(root, abs);\n if (rel === '' || (!rel.startsWith('..') && !isAbsolute(rel))) return abs;\n }\n return null;\n}\n\nexport function readFileToolSchema(): LocalToolSchema {\n return {\n name: READ_FILE_TOOL,\n description:\n \"Read a text file on the USER'S OWN COMPUTER (the local desktop), within the \" +\n 'allowed folders. Prefer this over `Shell cat` \u2014 it distinguishes \u201Cnot found\u201D ' +\n 'from \u201Cno permission\u201D cleanly. Returns UTF-8 text (truncated at maxBytes).' +\n SYNCED_WORKSPACE_NOTE,\n inputSchema: {\n type: 'object',\n properties: {\n path: {\n type: 'string',\n description: 'File path. Absolute, ~ for home, or relative to home.',\n },\n maxBytes: {\n type: 'number',\n description: `Max bytes to return (default/cap ${OUTPUT_CAP}).`,\n },\n },\n required: ['path'],\n },\n };\n}\n\nexport function writeFileToolSchema(): LocalToolSchema {\n return {\n name: WRITE_FILE_TOOL,\n description:\n \"Write (or append to) a text file on the USER'S OWN COMPUTER, within the allowed \" +\n 'folders. Creates parent directories as needed. Prefer this over shell redirection.' +\n SYNCED_WORKSPACE_NOTE,\n inputSchema: {\n type: 'object',\n properties: {\n path: {\n type: 'string',\n description: 'File path. Absolute, ~ for home, or relative to home.',\n },\n content: { type: 'string', description: 'Text to write.' },\n mode: { type: 'string', enum: ['overwrite', 'append'], description: 'Default overwrite.' },\n },\n required: ['path', 'content'],\n },\n };\n}\n\nexport function listDirToolSchema(): LocalToolSchema {\n return {\n name: LIST_DIR_TOOL,\n description:\n \"List a directory on the USER'S OWN COMPUTER (within allowed folders). Shows type/size/name.\" +\n SYNCED_WORKSPACE_NOTE,\n inputSchema: {\n type: 'object',\n properties: { path: { type: 'string', description: 'Directory path (default: home).' } },\n },\n };\n}\n\nexport function searchToolSchema(): LocalToolSchema {\n return {\n name: SEARCH_TOOL,\n description:\n \"Recursively search text files under a folder on the USER'S OWN COMPUTER (within allowed \" +\n 'folders) for a literal substring. Skips node_modules/.git/binaries. Returns path:line: match.',\n inputSchema: {\n type: 'object',\n properties: {\n query: { type: 'string', description: 'Literal substring to find.' },\n path: { type: 'string', description: 'Root folder to search (default: home).' },\n maxResults: { type: 'number', description: 'Max matches (default 100, cap 500).' },\n },\n required: ['query'],\n },\n };\n}\n\nexport function clipboardToolSchema(): LocalToolSchema {\n return {\n name: CLIPBOARD_TOOL,\n description: \"Read or write the USER'S system clipboard (plain text).\",\n inputSchema: {\n type: 'object',\n properties: {\n action: {\n type: 'string',\n enum: ['read', 'write'],\n description: 'read (default) or write.',\n },\n text: { type: 'string', description: 'Text to put on the clipboard when action=write.' },\n },\n },\n };\n}\n\nexport function notifyToolSchema(): LocalToolSchema {\n return {\n name: NOTIFY_TOOL,\n description: \"Show a desktop notification on the USER'S OWN COMPUTER.\",\n inputSchema: {\n type: 'object',\n properties: {\n title: { type: 'string', description: 'Notification title.' },\n body: { type: 'string', description: 'Notification body.' },\n },\n required: ['title'],\n },\n };\n}\n\nexport class LocalToolProvider {\n private cfg: Required<LocalShellConfig> = shellConfig(undefined);\n private delegate: LocalToolDelegate | null = null;\n /** \uC11C\uBC84 \uB7F0\uD0C0\uC784\uC774 \uC774 PC \uB97C \uC2E4\uD589 \uD658\uACBD\uC73C\uB85C \uC4F0\uB294 \uB0B4\uBD80 \uBE0C\uB9AC\uC9C0 (workspace-bridge-tools). */\n private workspaceBridge: LocalToolDelegate | null = null;\n /** main \uC758 \uACF5\uD1B5 NotificationCenter. \uC8FC\uC785\uD574 Node \uB2E8\uC704 \uD14C\uC2A4\uD2B8\uB294 Electron \uC744 \uC694\uAD6C\uD558\uC9C0 \uC54A\uB294\uB2E4. */\n private notificationHandler: LocalNotificationHandler | null = null;\n /** \uB85C\uCEEC MCP \uC790\uAE30\uAD00\uB9AC(McpAddServer/McpRemoveServer/McpListServers). \uB85C\uCEEC MCP \uAC00 \uCF1C\uC838\n * \uC788\uC744 \uB54C\uB9CC \uB3C4\uAD6C\uB97C \uAD11\uACE0\uD55C\uB2E4 \u2014 \uC774 delegate \uC790\uC2E0\uC774 \uAC8C\uC774\uD2B8\uB97C \uD310\uB2E8\uD55C\uB2E4. */\n private mcpAdmin: LocalToolDelegate | null = null;\n\n configure(cfg: LocalShellConfig | undefined, delegate?: LocalToolDelegate): void {\n this.cfg = shellConfig(cfg);\n this.delegate = delegate ?? null;\n }\n\n /** \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uBE0C\uB9AC\uC9C0 \uBC30\uC120 \u2014 \uB85C\uCEEC \uB3D9\uAE30\uD654 \uB9E4\uB2C8\uC800\uAC00 \uC900\uBE44\uB41C \uB4A4 \uD55C \uBC88 \uAC74\uB2E4. */\n configureWorkspaceBridge(bridge: LocalToolDelegate | null): void {\n this.workspaceBridge = bridge;\n }\n\n configureNotificationHandler(handler: LocalNotificationHandler | null): void {\n this.notificationHandler = handler;\n }\n\n /** \uB85C\uCEEC MCP \uC790\uAE30\uAD00\uB9AC delegate \uBC30\uC120(syncMcp \uC5D0\uC11C). null \uC774\uBA74 \uBBF8\uB178\uCD9C. */\n configureMcpAdmin(admin: LocalToolDelegate | null): void {\n this.mcpAdmin = admin;\n }\n\n /** True iff this call frame belongs to a built-in tool (server === LOCAL_SERVER). */\n owns(server: string): boolean {\n return server === LOCAL_SERVER;\n }\n\n /** Tools advertised into the catalog. Empty when the capability is off. */\n /**\n * \uC178/\uD30C\uC77C \uB3C4\uAD6C\uC758 **\uC804\uCCB4 \uCE74\uD0C8\uB85C\uADF8** \u2014 \uCF1C\uC838 \uC788\uB294\uC9C0\uC640 \uBB34\uAD00\uD558\uAC8C.\n *\n * `advertise()` \uB294 \"\uC9C0\uAE08 \uC5D0\uC774\uC804\uD2B8\uC5D0\uAC8C \uB178\uCD9C\uB418\uB294 \uAC83\"\uC774\uB77C \uAEBC\uC838 \uC788\uC73C\uBA74 \uBE48 \uBAA9\uB85D\uC774\uB2E4.\n * \uADF8\uAC74 \uC11C\uBC84\uC5D0 \uAD11\uACE0\uD560 \uB54C\uB294 \uB9DE\uC9C0\uB9CC, \uC0AC\uC6A9\uC790\uAC00 \"\uC774 \uB3C4\uAD6C\uB85C \uBB58 \uD560 \uC218 \uC788\uC9C0\"\uB97C \uBB3C\uC744 \uB54C\uB294\n * \uC544\uBB34 \uB2F5\uC774 \uC548 \uB41C\uB2E4. \uB450 \uC9C8\uBB38\uC740 \uB2E4\uB974\uBBC0\uB85C \uB2F5\uB3C4 \uB458\uC774\uB2E4.\n */\n catalog(): LocalToolSchema[] {\n return [\n shellToolSchema(),\n shellJobToolSchema(),\n openToolSchema(),\n readFileToolSchema(),\n writeFileToolSchema(),\n listDirToolSchema(),\n searchToolSchema(),\n clipboardToolSchema(),\n notifyToolSchema(),\n ];\n }\n\n advertise(): LocalToolSchema[] {\n const shell = this.cfg.enabled\n ? [\n shellToolSchema(),\n shellJobToolSchema(),\n openToolSchema(),\n readFileToolSchema(),\n writeFileToolSchema(),\n listDirToolSchema(),\n searchToolSchema(),\n clipboardToolSchema(),\n notifyToolSchema(),\n ]\n : [];\n // \uC6CC\uD06C\uC2A4\uD398\uC774\uC2A4 \uBE0C\uB9AC\uC9C0(_Exec \uB4F1)\uB294 \uB85C\uCEEC \uB3C4\uAD6C\uC640 \uAC19\uC740 \uB2A5\uB825 \uB4F1\uAE09\uC774\uBBC0\uB85C \uAC19\uC740\n // \uC2A4\uC704\uCE58\uC5D0 \uBB36\uC778\uB2E4. `_` \uC811\uB450\uB77C \uC11C\uBC84\uAC00 LLM \uB178\uCD9C\uC5D0\uC11C \uAC78\uB7EC\uB0B8\uB2E4 \u2014 \uCE74\uD0C8\uB85C\uADF8\uC5D0\uB294\n // \uC2E4\uB824\uC57C \uC11C\uBC84 \uC5B4\uB311\uD130\uAC00 \uC874\uC7AC\uB97C \uD655\uC778\uD55C\uB2E4.\n const bridge = this.cfg.enabled ? (this.workspaceBridge?.advertise() ?? []) : [];\n // MCP \uC790\uAE30\uAD00\uB9AC \uB3C4\uAD6C\uB294 \uB85C\uCEEC \uC178(cfg.enabled) \uACFC \uBB34\uAD00\uD558\uAC8C \uB85C\uCEEC MCP \uC2A4\uC704\uCE58\uB85C \uAC8C\uC774\uD2B8\uB41C\uB2E4\n // (delegate \uAC00 \uC2A4\uC2A4\uB85C \uD310\uB2E8) \u2014 \uB85C\uCEEC MCP \uB9CC \uCF1C\uB3C4 \uC5D0\uC774\uC804\uD2B8\uAC00 \uC11C\uBC84\uB97C \uCD94\uAC00/\uC81C\uAC70\uD560 \uC218 \uC788\uB2E4.\n const mcpAdmin = this.mcpAdmin?.advertise() ?? [];\n return [...shell, ...bridge, ...mcpAdmin, ...(this.delegate?.advertise() ?? [])];\n }\n\n async callTool(\n tool: string,\n args: unknown,\n context?: LocalToolCallContext,\n ): Promise<LocalToolResult> {\n if (this.delegate?.owns(tool)) return this.delegate.callTool(tool, args, context);\n // MCP \uC790\uAE30\uAD00\uB9AC \uB3C4\uAD6C\uB294 \uB85C\uCEEC \uC178 \uAC8C\uC774\uD2B8 \uC774\uC804\uC5D0 \uCC98\uB9AC(\uB85C\uCEEC MCP \uC2A4\uC704\uCE58\uB85C\uB9CC \uAC8C\uC774\uD2B8\uB428).\n if (this.mcpAdmin?.owns(tool)) return this.mcpAdmin.callTool(tool, args);\n if (!this.cfg.enabled) throw new Error('\uB85C\uCEEC \uB3C4\uAD6C \uC811\uADFC\uC774 \uAEBC\uC838 \uC788\uC2B5\uB2C8\uB2E4 (\uC124\uC815 > \uB85C\uCEEC \uB3C4\uAD6C).');\n if (this.workspaceBridge?.owns(tool)) return this.workspaceBridge.callTool(tool, args);\n if (tool === SHELL_TOOL) return this.shell(args);\n if (tool === SHELL_JOB_TOOL) return this.shellJob(args);\n if (tool === OPEN_TOOL) return this.open(args);\n if (tool === READ_FILE_TOOL) return this.readFile(args);\n if (tool === WRITE_FILE_TOOL) return this.writeFile(args);\n if (tool === LIST_DIR_TOOL) return this.listDir(args);\n if (tool === SEARCH_TOOL) return this.search(args);\n if (tool === CLIPBOARD_TOOL) return this.clipboard(args);\n if (tool === NOTIFY_TOOL) return this.notify(args, context);\n throw new Error(`unknown local tool: ${tool}`);\n }\n\n /** Resolve + scope-check a file path against allowedRoots (throws if outside). */\n private guardPath(p: unknown): string {\n const abs = resolveWithinRoots(String(p ?? ''), this.cfg.allowedRoots);\n if (!abs) {\n throw new Error(\n `\uACBD\uB85C\uAC00 \uD5C8\uC6A9\uB41C \uBC94\uC704 \uBC16\uC785\uB2C8\uB2E4: ${String(p ?? '')} ` +\n `(\uC124\uC815 > \uB85C\uCEEC \uB3C4\uAD6C > \uD5C8\uC6A9 \uD3F4\uB354\uC5D0\uC11C \uBC94\uC704\uB97C \uB113\uD790 \uC218 \uC788\uC2B5\uB2C8\uB2E4).`,\n );\n }\n return abs;\n }\n\n private async readFile(args: unknown): Promise<LocalToolResult> {\n const a = (args && typeof args === 'object' ? args : {}) as Record<string, unknown>;\n const abs = this.guardPath(a.path);\n const maxBytes = Math.max(1, Math.min(OUTPUT_CAP, Number(a.maxBytes) || OUTPUT_CAP));\n try {\n const buf = await fsReadFile(abs);\n const text = buf.subarray(0, maxBytes).toString('utf8');\n const suffix =\n buf.byteLength > maxBytes ? `\\n\u2026(truncated, ${buf.byteLength} bytes total)` : '';\n return { content: [{ type: 'text', text: (text || '(empty file)') + suffix }] };\n } catch (e) {\n return {\n content: [{ type: 'text', text: `\uC77D\uAE30 \uC2E4\uD328: ${(e as Error).message}` }],\n isError: true,\n };\n }\n }\n\n private async writeFile(args: unknown): Promise<LocalToolResult> {\n const a = (args && typeof args === 'object' ? args : {}) as Record<string, unknown>;\n const abs = this.guardPath(a.path);\n const content = typeof a.content === 'string' ? a.content : String(a.content ?? '');\n const append = a.mode === 'append' || a.append === true;\n try {\n await mkdir(dirname(abs), { recursive: true });\n if (append) await fsAppendFile(abs, content, 'utf8');\n else await fsWriteFile(abs, content, 'utf8');\n return {\n content: [\n {\n type: 'text',\n text: `${append ? '\uC774\uC5B4\uC37C\uC2B5\uB2C8\uB2E4' : '\uC800\uC7A5\uD588\uC2B5\uB2C8\uB2E4'}: ${abs} (${Buffer.byteLength(content)} bytes)`,\n },\n ],\n };\n } catch (e) {\n return {\n content: [{ type: 'text', text: `\uC4F0\uAE30 \uC2E4\uD328: ${(e as Error).message}` }],\n isError: true,\n };\n }\n }\n\n private async listDir(args: unknown): Promise<LocalToolResult> {\n const a = (args && typeof args === 'object' ? args : {}) as Record<string, unknown>;\n const abs = this.guardPath(a.path ?? '~');\n try {\n const names = await readdir(abs);\n const rows: string[] = [];\n for (const name of names.slice(0, 1000)) {\n try {\n const s = await stat(pathJoin(abs, name));\n rows.push(`${s.isDirectory() ? 'd' : '-'} ${String(s.size).padStart(10)} ${name}`);\n } catch {\n rows.push(`? ? ${name}`);\n }\n }\n const more = names.length > 1000 ? `\\n\u2026(${names.length} entries, first 1000 shown)` : '';\n return { content: [{ type: 'text', text: rows.join('\\n') + more || '(empty directory)' }] };\n } catch (e) {\n return {\n content: [{ type: 'text', text: `\uBAA9\uB85D \uC2E4\uD328: ${(e as Error).message}` }],\n isError: true,\n };\n }\n }\n\n private async search(args: unknown): Promise<LocalToolResult> {\n const a = (args && typeof args === 'object' ? args : {}) as Record<string, unknown>;\n const query = String(a.query ?? '');\n if (!query) throw new Error('query must not be empty');\n const abs = this.guardPath(a.path ?? '~');\n const maxResults = Math.max(1, Math.min(500, Number(a.maxResults) || 100));\n const hits: string[] = [];\n const skipDirs = new Set([\n 'node_modules',\n '.git',\n '.venv',\n 'dist',\n 'out',\n '.next',\n '__pycache__',\n ]);\n const walk = async (dir: string, depth: number): Promise<void> => {\n if (hits.length >= maxResults || depth > 8) return;\n let entries: string[];\n try {\n entries = await readdir(dir);\n } catch {\n return;\n }\n for (const name of entries) {\n if (hits.length >= maxResults) return;\n const full = pathJoin(dir, name);\n let s;\n try {\n s = await stat(full);\n } catch {\n continue;\n }\n if (s.isDirectory()) {\n if (!skipDirs.has(name) && !name.startsWith('.')) await walk(full, depth + 1);\n } else if (s.size <= 2_000_000 && !BINARY_EXT.has(extname(name).toLowerCase())) {\n try {\n const text = await fsReadFile(full, 'utf8');\n const lines = text.split(/\\r?\\n/);\n for (let i = 0; i < lines.length; i++) {\n if (lines[i].includes(query)) {\n hits.push(`${full}:${i + 1}: ${lines[i].trim().slice(0, 200)}`);\n if (hits.length >= maxResults) return;\n }\n }\n } catch {\n /* unreadable/binary \u2014 skip */\n }\n }\n }\n };\n await walk(abs, 0);\n return {\n content: [\n {\n type: 'text',\n text: hits.length ? hits.join('\\n') : `'${query}' \uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4 (${abs}).`,\n },\n ],\n };\n }\n\n private async clipboard(args: unknown): Promise<LocalToolResult> {\n const a = (args && typeof args === 'object' ? args : {}) as Record<string, unknown>;\n const action = String(a.action ?? 'read');\n const clip = interaction().clipboard;\n if (!clip) {\n return {\n content: [{ type: 'text', text: '\uC774 \uD638\uC2A4\uD2B8\uC5D0\uC11C\uB294 \uD074\uB9BD\uBCF4\uB4DC\uB97C \uC4F8 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.' }],\n isError: true,\n };\n }\n try {\n if (action === 'write') {\n await clip.write(String(a.text ?? ''));\n return { content: [{ type: 'text', text: '\uD074\uB9BD\uBCF4\uB4DC\uC5D0 \uBCF5\uC0AC\uD588\uC2B5\uB2C8\uB2E4.' }] };\n }\n const text = await clip.read();\n return { content: [{ type: 'text', text: text || '(\uD074\uB9BD\uBCF4\uB4DC\uAC00 \uBE44\uC5B4 \uC788\uC2B5\uB2C8\uB2E4)' }] };\n } catch (e) {\n return {\n content: [{ type: 'text', text: `\uD074\uB9BD\uBCF4\uB4DC \uC811\uADFC \uC2E4\uD328: ${(e as Error).message}` }],\n isError: true,\n };\n }\n }\n\n private async notify(args: unknown, context?: LocalToolCallContext): Promise<LocalToolResult> {\n const a = (args && typeof args === 'object' ? args : {}) as Record<string, unknown>;\n const title = String(a.title ?? 'XGEN');\n const body = String(a.body ?? '');\n try {\n if (this.notificationHandler) {\n const shown = await this.notificationHandler(title, body, context);\n return {\n content: [\n {\n type: 'text',\n text: shown\n ? '\uC54C\uB9BC\uC744 \uD45C\uC2DC\uD588\uC2B5\uB2C8\uB2E4.'\n : '\uC0AC\uC6A9\uC790\uC758 \uC54C\uB9BC \uC124\uC815\uC5D0 \uB530\uB77C \uD45C\uC2DC\uD558\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.',\n },\n ],\n };\n }\n const notify = interaction().notify;\n if (!notify) {\n return {\n content: [{ type: 'text', text: '\uC774 \uD638\uC2A4\uD2B8\uC5D0\uC11C\uB294 \uC54C\uB9BC\uC744 \uD45C\uC2DC\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.' }],\n isError: true,\n };\n }\n const shown = await notify(title, body);\n return {\n content: [\n { type: 'text', text: shown ? '\uC54C\uB9BC\uC744 \uD45C\uC2DC\uD588\uC2B5\uB2C8\uB2E4.' : '\uC54C\uB9BC\uC774 \uD45C\uC2DC\uB418\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4.' },\n ],\n };\n } catch (e) {\n return {\n content: [{ type: 'text', text: `\uC54C\uB9BC \uC2E4\uD328: ${(e as Error).message}` }],\n isError: true,\n };\n }\n }\n\n private async shell(args: unknown): Promise<LocalToolResult> {\n const { command, cwd, shell, timeoutMs, background } = coerceShellArgs(args);\n if (!command.trim()) throw new Error('command must not be empty');\n if (isBlocked(command, this.cfg.blocked)) {\n throw new Error(`\uBA85\uB839 '${firstToken(command)}' \uC740(\uB294) \uCC28\uB2E8 \uBAA9\uB85D\uC5D0 \uC788\uC5B4 \uC2E4\uD589\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.`);\n }\n // \uB418\uB3CC\uB9AC\uAE30 \uC5B4\uB824\uC6B4 \uBA85\uB839\uC740 \uC0AC\uC6A9\uC790 \uC2B9\uC778\uC744 \uBC1B\uB294\uB2E4 (\uC704\uD5D8 \uD328\uD134\uB9CC \u2014 \uC77C\uBC18 \uBA85\uB839\uC740 \uD655\uC778 \uC5C6\uC774).\n if (!(await ensureDangerousApproval(command))) {\n return {\n content: [\n { type: 'text', text: '\uC0AC\uC6A9\uC790\uAC00 \uC774 \uBA85\uB839\uC758 \uC2E4\uD589\uC744 \uAC70\uBD80\uD588\uC2B5\uB2C8\uB2E4 (\uC704\uD5D8\uD560 \uC218 \uC788\uB294 \uBA85\uB839).' },\n ],\n isError: true,\n };\n }\n const pathStr = await augmentedPath();\n const userShellBin = IS_WIN ? null : process.env.SHELL || null;\n const { file, args: argv } = shellInvocation(command, userShellBin, shell);\n const env = buildChildEnv(pathStr);\n const runCwd = cwd || this.cfg.cwd || homedir();\n\n if (background) return this.spawnBackground(command, file, argv, env, runCwd);\n\n const timeout = Math.max(\n MIN_TIMEOUT_MS,\n Math.min(MAX_TIMEOUT_MS, Math.round(timeoutMs || this.cfg.timeoutMs)),\n );\n const r = await this.spawnCapture(file, argv, env, runCwd, timeout);\n if (r.error)\n return {\n content: [{ type: 'text', text: `\uC178 \uC2E4\uD589 \uC2E4\uD328: ${r.error.message}` }],\n isError: true,\n };\n if (r.timedOut) {\n return {\n content: [\n {\n type: 'text',\n text:\n `\uBA85\uB839\uC774 ${Math.round(timeout / 1000)}\uCD08 \uC548\uC5D0 \uB05D\uB098\uC9C0 \uC54A\uC544 \uC911\uB2E8\uD588\uC2B5\uB2C8\uB2E4. ` +\n `\uB300\uD654\uD615 \uBA85\uB839\uC774\uAC70\uB098 \uC885\uB8CC\uB418\uC9C0 \uC54A\uB294 \uD504\uB85C\uADF8\uB7A8(\uC5D0\uB514\uD130\u00B7\uC11C\uBC84 \uB4F1)\uC774\uBA74 background:true \uB85C \uC2E4\uD589\uD558\uC138\uC694.` +\n (r.stdout || r.stderr\n ? `\\n\\n--- \uC911\uB2E8 \uC804 \uCD9C\uB825 ---\\n${(r.stdout + '\\n' + r.stderr).trim().slice(-2000)}`\n : ''),\n },\n ],\n isError: true,\n };\n }\n // Optional output paging (G13): head/tail lines + max_bytes cap. Tail-biased\n // so a caller asking to \"see the end\" of a chatty command gets the tail.\n const a = (args && typeof args === 'object' ? args : {}) as Record<string, unknown>;\n const head = Number(a.head) || 0;\n const tail = Number(a.tail) || 0;\n const maxBytes = Number(a.max_bytes ?? a.maxBytes) || 0;\n if (head || tail || maxBytes) {\n const outP = paginate(r.stdout, { head, tail, maxBytes });\n const errP = paginate(r.stderr, { head, tail, maxBytes });\n return shapeResult(outP.text, errP.text, r.code, r.signal);\n }\n return shapeResult(r.stdout, r.stderr, r.code, r.signal);\n }\n\n private async open(args: unknown): Promise<LocalToolResult> {\n const { target } = coerceOpenArgs(args);\n if (!target.trim()) throw new Error('target must not be empty');\n // G9: validate scheme (block javascript:/data:/vbscript:/unknown) and use\n // Electron's shell API \u2014 no shell string, so no cmd.exe arg-injection.\n const cls = classifyOpenTarget(target);\n if (cls.kind === 'blocked') {\n return { content: [{ type: 'text', text: `\uC5F4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4: ${cls.reason}` }], isError: true };\n }\n const host = interaction();\n if (!host.openExternal && !host.openPath) {\n return {\n content: [{ type: 'text', text: '\uC774 \uD638\uC2A4\uD2B8\uC5D0\uC11C\uB294 \uC678\uBD80 \uC5F4\uAE30\uB97C \uC9C0\uC6D0\uD558\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.' }],\n isError: true,\n };\n }\n try {\n if (cls.kind === 'url') {\n if (!host.openExternal) {\n return {\n content: [{ type: 'text', text: '\uC774 \uD638\uC2A4\uD2B8\uC5D0\uC11C\uB294 \uB9C1\uD06C\uB97C \uC5F4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.' }],\n isError: true,\n };\n }\n await host.openExternal(cls.value);\n return { content: [{ type: 'text', text: `\uC5F4\uC5C8\uC2B5\uB2C8\uB2E4: ${cls.value}` }] };\n }\n // Filesystem path \u2014 scope to allowedRoots (like the file tools), then open\n // with the OS default app. guardPath throws (caught below) if out of scope.\n const abs = this.guardPath(cls.value);\n if (!host.openPath) {\n return {\n content: [{ type: 'text', text: '\uC774 \uD638\uC2A4\uD2B8\uC5D0\uC11C\uB294 \uD30C\uC77C\uC744 \uC5F4 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.' }],\n isError: true,\n };\n }\n const err = await host.openPath(abs); // '' on success, else message\n if (err) return { content: [{ type: 'text', text: `\uC5F4\uAE30 \uC2E4\uD328: ${err}` }], isError: true };\n return { content: [{ type: 'text', text: `\uC5F4\uC5C8\uC2B5\uB2C8\uB2E4: ${abs}` }] };\n } catch (e) {\n return {\n content: [{ type: 'text', text: `\uC5F4\uAE30 \uC2E4\uD328: ${(e as Error).message}` }],\n isError: true,\n };\n }\n }\n\n /** Foreground: capture output, close stdin (no interactive hang), tree-kill on timeout. */\n private spawnCapture(\n file: string,\n argv: string[],\n env: Record<string, string>,\n cwd: string,\n timeoutMs: number,\n ): Promise<SpawnCaptured> {\n // POSIX: run in its own process group so the timeout can reap the whole tree.\n const detachedGroup = !IS_WIN;\n return new Promise<SpawnCaptured>((resolve) => {\n let child: ChildProcess;\n try {\n child = spawn(file, argv, {\n cwd: cwd || homedir(),\n env,\n windowsHide: true,\n detached: detachedGroup,\n // stdin IGNORED \u2192 interactive programs get EOF immediately instead of\n // blocking to the timeout (the \"\uB300\uD654\uD615 \uC258 \uD0C0\uC784\uC544\uC6C3\" report).\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n } catch (e) {\n resolve({ code: null, signal: null, stdout: '', stderr: '', error: e as Error });\n return;\n }\n let out = '';\n let err = '';\n let done = false;\n const finish = (r: SpawnCaptured) => {\n if (done) return;\n done = true;\n clearTimeout(timer);\n resolve(r);\n };\n const timer = setTimeout(() => {\n killTree(child, detachedGroup);\n finish({ code: null, signal: 'SIGKILL', stdout: out, stderr: err, timedOut: true });\n }, timeoutMs);\n child.stdout?.on('data', (d) => {\n out += String(d);\n if (out.length > OUTPUT_CAP * 2) out = out.slice(-OUTPUT_CAP * 2);\n });\n child.stderr?.on('data', (d) => {\n err += String(d);\n if (err.length > OUTPUT_CAP * 2) err = err.slice(-OUTPUT_CAP * 2);\n });\n child.on('error', (e) =>\n finish({ code: null, signal: null, stdout: out, stderr: err, error: e }),\n );\n child.on('close', (code, signal) => finish({ code, signal, stdout: out, stderr: err }));\n });\n }\n\n /** Background: detached, output captured into the job registry, returns a\n * job_id at once. The process keeps running past any tool-call timeout; poll\n * or kill it later with the ShellJob tool. */\n private spawnBackground(\n command: string,\n file: string,\n argv: string[],\n env: Record<string, string>,\n cwd: string,\n ): Promise<LocalToolResult> {\n const detachedGroup = !IS_WIN;\n return new Promise<LocalToolResult>((resolve) => {\n // Bound concurrent background jobs so a runaway loop can't accumulate live\n // processes/pipe FDs without limit (finished jobs are evicted separately).\n const running = [...bgJobs.values()].filter((j) => j.status === 'running').length;\n if (running >= MAX_RUNNING_JOBS) {\n resolve({\n content: [\n {\n type: 'text',\n text: `\uC2E4\uD589 \uC911\uC778 \uBC31\uADF8\uB77C\uC6B4\uB4DC \uC791\uC5C5\uC774 \uB108\uBB34 \uB9CE\uC2B5\uB2C8\uB2E4 (${running}/${MAX_RUNNING_JOBS}). ShellJob(action:'list')\uB85C \uD655\uC778\uD558\uACE0 kill \uB85C \uC815\uB9AC\uD55C \uB4A4 \uB2E4\uC2DC \uC2DC\uB3C4\uD558\uC138\uC694.`,\n },\n ],\n isError: true,\n });\n return;\n }\n let child: ChildProcess;\n try {\n child = spawn(file, argv, {\n cwd: cwd || homedir(),\n env,\n windowsHide: true,\n detached: detachedGroup,\n // Capture output (so it can be polled) but close stdin so REPLs don't hang.\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n } catch (e) {\n resolve({\n content: [\n {\n type: 'text',\n text: `\uBC31\uADF8\uB77C\uC6B4\uB4DC \uC2E4\uD589 \uC2E4\uD328: ${e instanceof Error ? e.message : String(e)}`,\n },\n ],\n isError: true,\n });\n return;\n }\n const job: BgJob = {\n id: newJobId(),\n command,\n pid: child.pid,\n child,\n detachedGroup,\n status: 'running',\n code: null,\n signal: null,\n startedAt: Date.now(),\n stdout: '',\n stderr: '',\n };\n bgJobs.set(job.id, job);\n evictFinishedJobs();\n\n child.stdout?.on('data', (d) => {\n job.stdout = appendCapped(job.stdout, String(d));\n });\n child.stderr?.on('data', (d) => {\n job.stderr = appendCapped(job.stderr, String(d));\n });\n\n let settled = false;\n const done = (r: LocalToolResult) => {\n if (settled) return;\n settled = true;\n resolve(r);\n };\n child.on('error', (e) => {\n if (job.status === 'running') {\n job.status = 'error';\n job.errorMsg = e.message;\n job.endedAt = Date.now();\n }\n done({\n content: [{ type: 'text', text: `\uBC31\uADF8\uB77C\uC6B4\uB4DC \uC2E4\uD589 \uC2E4\uD328: ${e.message}` }],\n isError: true,\n });\n });\n child.on('close', (code, signal) => {\n // Always record the real exit code/signal (even for a job we killed, so\n // list/poll can show it); only transition status if still running.\n job.code = code;\n job.signal = signal;\n if (job.status === 'running') {\n job.status = signal ? 'killed' : 'exited';\n job.endedAt = Date.now();\n } else if (!job.endedAt) {\n job.endedAt = Date.now();\n }\n });\n // Don't let the piped child keep the connector's event loop alive on quit \u2014\n // unref the process AND its stdout/stderr sockets (the pipes hold the loop).\n child.unref();\n (child.stdout as unknown as { unref?: () => void })?.unref?.();\n (child.stderr as unknown as { unref?: () => void })?.unref?.();\n // Brief grace so an ENOENT launch reports failure instead of a false success.\n setTimeout(\n () =>\n done({\n content: [\n {\n type: 'text',\n text:\n `\uBC31\uADF8\uB77C\uC6B4\uB4DC \uC791\uC5C5\uC744 \uC2DC\uC791\uD588\uC2B5\uB2C8\uB2E4.\\njob_id: ${job.id} (pid ${job.pid ?? '?'})\\n` +\n `\uACC4\uC18D \uC2E4\uD589\uB418\uBA70 \uCD9C\uB825\uC774 \uCEA1\uCC98\uB429\uB2C8\uB2E4. \uC0C1\uD0DC\u00B7\uCD9C\uB825\uC740 ShellJob(action:'poll', job_id) \uB85C, ` +\n `\uC885\uB8CC\uB294 ShellJob(action:'kill', job_id) \uB85C \uD655\uC778/\uC81C\uC5B4\uD558\uC138\uC694.`,\n },\n ],\n }),\n BG_SETTLE_MS,\n );\n });\n }\n\n /** ShellJob: manage background jobs \u2014 list / poll (status+output) / kill. */\n private async shellJob(args: unknown): Promise<LocalToolResult> {\n const a = (args && typeof args === 'object' ? args : {}) as Record<string, unknown>;\n const action = String(a.action ?? 'list').toLowerCase();\n const jobId = String(a.job_id ?? a.jobId ?? '').trim();\n\n if (action === 'list') {\n if (!bgJobs.size)\n return {\n content: [\n { type: 'text', text: '\uC2E4\uD589 \uC911\uC774\uAC70\uB098 \uCD5C\uADFC \uC885\uB8CC\uB41C \uBC31\uADF8\uB77C\uC6B4\uB4DC \uC791\uC5C5\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.' },\n ],\n };\n const rows = [...bgJobs.values()]\n .sort((x, y) => y.startedAt - x.startedAt)\n .map((j) => {\n const dur = Math.round(((j.endedAt ?? Date.now()) - j.startedAt) / 1000);\n const exit = j.status === 'running' ? '' : ` exit=${j.signal ? j.signal : j.code}`;\n return `${j.id} [${j.status}${exit}] pid=${j.pid ?? '?'} ${dur}s ${j.command.slice(0, 80)}`;\n });\n return { content: [{ type: 'text', text: rows.join('\\n') }] };\n }\n\n const job = jobId ? bgJobs.get(jobId) : undefined;\n if (!job) {\n return {\n content: [\n {\n type: 'text',\n text: `job_id '${jobId}' \uB97C \uCC3E\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4. ShellJob(action:'list') \uB85C \uD655\uC778\uD558\uC138\uC694.`,\n },\n ],\n isError: true,\n };\n }\n\n if (action === 'kill') {\n if (job.status === 'running') {\n killTree(job.child, job.detachedGroup);\n job.status = 'killed';\n job.endedAt = Date.now();\n }\n return {\n content: [\n { type: 'text', text: `\uC791\uC5C5 ${job.id} \uC744(\uB97C) \uC885\uB8CC\uD588\uC2B5\uB2C8\uB2E4 (\uC0C1\uD0DC: ${job.status}).` },\n ],\n };\n }\n\n // poll / logs \u2014 status + captured output (paginated).\n if (action === 'poll' || action === 'logs') {\n const head = Number(a.head) || 0;\n const tail = Number(a.tail) || (head ? 0 : 200); // default: last 200 lines\n const maxBytes = Number(a.max_bytes ?? a.maxBytes) || 0;\n const outP = paginate(job.stdout, { head, tail, maxBytes });\n const errP = paginate(job.stderr, { head, tail, maxBytes });\n const dur = Math.round(((job.endedAt ?? Date.now()) - job.startedAt) / 1000);\n const header =\n `job ${job.id} \u2014 ${job.status}` +\n (job.status !== 'running' ? ` (exit ${job.signal ? job.signal : job.code})` : '') +\n ` pid=${job.pid ?? '?'} ${dur}s`;\n const parts = [header];\n if (job.errorMsg) parts.push(`ERROR: ${job.errorMsg}`);\n parts.push(\n `--- stdout${outP.truncated ? ` (last, ${outP.totalBytes}B total)` : ''} ---\\n${outP.text || '(none)'}`,\n );\n if (errP.text.trim() || errP.totalBytes) {\n parts.push(\n `--- stderr${errP.truncated ? ` (last, ${errP.totalBytes}B total)` : ''} ---\\n${errP.text || '(none)'}`,\n );\n }\n return {\n content: [{ type: 'text', text: parts.join('\\n\\n') }],\n isError: job.status === 'error',\n };\n }\n\n return {\n content: [\n {\n type: 'text',\n text: `\uC54C \uC218 \uC5C6\uB294 action '${action}'. list | poll | kill \uC911 \uD558\uB098\uB97C \uC4F0\uC138\uC694.`,\n },\n ],\n isError: true,\n };\n }\n}\n\nlet _provider: LocalToolProvider | null = null;\nexport function getLocalToolProvider(): LocalToolProvider {\n if (!_provider) _provider = new LocalToolProvider();\n return _provider;\n}\n", "// \uC2E4\uD589 \uC911\uC778 \uB85C\uCEEC MCP \uCE74\uD0C8\uB85C\uADF8\uC640 \uB3C4\uAD6C \uD638\uCD9C \uC774\uB825\uC744 \uBA54\uBAA8\uB9AC\uC5D0\uB9CC \uBCF4\uAD00\uD55C\uB2E4.\n\nconst MAX_ENTRIES = 200;\n\nexport type McpRuntimeLogKind = 'catalog' | 'call' | 'result';\n\nexport interface McpRuntimeLogEntry {\n id: number;\n timestamp: number;\n kind: McpRuntimeLogKind;\n message: string;\n requestId?: string;\n server?: string;\n tool?: string;\n ok?: boolean;\n durationMs?: number;\n}\n\nconst entries: McpRuntimeLogEntry[] = [];\nconst listeners = new Set<(entry: McpRuntimeLogEntry) => void>();\nlet sequence = 0;\n\n// \uB85C\uCEEC \uB3C4\uAD6C \uC2E4\uD589\u00B7\uD638\uCD9C\u00B7\uACB0\uACFC\uB294 **\uD56D\uC0C1** \uAC10\uC0AC \uAE30\uB85D\uD55C\uB2E4 (\uAE30\uBCF8 \uAEBC\uC838 \uC788\uB358 \uBB38\uC81C \uC218\uC815 \u2014\n// \uB85C\uCEEC \uC178/\uB3C4\uAD6C\uAC00 \uC0AC\uC6A9\uC790 PC \uC5D0\uC11C \uBB34\uC5C7\uC744 \uD588\uB294\uC9C0\uB294 mcpDebug \uC5EC\uBD80\uC640 \uBB34\uAD00\uD558\uAC8C \uB0A8\uACA8\uC57C \uD55C\uB2E4).\n// \uC774 \uD1A0\uAE00\uC740 \uD558\uC704\uD638\uD658\uC6A9 no-op \uC774\uB2E4.\nexport function setMcpRuntimeLogEnabled(_next: boolean): void {\n /* no-op \u2014 \uAE30\uB85D\uC740 \uD56D\uC0C1 \uC720\uC9C0\uB41C\uB2E4. */\n}\n\nexport function appendMcpRuntimeLog(\n entry: Omit<McpRuntimeLogEntry, 'id' | 'timestamp'>,\n): McpRuntimeLogEntry | null {\n const next: McpRuntimeLogEntry = {\n ...entry,\n id: ++sequence,\n timestamp: Date.now(),\n };\n entries.push(next);\n if (entries.length > MAX_ENTRIES) entries.splice(0, entries.length - MAX_ENTRIES);\n for (const listener of listeners) {\n try {\n listener(next);\n } catch {\n /* UI \uAD6C\uB3C5\uC790 \uD558\uB098\uAC00 MCP \uC2E4\uD589\uC744 \uBC29\uD574\uD558\uC9C0 \uC54A\uB294\uB2E4. */\n }\n }\n return next;\n}\n\nexport function mcpRuntimeLogs(): McpRuntimeLogEntry[] {\n return [...entries];\n}\n\nexport function clearMcpRuntimeLogs(): void {\n entries.length = 0;\n}\n\nexport function onMcpRuntimeLog(listener: (entry: McpRuntimeLogEntry) => void): () => void {\n listeners.add(listener);\n return () => listeners.delete(listener);\n}\n", "// \uC11C\uBC84\uBCC4 \uC778\uC99D\uC11C \uC608\uC678\uC640 SSO \uC644\uB8CC \uC751\uB2F5\uC744 \uC548\uC804\uD558\uAC8C \uAC80\uC99D\uD558\uB294 \uC5F0\uACB0 \uBCF4\uC548 \uB3C4\uC6B0\uBBF8\nimport type { LoginResult } from '@dex/protocol/types';\n\nconst PRIVATE_CA_ERROR = 'CERT_AUTHORITY_INVALID';\n\nfunction certificateErrorName(result: string): string {\n return result.replace(/^net::/, '').replace(/^ERR_/, '');\n}\n\n/** \uC0AC\uC124 CA \uC2E0\uB8B0 \uC2E4\uD328\uC774\uBA70 \uC0AC\uC6A9\uC790\uAC00 \uC608\uC678\uB97C \uBA85\uC2DC\uD588\uB294\uC9C0 \uD655\uC778\uD55C\uB2E4. */\nexport function shouldIgnorePrivateCertificateError(\n enabled: boolean,\n verificationResult: string,\n): boolean {\n return enabled && certificateErrorName(verificationResult) === PRIVATE_CA_ERROR;\n}\n\n/** \uC124\uC815\uB41C \uC11C\uBC84 hostname\uC758 \uC0AC\uC124 CA \uC2E0\uB8B0 \uC2E4\uD328\uB9CC \uD5C8\uC6A9\uD55C\uB2E4. */\nexport function shouldAllowPrivateCertificate(\n serverUrl: string,\n enabled: boolean,\n hostname: string,\n verificationResult: string,\n): boolean {\n if (!shouldIgnorePrivateCertificateError(enabled, verificationResult)) return false;\n try {\n return new URL(serverUrl).hostname.toLowerCase() === hostname.toLowerCase();\n } catch {\n return false;\n }\n}\n\n/** \uC124\uC815\uB41C XGEN Node WebSocket\uC5D0 \uC801\uC6A9\uD560 TLS \uAC80\uC99D \uC635\uC158\uC744 \uB9CC\uB4E0\uB2E4. */\nexport function xgenWebSocketTlsOptions(enabled: boolean): { rejectUnauthorized: boolean } {\n return { rejectUnauthorized: !enabled };\n}\n\n/** \uC11C\uBC84 origin\uC5D0 \uC0C1\uB300 SSO PATH\uC640 \uACE0\uC815 \uC644\uB8CC \uCF5C\uBC31\uC744 \uACB0\uD569\uD55C\uB2E4. */\nexport function buildSsoUrl(serverUrl: string, path: string, callbackName: string): string {\n const base = new URL(serverUrl);\n const trimmed = path.trim();\n if (!trimmed.startsWith('/') || trimmed.startsWith('//')) {\n throw new Error('SSO PATH\uB294 /\uB85C \uC2DC\uC791\uD558\uB294 \uC0C1\uB300 \uACBD\uB85C\uC5EC\uC57C \uD569\uB2C8\uB2E4.');\n }\n const url = new URL(trimmed, base.origin);\n if (url.origin !== base.origin) throw new Error('SSO PATH\uB294 \uC124\uC815\uB41C \uC11C\uBC84\uC640 \uAC19\uC740 origin\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.');\n url.searchParams.set('next', `parent.${callbackName}`);\n return url.toString();\n}\n\n/** \uC6D0\uACA9 SSO \uD398\uC774\uC9C0\uAC00 \uC804\uB2EC\uD55C gateway \uB85C\uADF8\uC778 \uC751\uB2F5\uC744 \uCD5C\uC18C \uD544\uB4DC\uB9CC \uCC44\uD0DD\uD55C\uB2E4. */\nexport function parseSsoLoginResponse(payload: unknown): LoginResult {\n if (!payload || typeof payload !== 'object') throw new Error('SSO \uC778\uC99D \uC751\uB2F5\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.');\n const value = payload as Record<string, unknown>;\n if (value.success !== true || typeof value.access_token !== 'string' || !value.access_token) {\n throw new Error(typeof value.message === 'string' ? value.message : 'SSO \uC778\uC99D\uC5D0 \uC2E4\uD328\uD588\uC2B5\uB2C8\uB2E4.');\n }\n const optionalString = (key: string): string | undefined =>\n typeof value[key] === 'string' && value[key] ? (value[key] as string) : undefined;\n return {\n accessToken: value.access_token,\n refreshToken: optionalString('refresh_token'),\n tokenType: optionalString('token_type') ?? 'bearer',\n userId: optionalString('user_id') ?? '',\n username: optionalString('username') ?? '',\n };\n}\n", "import { chmodSync, existsSync, mkdirSync, readFileSync, renameSync, unlinkSync, writeFileSync } from 'node:fs';\nimport { dirname, join } from 'node:path';\nimport type { StoredSession } from './contract';\nimport { dataDirectory } from './config-store';\n\nconst SERVICE = 'xgen-dex-cli';\nconst ACCOUNT_PREFIX = 'profile:';\n\ninterface KeytarLike {\n getPassword(service: string, account: string): Promise<string | null>;\n setPassword(service: string, account: string, password: string): Promise<void>;\n deletePassword(service: string, account: string): Promise<boolean>;\n}\n\nexport interface CredentialStore {\n get(profile: string): Promise<StoredSession | null>;\n set(profile: string, session: StoredSession): Promise<void>;\n delete(profile: string): Promise<void>;\n}\n\n/** \uC5B4\uB514\uC5D0 \uC800\uC7A5\uD558\uACE0 \uC788\uB294\uC9C0. `dex status` \uAC00 \uC774\uAC78 \uB9D0\uD574 \uC900\uB2E4. */\nexport type CredentialBackend = 'keychain' | 'file';\n\nlet activeBackend: CredentialBackend = 'file';\n\nexport function credentialBackend(): CredentialBackend {\n return activeBackend;\n}\n\n/**\n * OS \uD0A4\uCCB4\uC778\uC744 **\uC4F8 \uC218 \uC788\uC73C\uBA74** \uB3CC\uB824\uC8FC\uACE0, \uC544\uB2C8\uBA74 ``null``.\n *\n * \uC608\uC804\uC5D0\uB294 \uC5EC\uAE30\uC11C \uB358\uC84C\uB2E4. \uADF8\uB798\uC11C keytar \uAC00 \uC548 \uBD99\uB294 \uD658\uACBD \u2014 \uC0C8 npm \uC774 \uC124\uCE58\n * \uC2A4\uD06C\uB9BD\uD2B8\uB97C \uB9C9\uC544 \uB124\uC774\uD2F0\uBE0C \uBC14\uC778\uB529\uC774 \uC548 \uAE54\uB9B0 \uACBD\uC6B0, \uD0A4\uB9C1\uC774 \uC5C6\uB294 \uD5E4\uB4DC\uB9AC\uC2A4 \uC11C\uBC84,\n * D-Bus \uAC00 \uC5C6\uB294 \uCEE8\uD14C\uC774\uB108 \u2014 \uC5D0\uC11C\uB294 **\uB85C\uADF8\uC778 \uC790\uCCB4\uAC00 \uBD88\uAC00\uB2A5**\uD588\uB2E4. \uADF8 \uC14B \uB2E4 \uD754\uD558\uB2E4.\n *\n * \uB370\uC2A4\uD06C\uD1B1\uC740 \uC774\uBBF8 \uD0A4\uCCB4\uC778 \u2192 \uC554\uD638\uD654 \uD30C\uC77C \u2192 \uD3C9\uBB38 \uD30C\uC77C \u2192 \uBA54\uBAA8\uB9AC \uC0AC\uB2E4\uB9AC\uB97C \uAC16\uACE0 \uC788\uC5C8\uB2E4.\n * \uD130\uBBF8\uB110\uC5D0\uB294 \uADF8\uAC8C \uC5C6\uC5C8\uC744 \uBFD0\uC774\uB2E4.\n */\nlet keytarProbe: Promise<KeytarLike | null> | null = null;\n\n/**\n * \uD0A4\uCCB4\uC778 \uD638\uCD9C\uC758 \uC0C1\uD55C.\n *\n * **\uD544\uC694\uD55C \uC774\uC720**: \uD0A4\uB9C1 \uB370\uBAAC\uC774 \uC5C6\uB294 \uB9AC\uB205\uC2A4(\uD5E4\uB4DC\uB9AC\uC2A4 \uC11C\uBC84\u00B7\uCEE8\uD14C\uC774\uB108\u00B7SSH \uC138\uC158)\uC5D0\uC11C\n * libsecret \uC740 \uC624\uB958\uB97C \uB0B4\uC9C0 \uC54A\uACE0 **\uADF8\uB0E5 \uC751\uB2F5\uD558\uC9C0 \uC54A\uB294\uB2E4.** \uC2E4\uCE21\uD588\uB2E4 \u2014 \uC774 \uBA38\uC2E0\uC5D0\uC11C\n * `getPassword` \uB294 28ms \uB9CC\uC5D0 null \uC744 \uC8FC\uB294\uB370 `setPassword` \uB294 \uC601\uC6D0\uD788 \uB3CC\uC544\uC624\uC9C0\n * \uC54A\uC558\uB2E4. \uD0C0\uC784\uC544\uC6C3\uC774 \uC5C6\uC73C\uBA74 `dex login` \uC774 \uC544\uBB34 \uBA54\uC2DC\uC9C0 \uC5C6\uC774 \uBA48\uCD98 \uAC83\uCC98\uB7FC \uBCF4\uC778\uB2E4.\n *\n * \uC751\uB2F5\uD558\uB294 \uD0A4\uB9C1\uC774\uB77C\uBA74 \uC218\uC2ED ms \uC548\uC5D0 \uB05D\uB09C\uB2E4 \u2014 2\uCD08\uB294 \uB290\uB9B0 \uAE30\uACC4\uB97C \uC704\uD55C \uC5EC\uC720\uB2E4.\n */\nconst KEYCHAIN_TIMEOUT_MS = 2_000;\n\n/** \uC815\uD574\uC9C4 \uC2DC\uAC04 \uC548\uC5D0 \uB05D\uB098\uC9C0 \uC54A\uC73C\uBA74 \uC2E4\uD328\uB85C \uBCF8\uB2E4. \uB9E4\uB2E4\uB294 \uAC83\uBCF4\uB2E4 \uD30C\uC77C\uC5D0 \uC800\uC7A5\uD558\uB294 \uD3B8\uC774 \uB0AB\uB2E4. */\nasync function withTimeout<T>(work: Promise<T>): Promise<T> {\n let timer: NodeJS.Timeout | undefined;\n try {\n return await Promise.race([\n work,\n new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error('keychain timeout')), KEYCHAIN_TIMEOUT_MS);\n // \uC774 \uD0C0\uC774\uBA38\uAC00 \uD504\uB85C\uC138\uC2A4\uB97C \uBD99\uC7A1\uC73C\uBA74 CLI \uAC00 \uB05D\uB098\uB3C4 \uC548 \uC8FD\uB294\uB2E4.\n timer.unref?.();\n }),\n ]);\n } finally {\n if (timer) clearTimeout(timer);\n }\n}\n\nasync function loadKeytar(): Promise<KeytarLike | null> {\n // \uD55C \uBC88\uB9CC \uC2DC\uB3C4\uD55C\uB2E4 \u2014 \uC5C6\uB294 \uBAA8\uB4C8\uC744 \uB9E4 \uD638\uCD9C\uB9C8\uB2E4 import \uD558\uBA74 \uADF8\uB9CC\uD07C \uB290\uB824\uC9C4\uB2E4.\n keytarProbe ??= (async () => {\n // \uC0AC\uC6A9\uC790\uAC00 \uBA85\uC2DC\uC801\uC73C\uB85C \uB04C \uC218 \uC788\uB2E4. \uD0A4\uB9C1\uC774 \uB9E4\uBC88 \uC554\uD638\uB97C \uBB3B\uB294 \uD658\uACBD\uC5D0\uC11C \uD544\uC694\uD558\uB2E4.\n if (process.env.DEX_NO_KEYCHAIN === '1') return null;\n try {\n const loaded = (await import('keytar')) as unknown as {\n default?: KeytarLike;\n getPassword?: KeytarLike['getPassword'];\n setPassword?: KeytarLike['setPassword'];\n deletePassword?: KeytarLike['deletePassword'];\n };\n const keytar = (loaded.default ?? loaded) as KeytarLike;\n if (!keytar.getPassword || !keytar.setPassword || !keytar.deletePassword) return null;\n // \uC2E4\uC81C\uB85C \uD55C \uBC88 \uC77D\uC5B4 \uBCF8\uB2E4 \u2014 \uBAA8\uB4C8\uC774 \uB85C\uB4DC\uB3FC\uB3C4 \uD0A4\uB9C1\uC774 \uC5C6\uC73C\uBA74 \uC5EC\uAE30\uC11C \uAC78\uB9B0\uB2E4.\n await withTimeout(keytar.getPassword(SERVICE, '__probe__'));\n return keytar;\n } catch {\n return null;\n }\n })();\n const keytar = await keytarProbe;\n activeBackend = keytar ? 'keychain' : 'file';\n return keytar;\n}\n\n/**\n * \uD0A4\uCCB4\uC778\uC774 \uC5C6\uC744 \uB54C\uC758 \uC790\uB9AC \u2014 \uC0AC\uC6A9\uC790 \uB370\uC774\uD130 \uD3F4\uB354\uC758 0600 \uD30C\uC77C.\n *\n * \"\uC554\uD638\uD654\" \uB77C\uACE0 \uBD80\uB974\uC9C0 \uC54A\uB294\uB2E4. OS \uAC00 \uD0A4\uB97C \uC950\uC5B4 \uC8FC\uC9C0 \uC54A\uB294 \uACF3\uC5D0\uC11C \uD0A4\uB97C \uD30C\uC77C \uC606\uC5D0\n * \uB450\uACE0 \uC554\uD638\uD654\uD588\uB2E4\uACE0 \uB9D0\uD558\uB294 \uAC83\uC740 \uAC70\uC9D3\uB9D0\uC774\uACE0, \uADF8 \uAC70\uC9D3\uB9D0\uC744 \uBBFF\uACE0 \uC774 \uD30C\uC77C\uC744 \uBC31\uC5C5\uC5D0\n * \uB123\uAC8C \uB41C\uB2E4. \uC2E4\uC81C\uB85C \uD558\uB294 \uC77C\uC740 **\uC18C\uC720\uC790\uB9CC \uC77D\uC744 \uC218 \uC788\uAC8C \uD558\uB294 \uAC83**\uC774\uACE0, \uADF8\uAC74 \uADF8\uB807\uAC8C\n * \uB9D0\uD574\uC57C \uD55C\uB2E4.\n */\nfunction filePath(): string {\n return join(dataDirectory(), 'credentials.json');\n}\n\nfunction readFileStore(): Record<string, string> {\n try {\n const raw = readFileSync(filePath(), 'utf8');\n const parsed: unknown = JSON.parse(raw);\n return parsed && typeof parsed === 'object' ? (parsed as Record<string, string>) : {};\n } catch {\n return {};\n }\n}\n\nfunction writeFileStore(values: Record<string, string>): void {\n const path = filePath();\n mkdirSync(dirname(path), { recursive: true });\n const tmp = `${path}.tmp`;\n writeFileSync(tmp, JSON.stringify(values, null, 2), { encoding: 'utf8', mode: 0o600 });\n try {\n chmodSync(tmp, 0o600);\n } catch {\n /* \uD30C\uC77C\uC2DC\uC2A4\uD15C\uC774 \uAD8C\uD55C\uC744 \uC548 \uBC1B\uB294 \uACF3(\uC77C\uBD80 \uB9C8\uC6B4\uD2B8) \u2014 \uB0B4\uC6A9\uC740 \uADF8\uB300\uB85C \uC4F4\uB2E4 */\n }\n renameSync(tmp, path);\n}\n\nasync function secretGet(account: string): Promise<string | null> {\n const keytar = await loadKeytar();\n if (keytar) {\n try {\n return await withTimeout(keytar.getPassword(SERVICE, account));\n } catch {\n // \uC800\uC7A5\uC740 \uD0A4\uCCB4\uC778\uC5D0 \uD588\uB294\uB370 \uC9C0\uAE08 \uC77D\uAE30\uAC00 \uB9C9\uD78C \uACBD\uC6B0(\uD0A4\uB9C1 \uC7A0\uAE40) \u2014 \uD30C\uC77C\uB85C \uB0B4\uB824\uAC04\uB2E4.\n }\n }\n return readFileStore()[account] ?? null;\n}\n\nasync function secretSet(account: string, value: string | null): Promise<void> {\n const keytar = await loadKeytar();\n if (keytar) {\n try {\n if (value === null) await withTimeout(keytar.deletePassword(SERVICE, account));\n else await withTimeout(keytar.setPassword(SERVICE, account, value));\n return;\n } catch {\n // \uC4F0\uAE30\uAC00 \uB9C9\uD788\uAC70\uB098 \uC751\uB2F5\uD558\uC9C0 \uC54A\uC73C\uBA74 \uC544\uB798 \uD30C\uC77C\uB85C \uB0B4\uB824\uAC04\uB2E4. \uC77D\uAE30\uB294 \uB418\uB294\uB370\n // \uC4F0\uAE30\uB9CC \uB9E4\uB2E4\uB294 \uD0A4\uB9C1\uC774 \uC2E4\uC81C\uB85C \uC788\uB2E4(\uC774 \uC800\uC7A5\uC18C\uB97C \uB9CC\uB4E0 \uBA38\uC2E0\uC774 \uADF8\uB7AC\uB2E4).\n activeBackend = 'file';\n }\n }\n const values = readFileStore();\n if (value === null) delete values[account];\n else values[account] = value;\n if (Object.keys(values).length === 0 && existsSync(filePath())) {\n try {\n unlinkSync(filePath());\n } catch {\n writeFileStore(values);\n }\n return;\n }\n writeFileStore(values);\n}\n\nfunction parseSession(raw: string | null): StoredSession | null {\n if (!raw) return null;\n try {\n const value = JSON.parse(raw) as StoredSession;\n if (!value.serverUrl || !value.accessToken) return null;\n return value;\n } catch {\n return null;\n }\n}\n\n/**\n * \uAE30\uBCF8 \uC790\uACA9\uC99D\uBA85 \uC800\uC7A5\uC18C \u2014 OS \uD0A4\uCCB4\uC778\uC774 \uC788\uC73C\uBA74 \uAC70\uAE30, \uC5C6\uC73C\uBA74 \uC18C\uC720\uC790 \uC804\uC6A9 \uD30C\uC77C.\n *\n * \uC774\uB984\uC5D0 keytar \uB97C \uB123\uC9C0 \uC54A\uB294\uB2E4. \uBB34\uC5C7\uC73C\uB85C \uC800\uC7A5\uD558\uB294\uC9C0\uB294 \uD658\uACBD\uC774 \uC815\uD558\uACE0, \uBD80\uB974\uB294 \uCABD\uC740\n * \uADF8\uAC78 \uC54C \uD544\uC694\uAC00 \uC5C6\uB2E4 \u2014 \uC54C\uC544\uC57C \uD560 \uB54C\uB294 `credentialBackend()` \uB85C \uBB3B\uB294\uB2E4.\n */\nexport class SystemCredentialStore implements CredentialStore {\n /**\n * \uC5D4\uC9C4\uC758 `SecretPort` \uB85C \uB0B4\uBCF4\uB0B4\uB294 \uB450 \uD568\uC218 \u2014 \uD504\uB85C\uD30C\uC77C \uC138\uC158 \uB9D0\uACE0 **\uC784\uC758\uC758 \uBE44\uBC00**\n * (MCP \uC11C\uBC84 \uC2DC\uD06C\uB9BF \u00B7 OAuth \uC0C1\uD0DC)\uC744 \uAC19\uC740 \uBC31\uC5D4\uB4DC\uC5D0 \uB454\uB2E4.\n *\n * \uC800\uC7A5\uC18C\uB97C \uB098\uB204\uC9C0 \uC54A\uB294 \uC774\uC720: \uC0AC\uC6A9\uC790\uC5D0\uAC8C\uB294 \"\uC774 \uC571\uC774 \uB0B4 \uD0A4\uCCB4\uC778\uC5D0 \uBB34\uC5C7\uC744 \uB123\uC5C8\uB098\"\uAC00\n * \uD558\uB098\uC758 \uC9C8\uBB38\uC774\uACE0, \uB450 \uACF3\uC5D0 \uB098\uB258\uBA74 \uB85C\uADF8\uC544\uC6C3\uD560 \uB54C \uD55C\uCABD\uC774 \uB0A8\uB294\uB2E4.\n */\n async getRaw(name: string): Promise<string | null> {\n return secretGet(name);\n }\n\n async setRaw(name: string, value: string | null): Promise<boolean> {\n await secretSet(name, value);\n return true;\n }\n\n async get(profile: string): Promise<StoredSession | null> {\n return parseSession(await secretGet(ACCOUNT_PREFIX + profile));\n }\n\n async set(profile: string, session: StoredSession): Promise<void> {\n await secretSet(ACCOUNT_PREFIX + profile, JSON.stringify(session));\n }\n\n async delete(profile: string): Promise<void> {\n await secretSet(ACCOUNT_PREFIX + profile, null);\n }\n}\n\n/** \uC61B \uC774\uB984 \u2014 \uD55C\uB3D9\uC548 \uB0A8\uAE34\uB2E4(\uC678\uBD80\uC5D0\uC11C import \uD558\uACE0 \uC788\uC744 \uC218 \uC788\uB2E4). */\nexport const KeytarCredentialStore = SystemCredentialStore;\n\nexport class MemoryCredentialStore implements CredentialStore {\n private values = new Map<string, StoredSession>();\n\n async get(profile: string): Promise<StoredSession | null> {\n const value = this.values.get(profile);\n return value ? structuredClone(value) : null;\n }\n\n async set(profile: string, session: StoredSession): Promise<void> {\n this.values.set(profile, structuredClone(session));\n }\n\n async delete(profile: string): Promise<void> {\n this.values.delete(profile);\n }\n}\n", "// \uBC30\uD3EC \uD504\uB85C\uD544\uC758 \uC11C\uBC84\u00B7\uC778\uC99D\u00B7SSO\u00B7\uC5C5\uB370\uC774\uD2B8 \uAE30\uBCF8\uAC12\uC744 \uAC80\uC99D\uD558\uACE0 \uAD6C\uC131\uD55C\uB2E4\nexport interface DeploymentDefaultInput {\n serverUrl?: string;\n allowPrivateCertificate?: string;\n ssoEnabled?: string;\n ssoPath?: string;\n updateServer?: string;\n}\n\nexport interface DeploymentDefaults {\n serverUrl?: string;\n allowPrivateCertificate?: boolean;\n ssoEnabled?: boolean;\n ssoPath?: string;\n updateServer?: 'github' | 'xgen';\n}\n\nfunction optionalBoolean(value: string | undefined, name: string): boolean | undefined {\n if (value === undefined || value === '') return undefined;\n if (value === 'true' || value === '1') return true;\n if (value === 'false' || value === '0') return false;\n throw new Error(`${name}\uC740 true, false, 1, 0 \uC911 \uD558\uB098\uC5EC\uC57C \uD569\uB2C8\uB2E4.`);\n}\n\nexport function resolveDeploymentDefaults(input: DeploymentDefaultInput): DeploymentDefaults {\n const defaults: DeploymentDefaults = {};\n\n if (input.serverUrl) {\n const serverUrl = input.serverUrl.trim().replace(/\\/+$/, '');\n const parsed = new URL(serverUrl);\n if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') {\n throw new Error('\uAE30\uBCF8 \uC11C\uBC84 URL\uC740 http \uB610\uB294 https URL\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.');\n }\n defaults.serverUrl = serverUrl;\n }\n\n const allowPrivateCertificate = optionalBoolean(\n input.allowPrivateCertificate,\n 'XGEN_DEFAULT_ALLOW_PRIVATE_CERTIFICATE',\n );\n if (allowPrivateCertificate !== undefined)\n defaults.allowPrivateCertificate = allowPrivateCertificate;\n\n const ssoEnabled = optionalBoolean(input.ssoEnabled, 'XGEN_DEFAULT_SSO_ENABLED');\n if (ssoEnabled !== undefined) defaults.ssoEnabled = ssoEnabled;\n\n if (input.ssoPath) {\n const ssoPath = input.ssoPath.trim();\n if (!ssoPath.startsWith('/') || ssoPath.startsWith('//')) {\n throw new Error('\uAE30\uBCF8 SSO PATH\uB294 /\uB85C \uC2DC\uC791\uD558\uB294 \uC0C1\uB300 \uACBD\uB85C\uC5EC\uC57C \uD569\uB2C8\uB2E4.');\n }\n defaults.ssoPath = ssoPath;\n }\n\n if (input.updateServer) {\n if (input.updateServer !== 'github' && input.updateServer !== 'xgen') {\n throw new Error('\uAE30\uBCF8 \uC5C5\uB370\uC774\uD2B8 \uC11C\uBC84\uB294 github \uB610\uB294 xgen\uC774\uC5B4\uC57C \uD569\uB2C8\uB2E4.');\n }\n defaults.updateServer = input.updateServer;\n }\n\n return defaults;\n}\n\nexport const DEPLOYMENT_DEFAULTS = resolveDeploymentDefaults({\n serverUrl: process.env.XGEN_DEFAULT_SERVER_URL,\n allowPrivateCertificate: process.env.XGEN_DEFAULT_ALLOW_PRIVATE_CERTIFICATE,\n ssoEnabled: process.env.XGEN_DEFAULT_SSO_ENABLED,\n ssoPath: process.env.XGEN_DEFAULT_SSO_PATH,\n updateServer: process.env.XGEN_DEFAULT_UPDATE_SERVER,\n});\n"],
5
+ "mappings": "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",
6
+ "names": ["sep", "homedir", "homedir", "join", "homedir", "homedir", "platform", "mkdir", "isAbsolute", "dirname", "IS_WIN", "platform", "IS_MAC", "IS_WIN", "IS_MAC", "IS_WIN", "homedir", "isAbsolute", "mkdir", "dirname", "entries", "shown", "ws", "dirname", "join", "withTimeout", "keytar", "join", "dirname"]
7
+ }