xgen-dex-cli 1.2.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.
- package/README.md +175 -0
- package/dist/chunks/chunk-QXSCKZEG.js +4560 -0
- package/dist/chunks/chunk-QXSCKZEG.js.map +7 -0
- package/dist/chunks/tui-DMHUMWPG.js +1048 -0
- package/dist/chunks/tui-DMHUMWPG.js.map +7 -0
- package/dist/cli.js +1094 -0
- package/dist/cli.js.map +7 -0
- package/docs/CONNECTOR_FEATURE_MAP.md +300 -0
- package/docs/PROTOCOL.md +187 -0
- package/docs/TUI.md +46 -0
- package/docs/VSCODE.md +41 -0
- package/package.json +65 -0
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../src/cli.ts", "../src/args.ts", "../src/io.ts", "../src/mode.ts", "../../../packages/rpc/src/server.ts", "../../../packages/rpc/src/wire.ts", "../src/dex-host.ts"],
|
|
4
|
+
"sourcesContent": ["#!/usr/bin/env node\nimport { stdin, stdout, stderr } from 'node:process';\nimport { parseArgs, flag, option, positiveIntegerOption, requiredOption } from './args';\nimport { FileConfigStore } from '@dex/engine';\nimport { KeytarCredentialStore } from '@dex/engine';\nimport { DexEngine } from '@dex/engine';\nimport { DexError, publicError } from '@dex/engine';\nimport { promptLine, promptSecret, readStdin } from './io';\nimport { isInteractiveTerminal, shouldLaunchTui } from './mode';\nimport { DexRpcServer } from '@dex/rpc/server';\nimport type { LocalToolsStatus } from '@dex/engine';\nimport type { Agent, AgentListQuery, ChatEvent, Conversation, HistoryTurn } from '@dex/engine';\nimport { bindCliHost } from './dex-host';\n\nconst VERSION = '0.1.0';\n\nconst HELP = `XGEN Dex CLI ${VERSION}\n\nUsage:\n dex \uB300\uD654\uD615 \uD130\uBBF8\uB110 UI\n dex ui \uB300\uD654\uD615 \uD130\uBBF8\uB110 UI\n dex profile set [name] --server <url>\n dex profile use <name>\n dex profile list [--json]\n dex login --email <email> [--profile <name>] [--password-stdin]\n dex status [--profile <name>] [--json]\n dex logout [--profile <name>]\n dex agents list [--search <text>] [--owner personal|shared] [--json]\n dex chat --agent <workflow-id> [--name <workflow-name>] [--interaction <id>] [--jsonl]\n dex history list [--json]\n dex history turns --workflow <id> --interaction <id> [--json]\n dex tools list [--json]\n dex tools status [--profile <name>] [--json]\n dex tools enable [--cwd <path>] [--allow <path,...>] [--block <command,...>] [--allow-dangerous]\n dex tools configure [--cwd <path>] [--allow <path,...>] [--block <command,...>] [--timeout <ms>]\n [--allow-dangerous|--no-allow-dangerous]\n dex tools disable\n dex tools run <Shell|ShellJob|ReadFile|WriteFile|ListDir|Search|Open|Clipboard|Notify> [--args <json>] [--json]\n dex ssh list [--json]\n dex ssh enable | dex ssh disable\n dex ssh test <name> [--json]\n dex tools serve [--profile <name>] \uB85C\uCEEC \uB3C4\uAD6C \uBE0C\uB9AC\uC9C0\uB9CC \uACC4\uC18D \uC2E4\uD589\n dex tool ... dex tools ...\uC758 \uB2E8\uC218\uD615 \uBCC4\uCE6D\n dex serve --stdio\n\nGlobal options:\n --profile <name> \uC0AC\uC6A9\uD560 \uC11C\uBC84 \uD504\uB85C\uD544\n --json \uB2E8\uC77C JSON \uACB0\uACFC\n --jsonl \uCC44\uD305 \uC774\uBCA4\uD2B8\uB97C NDJSON\uC73C\uB85C \uCD9C\uB825\n -h, --help \uB3C4\uC6C0\uB9D0\n -v, --version \uBC84\uC804\n\nExamples:\n dex profile set corp --server https://xgen.example.com\n dex login --email me@corp.com\n dex agents list\n dex tools enable --cwd . --allow . --block sudo\n echo '\uC774 \uC800\uC7A5\uC18C\uB97C \uC124\uBA85\uD574\uC918' | dex chat --agent wf_abc\n`;\n\nfunction writeJson(value: unknown): void {\n stdout.write(`${JSON.stringify(value, null, 2)}\\n`);\n}\n\nfunction cell(value: unknown, width: number): string {\n const text = String(value ?? '');\n return text.length > width ? `${text.slice(0, Math.max(0, width - 1))}\u2026` : text.padEnd(width);\n}\n\nfunction printAgents(agents: Agent[]): void {\n if (agents.length === 0) {\n stdout.write('Agent\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.\\n');\n return;\n }\n stdout.write(`${cell('WORKFLOW ID', 28)} ${cell('NAME', 30)} OWNER\\n`);\n for (const agent of agents) {\n stdout.write(\n `${cell(agent.workflowId, 28)} ${cell(agent.workflowName, 30)} ${agent.isShared ? 'shared' : 'personal'}\\n`,\n );\n }\n}\n\nfunction printConversations(items: Conversation[]): void {\n if (items.length === 0) {\n stdout.write('\uB300\uD654 \uAE30\uB85D\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.\\n');\n return;\n }\n stdout.write(`${cell('INTERACTION ID', 38)} ${cell('AGENT', 28)} UPDATED\\n`);\n for (const item of items) {\n stdout.write(`${cell(item.interactionId, 38)} ${cell(item.workflowName, 28)} ${item.updatedAt}\\n`);\n }\n}\n\nfunction printTurns(items: HistoryTurn[]): void {\n for (const item of items) {\n stdout.write(`\\n[You]\\n${item.input}\\n\\n[${item.workflowName}]\\n${item.output}\\n`);\n }\n if (items.length === 0) stdout.write('\uB300\uD654 turn\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.\\n');\n}\n\nfunction describeEvent(event: ChatEvent): string | null {\n if (event.kind === 'status') return `${event.surface}: ${event.detail ?? event.reason ?? '\uC0C1\uD0DC \uBCC0\uACBD'}`;\n if (event.kind === 'tool') return `tool: ${event.event.toolName ?? event.event.eventType}`;\n if (event.kind === 'node_status') return `node: ${event.event.nodeId} ${event.event.status}`;\n if (event.kind === 'quota') return `quota: ${event.level}`;\n if (event.kind === 'error') return `error: ${event.detail}`;\n return null;\n}\n\nfunction printLocalToolsStatus(status: LocalToolsStatus): void {\n stdout.write(`\uB85C\uCEEC \uB3C4\uAD6C: ${status.config.enabled ? '\uCF1C\uC9D0' : '\uAEBC\uC9D0'}\\n`);\n stdout.write(`\uC791\uC5C5 \uD3F4\uB354: ${status.config.cwd || '(\uBBF8\uC124\uC815)'}\\n`);\n stdout.write(`\uD5C8\uC6A9 \uACBD\uB85C: ${status.config.allowedRoots.join(', ') || '(\uC791\uC5C5 \uD3F4\uB354)'}\\n`);\n stdout.write(`\uC704\uD5D8 \uBA85\uB839: ${status.config.allowDangerous ? '\uD5C8\uC6A9' : '\uCC28\uB2E8'}\\n`);\n stdout.write(\n `\uBE0C\uB9AC\uC9C0: ${\n status.bridge.catalogSynced\n ? `\uC5F0\uACB0\uB428 (\uB3C4\uAD6C ${status.bridge.serverToolCount}\uAC1C)`\n : status.bridge.connected\n ? '\uCE74\uD0C8\uB85C\uADF8 \uB3D9\uAE30\uD654 \uC911'\n : status.bridge.enabled\n ? '\uC5F0\uACB0 \uB300\uAE30 \uC911'\n : '\uC911\uC9C0\uB428'\n }\\n`,\n );\n if (status.bridge.error) stdout.write(`\uC624\uB958: ${status.bridge.error}\\n`);\n}\n\nfunction csvOption(args: ReturnType<typeof parseArgs>, name: string): string[] | undefined {\n const value = option(args, name);\n if (value === undefined) return undefined;\n return [...new Set(value.split(',').map((item) => item.trim()).filter(Boolean))];\n}\n\nfunction jsonObjectOption(args: ReturnType<typeof parseArgs>, name: string): Record<string, unknown> {\n const value = option(args, name);\n if (!value) return {};\n let parsed: unknown;\n try {\n parsed = JSON.parse(value);\n } catch {\n throw new DexError('usage_error', `--${name}\uC740 JSON \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4.`);\n }\n if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {\n throw new DexError('usage_error', `--${name}\uC740 JSON \uAC1D\uCCB4\uC5EC\uC57C \uD569\uB2C8\uB2E4.`);\n }\n return parsed as Record<string, unknown>;\n}\n\nasync function waitForStopSignal(): Promise<void> {\n await new Promise<void>((resolve) => {\n const stop = (): void => {\n process.off('SIGINT', stop);\n process.off('SIGTERM', stop);\n resolve();\n };\n process.once('SIGINT', stop);\n process.once('SIGTERM', stop);\n });\n}\n\nfunction exitCode(error: unknown): number {\n if (!(error instanceof DexError)) return 1;\n if (error.code === 'usage_error' || error.code === 'config_invalid') return 2;\n if (error.code === 'auth_required' || error.code === 'auth_invalid') return 3;\n if (error.code === 'network_error') return 4;\n return 1;\n}\n\nasync function runChat(engine: DexEngine, args: ReturnType<typeof parseArgs>): Promise<void> {\n const workflowId = requiredOption(args, 'agent');\n const input = flag(args, 'stdin') || !stdin.isTTY ? await readStdin() : await promptLine('Message: ');\n if (!input.trim()) throw new DexError('usage_error', '\uBCF4\uB0BC \uBA54\uC2DC\uC9C0\uAC00 \uBE44\uC5B4 \uC788\uC2B5\uB2C8\uB2E4.');\n const resolved = await engine.resolveChatInput({\n profile: option(args, 'profile'),\n workflowId,\n workflowName: option(args, 'name'),\n interactionId: option(args, 'interaction'),\n input,\n });\n const jsonl = flag(args, 'jsonl');\n if (jsonl) writeJson({ kind: 'start', ...resolved, input: undefined });\n else stderr.write(`interaction: ${resolved.interactionId}\\n`);\n\n const controller = new AbortController();\n const onInterrupt = (): void => controller.abort();\n process.once('SIGINT', onInterrupt);\n try {\n for await (const event of engine.chat(resolved, controller.signal)) {\n if (jsonl) {\n stdout.write(`${JSON.stringify(event)}\\n`);\n } else if (event.kind === 'text') {\n stdout.write(event.content);\n } else if (event.kind === 'summary') {\n stdout.write(event.text);\n } else {\n const description = describeEvent(event);\n if (description) stderr.write(`[${description}]\\n`);\n }\n }\n if (!jsonl) stdout.write('\\n');\n } finally {\n process.off('SIGINT', onInterrupt);\n engine.stopLocalTools();\n }\n}\n\nasync function run(): Promise<void> {\n const args = parseArgs(process.argv.slice(2));\n if (flag(args, 'version')) {\n stdout.write(`${VERSION}\\n`);\n return;\n }\n if (flag(args, 'help')) {\n stdout.write(HELP);\n return;\n }\n\n // \uC5D4\uC9C4\uC5D0 \uC774 \uD638\uC2A4\uD2B8\uB97C \uBD99\uC778\uB2E4 \u2014 **\uC5D4\uC9C4\uC744 \uB9CC\uB4E4\uAE30 \uC804\uC5D0**. \uB85C\uCEEC \uB3C4\uAD6C\uC640 MCP \uAC00 \uC804\uBD80\n // \uC774 \uD3EC\uD2B8\uB4E4 \uC704\uC5D0\uC11C \uB3CC\uACE0, \uBD99\uAE30 \uC804\uC5D0 \uAC74\uB4DC\uB9AC\uBA74 \uC5D4\uC9C4\uC774 \uBA85\uD655\uD788 \uB358\uC9C4\uB2E4.\n const configStore = new FileConfigStore();\n bindCliHost(configStore);\n const engine = new DexEngine(configStore, new KeytarCredentialStore());\n const terminal = {\n stdinIsTty: !!stdin.isTTY,\n stdoutIsTty: !!stdout.isTTY,\n term: process.env.TERM,\n ci: process.env.CI,\n };\n if (shouldLaunchTui(args.positionals, terminal)) {\n const { runTui } = await import('./tui/index');\n try {\n await runTui(engine);\n } finally {\n engine.stopLocalTools();\n }\n return;\n }\n if (args.positionals.length === 0) {\n stdout.write(HELP);\n return;\n }\n if (args.positionals[0] === 'ui' && !isInteractiveTerminal(terminal)) {\n throw new DexError('usage_error', '\uD130\uBBF8\uB110 UI\uB294 \uB300\uD654\uD615 TTY\uC5D0\uC11C\uB9CC \uC2E4\uD589\uD560 \uC218 \uC788\uC2B5\uB2C8\uB2E4.');\n }\n const [rawCommand, action] = args.positionals;\n const command = rawCommand === 'tool' ? 'tools' : rawCommand;\n const asJson = flag(args, 'json');\n\n if (command === 'profile' && action === 'set') {\n const profile = await engine.setProfile(args.positionals[2] ?? 'default', requiredOption(args, 'server'));\n if (asJson) writeJson(profile);\n else stdout.write(`\uD504\uB85C\uD544 \uC800\uC7A5: ${profile.name} \u2192 ${profile.serverUrl}\\n`);\n return;\n }\n if (command === 'profile' && action === 'use') {\n const name = args.positionals[2];\n if (!name) throw new DexError('usage_error', '\uC0AC\uC6A9\uD560 \uD504\uB85C\uD544 \uC774\uB984\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.');\n const profile = await engine.useProfile(name);\n if (asJson) writeJson(profile);\n else stdout.write(`\uD604\uC7AC \uD504\uB85C\uD544: ${profile.name}\\n`);\n return;\n }\n if (command === 'profile' && action === 'list') {\n const profiles = await engine.listProfiles();\n if (asJson) writeJson(profiles);\n else if (profiles.length === 0) stdout.write('\uD504\uB85C\uD544\uC774 \uC5C6\uC2B5\uB2C8\uB2E4.\\n');\n else {\n for (const profile of profiles) {\n stdout.write(`${profile.current ? '*' : ' '} ${profile.name.padEnd(16)} ${profile.serverUrl}\\n`);\n }\n }\n return;\n }\n if (command === 'login') {\n const password =\n flag(args, 'password-stdin') || !stdin.isTTY ? await readStdin() : await promptSecret('Password: ');\n const status = await engine.login(requiredOption(args, 'email'), password, option(args, 'profile'));\n if (asJson) writeJson(status);\n else stdout.write(`\uB85C\uADF8\uC778\uB428: ${status.user?.username ?? 'unknown'} (${status.profile})\\n`);\n return;\n }\n if (command === 'status') {\n const status = await engine.authStatus(option(args, 'profile'));\n if (asJson) writeJson(status);\n else if (status.authenticated) {\n stdout.write(`\uB85C\uADF8\uC778\uB428: ${status.user?.username ?? 'unknown'} @ ${status.serverUrl}\\n`);\n } else {\n stdout.write(`\uB85C\uADF8\uC544\uC6C3\uB428: ${status.profile} (${status.reason ?? 'unknown'})\\n`);\n }\n return;\n }\n if (command === 'logout') {\n await engine.logout(option(args, 'profile'));\n if (asJson) writeJson({ ok: true });\n else stdout.write('\uB85C\uADF8\uC544\uC6C3\uD588\uC2B5\uB2C8\uB2E4.\\n');\n return;\n }\n if (command === 'tools' && action === 'list') {\n // \uC804\uCCB4 \uCE74\uD0C8\uB85C\uADF8\uB97C \uBCF4\uC5EC \uC8FC\uB418 **\uC9C0\uAE08 \uB178\uCD9C \uC911\uC778\uC9C0**\uB97C \uD568\uAED8 \uB9D0\uD55C\uB2E4. \uB178\uCD9C \uBAA9\uB85D\uB9CC\n // \uBCF4\uC5EC \uC8FC\uBA74 \uAEBC \uB454 \uC0AC\uC6A9\uC790\uC5D0\uAC8C\uB294 \uBE48 \uD654\uBA74\uC774\uB77C \"\uBB58 \uD560 \uC218 \uC788\uB294\uC9C0\" \uC54C \uC218 \uC5C6\uACE0,\n // \uCE74\uD0C8\uB85C\uADF8\uB9CC \uBCF4\uC5EC \uC8FC\uBA74 \uAEBC \uB454 \uC904 \uBAA8\uB974\uACE0 \"\uC65C \uC548 \uB418\uC9C0\"\uB97C \uBB3B\uAC8C \uB41C\uB2E4.\n const status = await engine.localToolsStatus();\n const exposed = new Set(status.tools.map((t) => t.name));\n if (asJson) writeJson({ enabled: status.config.enabled, catalog: status.catalog, exposed: [...exposed] });\n else {\n if (!status.config.enabled) {\n stdout.write('\uB85C\uCEEC \uB3C4\uAD6C\uAC00 \uAEBC\uC838 \uC788\uC2B5\uB2C8\uB2E4 \u2014 \uC544\uB798\uB294 \uCF30\uC744 \uB54C \uC4F8 \uC218 \uC788\uB294 \uBAA9\uB85D\uC785\uB2C8\uB2E4.\\n');\n stdout.write('\uCF1C\uAE30: dex tools enable\\n\\n');\n }\n stdout.write(`${cell('TOOL', 14)} ${cell('\uB178\uCD9C', 5)} DESCRIPTION\\n`);\n for (const tool of status.catalog) {\n // \uC124\uBA85 \uCCAB \uC904\uB9CC \u2014 \uC774 \uB3C4\uAD6C\uB4E4\uC758 description \uC740 \uBAA8\uB378\uC744 \uC704\uD55C \uAC83\uC774\uB77C \uC218\uC2ED \uC904\uC774\uB2E4.\n // \uC804\uBB38\uC740 --json \uC73C\uB85C \uBCF8\uB2E4.\n const summary = String(tool.description ?? '').split('\\n')[0] ?? '';\n stdout.write(\n `${cell(tool.name, 14)} ${cell(exposed.has(tool.name) ? '\u25CF' : '\u00B7', 5)} ${summary.slice(0, 96)}\\n`,\n );\n }\n }\n return;\n }\n if (command === 'tools' && (action === 'enable' || action === 'configure')) {\n const current = (await engine.localToolsStatus()).config;\n const cwd = option(args, 'cwd') || current.cwd || process.cwd();\n const timeoutRaw = option(args, 'timeout');\n const timeoutMs = timeoutRaw === undefined ? current.timeoutMs : Number(timeoutRaw);\n if (!Number.isFinite(timeoutMs) || timeoutMs < 1_000 || timeoutMs > 3_600_000) {\n throw new DexError('usage_error', '--timeout\uC740 1000~3600000 \uC0AC\uC774\uC758 \uBC00\uB9AC\uCD08\uC5EC\uC57C \uD569\uB2C8\uB2E4.');\n }\n const allowedRoots = csvOption(args, 'allow') ?? (current.allowedRoots.length ? current.allowedRoots : [cwd]);\n const blockedCommands = csvOption(args, 'block') ?? current.blockedCommands;\n const allowDangerous = flag(args, 'allow-dangerous')\n ? true\n : flag(args, 'no-allow-dangerous')\n ? false\n : current.allowDangerous;\n const status = await engine.configureLocalTools({\n enabled: action === 'enable' ? true : current.enabled,\n cwd,\n timeoutMs,\n allowedRoots,\n blockedCommands,\n allowDangerous,\n });\n if (asJson) writeJson(status);\n else printLocalToolsStatus(status);\n return;\n }\n if (command === 'tools' && action === 'disable') {\n const status = await engine.configureLocalTools({ enabled: false });\n if (asJson) writeJson(status);\n else printLocalToolsStatus(status);\n return;\n }\n if (command === 'tools' && action === 'run') {\n const tool = args.positionals[2];\n if (!tool) throw new DexError('usage_error', '\uC2E4\uD589\uD560 \uB85C\uCEEC \uB3C4\uAD6C \uC774\uB984\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.');\n const result = await engine.runLocalTool(tool, jsonObjectOption(args, 'args'));\n if (asJson) writeJson(result);\n else for (const content of result.content) stdout.write(`${content.text}\\n`);\n if (result.isError) process.exitCode = 1;\n return;\n }\n if (command === 'tools' && (action === 'status' || action === 'serve')) {\n let status = await engine.localToolsStatus();\n if (status.config.enabled) {\n try {\n status = await engine.startLocalTools(option(args, 'profile'), action === 'serve' ? 5_000 : 2_000);\n } catch (error) {\n if (action === 'serve') throw error;\n status = {\n ...status,\n bridge: { ...status.bridge, error: error instanceof Error ? error.message : String(error) },\n };\n }\n }\n if (asJson) writeJson(status);\n else printLocalToolsStatus(status);\n if (action === 'serve') {\n if (!status.config.enabled) throw new DexError('local_tools_disabled', '\uBA3C\uC800 dex tools enable\uC744 \uC2E4\uD589\uD558\uC138\uC694.');\n if (!asJson) stderr.write('\uB85C\uCEEC \uB3C4\uAD6C \uBE0C\uB9AC\uC9C0\uAC00 \uC2E4\uD589 \uC911\uC785\uB2C8\uB2E4. \uC885\uB8CC\uD558\uB824\uBA74 Ctrl+C\uB97C \uB204\uB974\uC138\uC694.\\n');\n try {\n await waitForStopSignal();\n } finally {\n engine.stopLocalTools();\n }\n } else {\n engine.stopLocalTools();\n }\n return;\n }\n if (command === 'agents' && action === 'list') {\n const owner = option(args, 'owner');\n if (owner && owner !== 'personal' && owner !== 'shared') {\n throw new DexError('usage_error', '--owner\uB294 personal \uB610\uB294 shared\uC5EC\uC57C \uD569\uB2C8\uB2E4.');\n }\n const query: AgentListQuery = {\n page: positiveIntegerOption(args, 'page'),\n pageSize: positiveIntegerOption(args, 'page-size'),\n search: option(args, 'search'),\n owner: owner as AgentListQuery['owner'],\n status: option(args, 'status'),\n includeHarness: flag(args, 'include-harness'),\n };\n const result = await engine.listAgents(query, option(args, 'profile'));\n if (asJson) writeJson(result);\n else printAgents(result.items);\n return;\n }\n if (command === 'chat') {\n await runChat(engine, args);\n return;\n }\n if (command === 'history' && action === 'list') {\n const conversations = await engine.listConversations(option(args, 'profile'));\n if (asJson) writeJson(conversations);\n else printConversations(conversations);\n return;\n }\n if (command === 'history' && action === 'turns') {\n const turns = await engine.historyTurns(\n requiredOption(args, 'workflow'),\n requiredOption(args, 'interaction'),\n option(args, 'name'),\n option(args, 'profile'),\n );\n if (asJson) writeJson(turns);\n else printTurns(turns);\n return;\n }\n if (command === 'serve') {\n if (!flag(args, 'stdio')) throw new DexError('usage_error', '\uD604\uC7AC\uB294 serve --stdio\uB9CC \uC9C0\uC6D0\uD569\uB2C8\uB2E4.');\n const server = new DexRpcServer(engine, { version: VERSION });\n server.start();\n return;\n }\n // \u2500\u2500 SSH \u2500\u2500\n // \uBAA9\uB85D\u00B7\uCF1C\uACE0 \uB044\uAE30\u00B7\uC5F0\uACB0 \uD14C\uC2A4\uD2B8\uB9CC \uC5F0\uB2E4. \uC11C\uBC84 \uB4F1\uB85D/\uD3B8\uC9D1\uC740 \uC790\uACA9\uC99D\uBA85 \uC785\uB825\uC774\uB77C\n // \uB9C8\uC774\uD398\uC774\uC9C0\uB098 \uB370\uC2A4\uD06C\uD1B1\uC5D0\uC11C \uD558\uB294 \uD3B8\uC774 \uC548\uC804\uD558\uACE0, \uADF8\uCABD \uD654\uBA74\uC774 \uC774\uBBF8 \uC788\uB2E4.\n if (command === 'ssh' && (action === 'list' || action === undefined)) {\n const config = await engine.sshConfig(option(args, 'profile'));\n if (asJson) writeJson(config);\n else {\n stdout.write(`SSH \uC5F0\uB3D9: ${config.enabled ? '\uCF1C\uC9D0' : '\uAEBC\uC9D0'}\\n`);\n if (config.servers.length === 0) stdout.write('\uB4F1\uB85D\uB41C \uC11C\uBC84\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4.\\n');\n for (const srv of config.servers) {\n const via = srv.jump_via.length ? ` (\uACBD\uC720 ${srv.jump_via.join(' \u2192 ')})` : '';\n const off = srv.enabled ? '' : ' [\uC0AC\uC6A9 \uC548 \uD568]';\n stdout.write(`${cell(srv.name, 18)} ${srv.username}@${srv.host}:${srv.port} ${srv.auth}${via}${off}\\n`);\n }\n }\n return;\n }\n if (command === 'ssh' && (action === 'enable' || action === 'disable')) {\n const config = await engine.setSshEnabled(action === 'enable', option(args, 'profile'));\n if (asJson) writeJson(config);\n else stdout.write(`SSH \uC5F0\uB3D9\uC744 ${config.enabled ? '\uCF30\uC2B5\uB2C8\uB2E4' : '\uAED0\uC2B5\uB2C8\uB2E4'}.\\n`);\n return;\n }\n if (command === 'ssh' && action === 'test') {\n const name = args.positionals[2];\n if (!name) throw new DexError('usage_error', '\uC11C\uBC84 \uC774\uB984\uC774 \uD544\uC694\uD569\uB2C8\uB2E4: dex ssh test <name>');\n const result = await engine.testSshServer(name, option(args, 'profile'));\n if (asJson) writeJson(result);\n else {\n stdout.write(\n result.success\n ? `\uC811\uC18D \uC131\uACF5 (${Math.round(result.latency_ms ?? 0)}ms)\\n`\n : `\uC811\uC18D \uC2E4\uD328 \u2014 ${result.error ?? ''}\\n`,\n );\n // 3\uB2E8 \uACBD\uB85C\uC5D0\uC11C \"\uC2E4\uD328\"\uB9CC \uC54C\uBA74 \uC5B4\uB514\uB97C \uACE0\uCCD0\uC57C \uD560\uC9C0 \uC54C \uC218 \uC5C6\uB2E4.\n if (result.hops && result.hops.length > 1) {\n stdout.write(`\uACBD\uB85C: ${result.hops.join(' \u2192 ')}\\n`);\n }\n }\n return;\n }\n\n throw new DexError('usage_error', `\uC54C \uC218 \uC5C6\uB294 \uBA85\uB839\uC785\uB2C8\uB2E4: ${args.positionals.join(' ')}`);\n}\n\nrun().catch((error: unknown) => {\n const exposed = publicError(error);\n const machine = process.argv.includes('--json') || process.argv.includes('--jsonl');\n if (machine) stderr.write(`${JSON.stringify({ error: exposed })}\\n`);\n else stderr.write(`dex: ${exposed.message}\\n`);\n process.exitCode = exitCode(error);\n});\n", "import { DexError } from '@dex/engine';\n\nconst BOOLEAN_OPTIONS = new Set([\n 'allow-dangerous',\n 'help',\n 'include-harness',\n 'json',\n 'jsonl',\n 'no-allow-dangerous',\n 'password-stdin',\n 'stdin',\n 'stdio',\n 'version',\n]);\n\nexport interface ParsedArgs {\n positionals: string[];\n options: Map<string, string | true>;\n}\n\nexport function parseArgs(argv: string[]): ParsedArgs {\n const positionals: string[] = [];\n const options = new Map<string, string | true>();\n for (let index = 0; index < argv.length; index += 1) {\n const argument = argv[index];\n if (argument === '-h') {\n options.set('help', true);\n continue;\n }\n if (argument === '-v') {\n options.set('version', true);\n continue;\n }\n if (!argument.startsWith('--')) {\n positionals.push(argument);\n continue;\n }\n const equal = argument.indexOf('=');\n const name = argument.slice(2, equal >= 0 ? equal : undefined);\n if (!name) throw new DexError('usage_error', `\uC798\uBABB\uB41C option\uC785\uB2C8\uB2E4: ${argument}`);\n if (equal >= 0) {\n options.set(name, argument.slice(equal + 1));\n continue;\n }\n if (BOOLEAN_OPTIONS.has(name)) {\n options.set(name, true);\n continue;\n }\n const value = argv[index + 1];\n if (value === undefined || value.startsWith('--')) {\n throw new DexError('usage_error', `--${name} \uAC12\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.`);\n }\n options.set(name, value);\n index += 1;\n }\n return { positionals, options };\n}\n\nexport function option(args: ParsedArgs, name: string): string | undefined {\n const value = args.options.get(name);\n return typeof value === 'string' ? value : undefined;\n}\n\nexport function requiredOption(args: ParsedArgs, name: string): string {\n const value = option(args, name);\n if (!value) throw new DexError('usage_error', `--${name} \uAC12\uC774 \uD544\uC694\uD569\uB2C8\uB2E4.`);\n return value;\n}\n\nexport function flag(args: ParsedArgs, name: string): boolean {\n return args.options.get(name) === true;\n}\n\nexport function positiveIntegerOption(args: ParsedArgs, name: string): number | undefined {\n const raw = option(args, name);\n if (raw === undefined) return undefined;\n const value = Number(raw);\n if (!Number.isInteger(value) || value < 1) {\n throw new DexError('usage_error', `--${name}\uC740 \uC591\uC758 \uC815\uC218\uC5EC\uC57C \uD569\uB2C8\uB2E4.`);\n }\n return value;\n}\n", "import { createInterface } from 'node:readline/promises';\nimport { stdin, stderr } from 'node:process';\n\nexport async function readStdin(): Promise<string> {\n const chunks: Buffer[] = [];\n for await (const chunk of stdin) chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n return Buffer.concat(chunks).toString('utf8').replace(/[\\r\\n]+$/, '');\n}\n\nexport async function promptLine(label: string): Promise<string> {\n const readline = createInterface({ input: stdin, output: stderr });\n try {\n return await readline.question(label);\n } finally {\n readline.close();\n }\n}\n\nexport async function promptSecret(label: string): Promise<string> {\n if (!stdin.isTTY || typeof stdin.setRawMode !== 'function') return readStdin();\n stderr.write(label);\n return new Promise<string>((resolve, reject) => {\n let value = '';\n const cleanup = (): void => {\n stdin.off('data', onData);\n stdin.setRawMode(false);\n stdin.pause();\n stderr.write('\\n');\n };\n const onData = (chunk: Buffer | string): void => {\n const text = chunk.toString();\n for (const character of text) {\n if (character === '\\u0003') {\n cleanup();\n reject(Object.assign(new Error('\uC785\uB825\uC774 \uCDE8\uC18C\uB418\uC5C8\uC2B5\uB2C8\uB2E4.'), { name: 'AbortError' }));\n return;\n }\n if (character === '\\r' || character === '\\n') {\n cleanup();\n resolve(value);\n return;\n }\n if (character === '\\u007f' || character === '\\b') value = value.slice(0, -1);\n else if (character >= ' ') value += character;\n }\n };\n stdin.setRawMode(true);\n stdin.resume();\n stdin.on('data', onData);\n });\n}\n", "export interface TerminalModeInput {\n stdinIsTty: boolean;\n stdoutIsTty: boolean;\n term?: string;\n ci?: string;\n}\n\nexport function isInteractiveTerminal(input: TerminalModeInput): boolean {\n const ci = input.ci?.trim().toLowerCase();\n const isCi = !!ci && ci !== 'false' && ci !== '0';\n return input.stdinIsTty && input.stdoutIsTty && input.term !== 'dumb' && !isCi;\n}\n\nexport function shouldLaunchTui(positionals: string[], terminal: TerminalModeInput): boolean {\n if (!isInteractiveTerminal(terminal)) return false;\n return positionals.length === 0 || (positionals.length === 1 && positionals[0] === 'ui');\n}\n", "import { createInterface, type Interface as ReadlineInterface } from 'node:readline';\nimport type { Readable, Writable } from 'node:stream';\nimport { randomUUID } from 'node:crypto';\nimport { DexEngine, DexError, publicError } from '@dex/engine';\nimport type {\n AgentListQuery,\n ChatInput,\n LocalToolsConfig,\n ResolvedChatInput,\n} from '@dex/engine';\nimport { DEX_PROTOCOL_VERSION } from './wire';\n\ntype RpcId = string | number | null;\n\ninterface RpcRequest {\n jsonrpc: '2.0';\n id?: RpcId;\n method: string;\n params?: unknown;\n}\n\nclass RpcFailure extends Error {\n constructor(\n readonly rpcCode: number,\n message: string,\n readonly data?: unknown,\n ) {\n super(message);\n }\n}\n\nfunction objectParams(params: unknown): Record<string, unknown> {\n if (params === undefined) return {};\n if (!params || typeof params !== 'object' || Array.isArray(params)) {\n throw new RpcFailure(-32602, 'params must be an object');\n }\n return params as Record<string, unknown>;\n}\n\nfunction requiredString(params: Record<string, unknown>, key: string): string {\n const value = params[key];\n if (typeof value !== 'string' || !value.trim()) throw new RpcFailure(-32602, `${key} is required`);\n return value;\n}\n\nfunction optionalString(params: Record<string, unknown>, key: string): string | undefined {\n const value = params[key];\n if (value === undefined || value === null || value === '') return undefined;\n if (typeof value !== 'string') throw new RpcFailure(-32602, `${key} must be a string`);\n return value;\n}\n\nfunction optionalInteger(params: Record<string, unknown>, key: string): number | undefined {\n const value = params[key];\n if (value === undefined || value === null) return undefined;\n if (!Number.isInteger(value) || Number(value) < 1) {\n throw new RpcFailure(-32602, `${key} must be a positive integer`);\n }\n return Number(value);\n}\n\nfunction optionalBoolean(params: Record<string, unknown>, key: string): boolean | undefined {\n const value = params[key];\n if (value === undefined || value === null) return undefined;\n if (typeof value !== 'boolean') throw new RpcFailure(-32602, `${key} must be a boolean`);\n return value;\n}\n\nfunction optionalStringArray(params: Record<string, unknown>, key: string): string[] | undefined {\n const value = params[key];\n if (value === undefined || value === null) return undefined;\n if (!Array.isArray(value) || value.some((item) => typeof item !== 'string')) {\n throw new RpcFailure(-32602, `${key} must be an array of strings`);\n }\n return value.map((item) => item.trim()).filter(Boolean);\n}\n\nfunction isRequest(value: unknown): value is RpcRequest {\n if (!value || typeof value !== 'object' || Array.isArray(value)) return false;\n const request = value as Record<string, unknown>;\n return request.jsonrpc === '2.0' && typeof request.method === 'string';\n}\n\nexport interface RpcServerOptions {\n input?: Readable;\n output?: Writable;\n log?: (message: string) => void;\n version?: string;\n}\n\nexport class DexRpcServer {\n private readonly input: Readable;\n private readonly output: Writable;\n private readonly log: (message: string) => void;\n private readonly version: string;\n private readonly activeChats = new Map<string, AbortController>();\n private readline: ReadlineInterface | null = null;\n private initialized = false;\n private closed = false;\n private readonly removeLocalToolsListener: () => void;\n\n constructor(\n private readonly engine: DexEngine,\n options: RpcServerOptions = {},\n ) {\n this.input = options.input ?? process.stdin;\n this.output = options.output ?? process.stdout;\n this.log = options.log ?? ((message) => process.stderr.write(`${message}\\n`));\n this.version = options.version ?? '0.1.0';\n this.removeLocalToolsListener = engine.onLocalToolsStatus((status) => {\n if (this.initialized && !this.closed) this.notify('localTools/status', status);\n });\n }\n\n start(): void {\n if (this.readline) return;\n this.readline = createInterface({ input: this.input, crlfDelay: Infinity });\n this.readline.on('line', (line) => void this.onLine(line));\n this.readline.on('close', () => this.close());\n }\n\n close(): void {\n if (this.closed) return;\n this.closed = true;\n for (const controller of this.activeChats.values()) controller.abort();\n this.activeChats.clear();\n this.engine.stopLocalTools();\n this.removeLocalToolsListener();\n if (this.readline) {\n const readline = this.readline;\n this.readline = null;\n readline.close();\n }\n this.input.pause();\n }\n\n private async onLine(line: string): Promise<void> {\n if (!line.trim() || this.closed) return;\n let value: unknown;\n try {\n value = JSON.parse(line);\n } catch {\n this.writeError(null, -32700, 'Parse error');\n return;\n }\n if (!isRequest(value)) {\n this.writeError(null, -32600, 'Invalid Request');\n return;\n }\n const hasId = Object.prototype.hasOwnProperty.call(value, 'id');\n try {\n if (!this.initialized && value.method !== 'initialize' && value.method !== 'exit') {\n throw new RpcFailure(-32002, 'initialize must be called first');\n }\n const result = await this.dispatch(value.method, value.params);\n if (hasId) this.write({ jsonrpc: '2.0', id: value.id ?? null, result });\n if (value.method === 'shutdown' || value.method === 'exit') setImmediate(() => this.close());\n } catch (error) {\n if (!hasId) {\n this.log(`notification ${value.method} failed: ${publicError(error).message}`);\n return;\n }\n if (error instanceof RpcFailure) {\n this.writeError(value.id ?? null, error.rpcCode, error.message, error.data);\n return;\n }\n const exposed = publicError(error);\n this.writeError(value.id ?? null, -32000, exposed.message, exposed);\n }\n }\n\n private async dispatch(method: string, rawParams: unknown): Promise<unknown> {\n const params = objectParams(rawParams);\n switch (method) {\n case 'initialize': {\n const requested = params.protocolVersion;\n if (requested !== DEX_PROTOCOL_VERSION) {\n throw new DexError(\n 'protocol_mismatch',\n `\uC9C0\uC6D0\uD558\uC9C0 \uC54A\uB294 protocolVersion\uC785\uB2C8\uB2E4: ${String(requested)}`,\n { supported: DEX_PROTOCOL_VERSION },\n );\n }\n this.initialized = true;\n setImmediate(() => {\n void this.engine.startLocalTools().catch((error: unknown) =>\n this.log(`local tools: ${publicError(error).message}`),\n );\n });\n return {\n protocolVersion: DEX_PROTOCOL_VERSION,\n server: { name: 'dex-cli', version: this.version },\n capabilities: {\n profiles: true,\n authentication: ['password'],\n agents: true,\n chatStreaming: true,\n chatCancellation: true,\n history: true,\n localTools: true,\n ssh: true,\n },\n };\n }\n case 'shutdown':\n case 'exit':\n this.engine.stopLocalTools();\n return null;\n case 'health':\n return { ok: true, activeChats: this.activeChats.size };\n case 'profile/list':\n return this.engine.listProfiles();\n case 'profile/set':\n return this.engine.setProfile(requiredString(params, 'name'), requiredString(params, 'serverUrl'));\n case 'profile/use': {\n const profile = await this.engine.useProfile(requiredString(params, 'name'));\n void this.engine.startLocalTools(profile.name).catch((error: unknown) => this.log(`local tools: ${publicError(error).message}`));\n return profile;\n }\n case 'auth/login': {\n const auth = await this.engine.login(\n requiredString(params, 'email'),\n requiredString(params, 'password'),\n optionalString(params, 'profile'),\n );\n void this.engine.startLocalTools(auth.profile).catch((error: unknown) => this.log(`local tools: ${publicError(error).message}`));\n return auth;\n }\n case 'auth/status':\n return this.engine.authStatus(optionalString(params, 'profile'));\n case 'auth/logout':\n await this.engine.logout(optionalString(params, 'profile'));\n return { ok: true };\n // \u2500\u2500 SSH \u2500\u2500\n // \uD504\uB85C\uD1A0\uCF5C\uC5D0\uB294 Teams \u00B7 \uC74C\uC131 \u00B7 \uC54C\uB9BC\uB3C4 \uC788\uC9C0\uB9CC RPC \uB85C \uC5F4\uC9C0 \uC54A\uB294\uB2E4. CLI \uC640\n // \uD3B8\uC9D1\uAE30\uC5D0\uC11C \uC4F8 \uC77C\uC774 \uC544\uC9C1 \uC5C6\uACE0, \uC5F4\uC5B4 \uB450\uBA74 \"\uB418\uB294 \uC904 \uC54C\uACE0\" \uBD80\uB974\uB294 \uACBD\uB85C\uAC00\n // \uC0DD\uAE34\uB2E4. \uD0C0\uC785\uC740 @dex/protocol \uC5D0 \uADF8\uB300\uB85C \uC788\uC73C\uBBC0\uB85C \uC5EC\uB294 \uAC83\uC740 \uD55C \uC904\uC774\uB2E4.\n case 'ssh/config':\n return this.engine.sshConfig(optionalString(params, 'profile'));\n case 'ssh/setEnabled':\n return this.engine.setSshEnabled(\n params.enabled === true,\n optionalString(params, 'profile'),\n );\n case 'ssh/createServer':\n return this.engine.createSshServer(\n objectParams(params.server),\n optionalString(params, 'profile'),\n );\n case 'ssh/updateServer':\n return this.engine.updateSshServer(\n requiredString(params, 'name'),\n objectParams(params.server),\n optionalString(params, 'profile'),\n );\n case 'ssh/deleteServer':\n return this.engine.deleteSshServer(\n requiredString(params, 'name'),\n optionalString(params, 'profile'),\n );\n case 'ssh/testServer':\n return this.engine.testSshServer(\n requiredString(params, 'name'),\n optionalString(params, 'profile'),\n );\n\n case 'localTools/status':\n return this.engine.localToolsStatus();\n case 'localTools/list':\n return (await this.engine.localToolsStatus()).tools;\n case 'localTools/configure': {\n const patch: Partial<LocalToolsConfig> = {};\n const enabled = optionalBoolean(params, 'enabled');\n const cwd = optionalString(params, 'cwd');\n const timeoutMs = optionalInteger(params, 'timeoutMs');\n const allowedRoots = optionalStringArray(params, 'allowedRoots');\n const blockedCommands = optionalStringArray(params, 'blockedCommands');\n const allowDangerous = optionalBoolean(params, 'allowDangerous');\n if (enabled !== undefined) patch.enabled = enabled;\n if (cwd !== undefined) patch.cwd = cwd;\n if (timeoutMs !== undefined) patch.timeoutMs = timeoutMs;\n if (allowedRoots !== undefined) patch.allowedRoots = allowedRoots;\n if (blockedCommands !== undefined) patch.blockedCommands = blockedCommands;\n if (allowDangerous !== undefined) patch.allowDangerous = allowDangerous;\n const status = await this.engine.configureLocalTools(patch);\n if (status.config.enabled) {\n void this.engine.startLocalTools(optionalString(params, 'profile')).catch((error: unknown) =>\n this.log(`local tools: ${publicError(error).message}`),\n );\n }\n return status;\n }\n case 'localTools/run':\n return this.engine.runLocalTool(requiredString(params, 'tool'), params.args ?? {});\n case 'localTools/start':\n return this.engine.startLocalTools(optionalString(params, 'profile'), optionalInteger(params, 'waitMs') ?? 0);\n case 'localTools/stop':\n this.engine.stopLocalTools();\n return this.engine.localToolsStatus();\n case 'agents/list': {\n const owner = optionalString(params, 'owner');\n if (owner && owner !== 'personal' && owner !== 'shared') {\n throw new RpcFailure(-32602, 'owner must be personal or shared');\n }\n const query: AgentListQuery = {\n page: optionalInteger(params, 'page'),\n pageSize: optionalInteger(params, 'pageSize'),\n search: optionalString(params, 'search'),\n status: optionalString(params, 'status'),\n owner: owner as AgentListQuery['owner'],\n includeHarness: params.includeHarness === true,\n };\n return this.engine.listAgents(query, optionalString(params, 'profile'));\n }\n case 'history/conversations':\n return this.engine.listConversations(optionalString(params, 'profile'));\n case 'history/turns':\n return this.engine.historyTurns(\n requiredString(params, 'workflowId'),\n requiredString(params, 'interactionId'),\n optionalString(params, 'workflowName'),\n optionalString(params, 'profile'),\n );\n case 'chat/start':\n return this.startChat(params);\n case 'chat/cancel': {\n const streamId = requiredString(params, 'streamId');\n const controller = this.activeChats.get(streamId);\n if (!controller) return { cancelled: false };\n controller.abort();\n return { cancelled: true };\n }\n default:\n throw new RpcFailure(-32601, `Method not found: ${method}`);\n }\n }\n\n private async startChat(params: Record<string, unknown>): Promise<{\n streamId: string;\n interactionId: string;\n workflowId: string;\n workflowName: string;\n }> {\n const rawInput = params.input;\n if (\n typeof rawInput !== 'string' &&\n !Array.isArray(rawInput) &&\n (!rawInput || typeof rawInput !== 'object')\n ) {\n throw new RpcFailure(-32602, 'input must be a string, object, or array');\n }\n const input: ChatInput = {\n profile: optionalString(params, 'profile'),\n workflowId: requiredString(params, 'workflowId'),\n workflowName: optionalString(params, 'workflowName'),\n interactionId: optionalString(params, 'interactionId'),\n input: rawInput as ChatInput['input'],\n };\n const resolved = await this.engine.resolveChatInput(input);\n const streamId = optionalString(params, 'streamId') ?? randomUUID();\n if (this.activeChats.has(streamId)) throw new RpcFailure(-32602, `streamId already exists: ${streamId}`);\n const controller = new AbortController();\n this.activeChats.set(streamId, controller);\n // start response\uAC00 notification\uBCF4\uB2E4 \uBC18\uB4DC\uC2DC \uBA3C\uC800 \uB098\uAC00\uC57C client\uAC00 streamId\uB97C \uB4F1\uB85D\uD560 \uC218 \uC788\uB2E4.\n setImmediate(() => void this.runChat(streamId, resolved, controller));\n return {\n streamId,\n interactionId: resolved.interactionId,\n workflowId: resolved.workflowId,\n workflowName: resolved.workflowName,\n };\n }\n\n private async runChat(\n streamId: string,\n input: ResolvedChatInput,\n controller: AbortController,\n ): Promise<void> {\n try {\n for await (const event of this.engine.chat(input, controller.signal)) {\n this.notify('chat/event', { streamId, event });\n }\n this.notify('chat/complete', { streamId, interactionId: input.interactionId });\n } catch (error) {\n const exposed = publicError(error);\n this.notify('chat/error', { streamId, error: exposed });\n } finally {\n this.activeChats.delete(streamId);\n }\n }\n\n private notify(method: string, params: unknown): void {\n this.write({ jsonrpc: '2.0', method, params });\n }\n\n private writeError(id: RpcId, code: number, message: string, data?: unknown): void {\n this.write({\n jsonrpc: '2.0',\n id,\n error: { code, message, ...(data === undefined ? {} : { data }) },\n });\n }\n\n private write(value: unknown): void {\n if (this.closed) return;\n this.output.write(`${JSON.stringify(value)}\\n`);\n }\n}\n", "/**\n * RPC \uB85C \uC624\uAC00\uB294 \uAC83\uC758 \uBAA8\uC591 \u2014 \uADF8\uB9AC\uACE0 \uC624\uC9C0 \uC54A\uB294 \uAC83.\n *\n * \uC5EC\uAE30\uC11C **\uC0C8\uB85C \uC120\uC5B8\uD558\uB294 \uAC83\uC740 \uBC30\uC120\uBFD0**\uC774\uB2E4(\uCD08\uAE30\uD654 \uACB0\uACFC \u00B7 \uC54C\uB9BC \uBD09\uD22C \u00B7 \uCC44\uD305 \uC2DC\uC791\n * \uC751\uB2F5). \uB3C4\uBA54\uC778 \uD0C0\uC785(Agent \u00B7 Conversation \u00B7 ChatEvent \u2026)\uC740 `@dex/engine` \uC5D0\uC11C\n * \uADF8\uB300\uB85C \uB2E4\uC2DC \uB0B4\uBCF4\uB0B8\uB2E4.\n *\n * \uC608\uC804\uC5D0\uB294 VSCode \uD655\uC7A5\uC774 \uC774 \uD30C\uC77C\uC5D0\uC11C \uADF8 \uC5EC\uB35F \uAC1C\uB97C **\uC190\uC73C\uB85C \uB2E4\uC2DC \uC120\uC5B8**\uD588\uB2E4.\n * \uC11C\uBC84\uAC00 \uD544\uB4DC\uB97C \uD558\uB098 \uB298\uB9AC\uBA74 \uD504\uB85C\uD1A0\uCF5C\u00B7\uC5D4\uC9C4\u00B7\uD655\uC7A5 \uC138 \uACF3\uC744 \uACE0\uCCD0\uC57C \uD588\uACE0, \uD655\uC7A5\uC740 \uB298\n * \uB9C8\uC9C0\uB9C9\uC5D0 \uC78A\uD614\uB2E4. \uC7AC\uC120\uC5B8\uC744 \uC9C0\uC6B0\uBA74 \uC78A\uC744 \uC790\uB9AC\uAC00 \uC5C6\uC5B4\uC9C4\uB2E4.\n */\nimport type {\n Agent,\n AgentListResult,\n AuthStatus,\n ChatEvent,\n Conversation,\n HistoryTurn,\n LocalToolsConfig,\n LocalToolSchema,\n LocalToolsStatus,\n McpBridgeStatus,\n ProfileSummary,\n ToolEvent,\n} from '@dex/engine';\n\nexport type {\n Agent,\n AgentListResult,\n AuthStatus,\n ChatEvent,\n Conversation,\n HistoryTurn,\n LocalToolsConfig,\n LocalToolSchema,\n LocalToolsStatus,\n McpBridgeStatus,\n ProfileSummary,\n ToolEvent,\n};\n\n/** RPC \uBC30\uC120\uC758 \uBC84\uC804. \uC11C\uBC84\uC640 \uD074\uB77C\uC774\uC5B8\uD2B8\uAC00 initialize \uC5D0\uC11C \uB9DE\uCDB0 \uBCF8\uB2E4. */\nexport const DEX_PROTOCOL_VERSION = 1;\n\n/**\n * \uBE0C\uB9BF\uC9C0 \uC0C1\uD0DC\uC758 \uC774\uB984 \u2014 \uC608\uC804 CLI \uB294 `LocalToolBridgeStatus`, \uB370\uC2A4\uD06C\uD1B1\uC740\n * `McpBridgeStatus` \uB77C\uACE0 \uBD88\uB800\uB2E4. \uAC19\uC740 \uAC83\uC774\uB2E4. \uC5D4\uC9C4\uC758 \uC774\uB984\uC744 \uC815\uBCF8\uC73C\uB85C \uC0BC\uACE0\n * \uC61B \uC774\uB984\uC740 \uBCC4\uCE6D\uC73C\uB85C\uB9CC \uB0A8\uAE34\uB2E4(\uD655\uC7A5 \uCF54\uB4DC\uAC00 \uC544\uC9C1 \uADF8 \uC774\uB984\uC744 \uC4F4\uB2E4).\n */\nexport type LocalToolBridgeStatus = McpBridgeStatus;\n\nexport interface InitializeResult {\n protocolVersion: number;\n server: { name: string; version: string };\n capabilities: {\n profiles: boolean;\n authentication: string[];\n agents: boolean;\n chatStreaming: boolean;\n chatCancellation: boolean;\n history: boolean;\n localTools: boolean;\n /** SSH \uC11C\uBC84 \uAD00\uB9AC. Teams \u00B7 \uC74C\uC131\uC740 \uC544\uC9C1 \uC5F4\uC9C0 \uC54A\uC558\uB2E4 \u2014 \uD0C0\uC785\uC740 \uC788\uACE0 \uD45C\uBA74\uB9CC \uC5C6\uB2E4. */\n ssh?: boolean;\n };\n}\n\nexport interface ChatStartResult {\n streamId: string;\n interactionId: string;\n workflowId: string;\n workflowName: string;\n}\n\nexport interface RpcNotification {\n jsonrpc: '2.0';\n method: string;\n params?: unknown;\n}\n\nexport interface ChatEventNotification {\n streamId: string;\n event: ChatEvent;\n}\n\nexport interface ChatCompleteNotification {\n streamId: string;\n interactionId: string;\n}\n\nexport interface ChatErrorNotification {\n streamId: string;\n error: {\n code: string;\n message: string;\n details?: unknown;\n };\n}\n", "/**\n * \uD130\uBBF8\uB110\uC774 \uC5D4\uC9C4\uC5D0\uAC8C \uC8FC\uB294 \uAC83 \u2014 \uD3EC\uD2B8 \uAD6C\uD604 \uC14B.\n *\n * \uB370\uC2A4\uD06C\uD1B1\uC740 \uAC19\uC740 \uC790\uB9AC\uB97C Electron \uC73C\uB85C \uCC44\uC6B4\uB2E4(apps/desktop/src/main/dex-host.ts).\n * \uB450 \uD30C\uC77C\uC744 \uB098\uB780\uD788 \uB193\uACE0 \uBCF4\uBA74 \uC774 \uC800\uC7A5\uC18C\uC758 \uC694\uC810\uC774 \uBCF4\uC778\uB2E4 \u2014 \uB2E4\uB978 \uAC83\uC740 \uC5EC\uAE30\uBFD0\uC774\uACE0,\n * \uADF8 \uC544\uB798 \uC5D4\uC9C4\uC740 \uD55C \uBC8C\uC774\uB2E4.\n */\nimport { spawn } from 'node:child_process';\nimport { createInterface } from 'node:readline';\nimport { platform } from 'node:os';\nimport { stdin, stdout } from 'node:process';\nimport {\n bindHost,\n DANGEROUS_COMMAND_PROMPT,\n openerInvocation,\n type HostPorts,\n type InteractionPort,\n type DexConfig,\n} from '@dex/engine';\nimport { FileConfigStore, dataDirectory } from '@dex/engine';\nimport { KeytarCredentialStore } from '@dex/engine';\n\n/** \uD55C \uC904 \uC2E4\uD589\uD558\uACE0 (\uC131\uACF5, \uD45C\uC900\uCD9C\uB825) \uC744 \uB3CC\uB824\uC900\uB2E4. \uC5C6\uB294 \uBA85\uB839\uC740 \uC870\uC6A9\uD788 \uC2E4\uD328\uD55C\uB2E4. */\nfunction run(file: string, args: string[], input?: string): Promise<{ ok: boolean; out: string }> {\n return new Promise((resolve) => {\n let child;\n try {\n child = spawn(file, args, { stdio: ['pipe', 'pipe', 'ignore'] });\n } catch {\n resolve({ ok: false, out: '' });\n return;\n }\n let out = '';\n child.stdout?.on('data', (c: Buffer) => (out += c.toString('utf8')));\n child.on('error', () => resolve({ ok: false, out: '' }));\n child.on('close', (code) => resolve({ ok: code === 0, out }));\n if (input !== undefined) child.stdin?.end(input);\n else child.stdin?.end();\n });\n}\n\n/** OS \uBCC4 \uD074\uB9BD\uBCF4\uB4DC \uB3C4\uC6B0\uBBF8. \uC124\uCE58\uB3FC \uC788\uC9C0 \uC54A\uC73C\uBA74 \uC2E4\uD328\uB85C \uB098\uC624\uACE0, \uB3C4\uAD6C\uAC00 \uADF8\uB807\uAC8C \uB9D0\uD55C\uB2E4. */\nfunction clipboardCommands(): { read: [string, string[]]; write: [string, string[]] } | null {\n const os = platform();\n if (os === 'darwin') return { read: ['pbpaste', []], write: ['pbcopy', []] };\n if (os === 'win32') {\n return {\n read: ['powershell', ['-NoProfile', '-Command', 'Get-Clipboard']],\n write: ['clip', []],\n };\n }\n // \uB9AC\uB205\uC2A4\uB294 \uBC30\uD3EC\uD310\u00B7\uC138\uC158(X11/Wayland)\uC5D0 \uB530\uB77C \uB2E4\uB974\uB2E4. \uC21C\uC11C\uB300\uB85C \uC2DC\uB3C4\uD55C\uB2E4.\n return { read: ['xclip', ['-selection', 'clipboard', '-o']], write: ['xclip', ['-selection', 'clipboard']] };\n}\n\n/**\n * \uC704\uD5D8\uD55C \uBA85\uB839 \uD655\uC778 \u2014 \uD130\uBBF8\uB110 \uD504\uB86C\uD504\uD2B8.\n *\n * **\uBE44\uB300\uD654\uD615\uC774\uBA74 \uBB3B\uC9C0 \uC54A\uACE0 \uAC70\uBD80\uD55C\uB2E4.** \uD30C\uC774\uD504\uC5D0 \uBB3C\uB824 \uB3CC \uB54C \uD504\uB86C\uD504\uD2B8\uB97C \uB744\uC6B0\uBA74\n * \uC544\uBB34\uB3C4 \uBABB \uBCF4\uB294 \uC9C8\uBB38 \uC55E\uC5D0\uC11C \uC601\uC6D0\uD788 \uBA48\uCD98\uB2E4. \uC2A4\uD06C\uB9BD\uD2B8\uC5D0\uC11C \uD544\uC694\uD558\uBA74 \uC124\uC815\uC73C\uB85C\n * \uBBF8\uB9AC \uC2B9\uC778\uD55C\uB2E4(`dex tools configure --allow-dangerous`).\n */\nasync function confirmDangerous(command: string): Promise<'once' | 'session' | 'deny'> {\n if (!stdin.isTTY || !stdout.isTTY) return 'deny';\n const rl = createInterface({ input: stdin, output: stdout });\n try {\n stdout.write(`\\n${DANGEROUS_COMMAND_PROMPT.title}\\n${DANGEROUS_COMMAND_PROMPT.message}\\n`);\n stdout.write(` ${DANGEROUS_COMMAND_PROMPT.detail(command)}\\n`);\n const answer = await new Promise<string>((resolve) =>\n rl.question('\uD5C8\uC6A9\uD558\uC2DC\uACA0\uC2B5\uB2C8\uAE4C? [n=\uAC70\uBD80 / y=\uC774\uBC88\uB9CC / a=\uC774 \uC138\uC158 \uB3D9\uC548] ', resolve),\n );\n const a = answer.trim().toLowerCase();\n // \uAE30\uBCF8\uAC12\uC740 \uAC70\uBD80\uB2E4 \u2014 \uC5D4\uD130\uB9CC \uCE5C \uC0AC\uC6A9\uC790\uAC00 \uC2B9\uC778\uD55C \uAC83\uC774 \uB418\uBA74 \uC548 \uB41C\uB2E4.\n return a === 'a' ? 'session' : a === 'y' ? 'once' : 'deny';\n } finally {\n rl.close();\n }\n}\n\nconst clip = clipboardCommands();\n\nconst terminalInteraction: InteractionPort = {\n confirmDangerous,\n clipboard: clip\n ? {\n async read() {\n const r = await run(clip.read[0], clip.read[1]);\n if (!r.ok) throw new Error(`\uD074\uB9BD\uBCF4\uB4DC\uB97C \uC77D\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4 (${clip.read[0]} \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4).`);\n return r.out;\n },\n async write(text) {\n const r = await run(clip.write[0], clip.write[1], text);\n if (!r.ok) throw new Error(`\uD074\uB9BD\uBCF4\uB4DC\uC5D0 \uC4F0\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4 (${clip.write[0]} \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4).`);\n },\n }\n : undefined,\n /**\n * \uD130\uBBF8\uB110\uC5D0\uB294 \uC54C\uB9BC \uC13C\uD130\uAC00 \uC5C6\uB2E4. OS \uB3C4\uC6B0\uBBF8\uAC00 \uC788\uC73C\uBA74 \uADF8\uAC83\uC73C\uB85C, \uC5C6\uC73C\uBA74 **\uD45C\uC900\n * \uC624\uB958\uC5D0 \uD55C \uC904** \uC4F4\uB2E4 \u2014 \uC0AC\uC6A9\uC790\uAC00 \uBCF4\uACE0 \uC788\uB294 \uACF3\uC774 \uAC70\uAE30\uB2E4. \uC870\uC6A9\uD788 \uC131\uACF5\uD55C \uCC99\uD558\uC9C0\n * \uC54A\uB294 \uAC83\uC774 \uADDC\uCE59\uC774\uC9C0\uB9CC, \uC5EC\uAE30\uC11C\uB294 \uC2E4\uC81C\uB85C \uC0AC\uB78C\uC5D0\uAC8C \uB2FF\uB294\uB2E4.\n */\n async notify(title, body) {\n const os = platform();\n if (os === 'darwin') {\n const script = `display notification ${JSON.stringify(body)} with title ${JSON.stringify(title)}`;\n const r = await run('osascript', ['-e', script]);\n if (r.ok) return true;\n } else if (os === 'linux') {\n const r = await run('notify-send', [title, body]);\n if (r.ok) return true;\n }\n process.stderr.write(`\\n[\uC54C\uB9BC] ${title}${body ? `: ${body}` : ''}\\n`);\n return true;\n },\n async openExternal(url) {\n const { file, args } = openerInvocation(url);\n const r = await run(file, args);\n if (!r.ok) throw new Error(`\uC5F4\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4 (${file} \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4).`);\n },\n async openPath(absolutePath) {\n const { file, args } = openerInvocation(absolutePath);\n const r = await run(file, args);\n return r.ok ? '' : `\uC5F4\uC9C0 \uBABB\uD588\uC2B5\uB2C8\uB2E4 (${file} \uAC00 \uD544\uC694\uD569\uB2C8\uB2E4).`;\n },\n};\n\n/**\n * \uC5D4\uC9C4\uC5D0 \uC774 \uD638\uC2A4\uD2B8\uB97C \uBD99\uC778\uB2E4. **\uC5D4\uC9C4\uC744 \uAC74\uB4DC\uB9AC\uAE30 \uC804\uC5D0 \uD55C \uBC88** \uBD80\uB978\uB2E4 \u2014 \uBD99\uC9C0 \uC54A\uC740\n * \uCC44\uB85C \uC4F0\uBA74 \uC5D4\uC9C4\uC774 \uBA85\uD655\uD788 \uB358\uC9C4\uB2E4(\uC870\uC6A9\uD788 \uBA54\uBAA8\uB9AC\uB85C \uD3F4\uBC31\uD558\uC9C0 \uC54A\uB294\uB2E4).\n */\nexport function bindCliHost(configStore: FileConfigStore): void {\n // \uC5D4\uC9C4\uC758 ConfigPort \uB294 \uB3D9\uAE30 load/save \uB2E4(\uC124\uC815\uC744 \uC77D\uB294 \uC790\uB9AC\uB9C8\uB2E4 await \uAC00 \uBD99\uC73C\uBA74\n // \uD638\uCD9C\uBD80\uAC00 \uC9C0\uC800\uBD84\uD574\uC9C4\uB2E4). CLI \uC758 \uD30C\uC77C \uC800\uC7A5\uC18C\uB294 \uBE44\uB3D9\uAE30\uB77C \uB9C8\uC9C0\uB9C9\uC73C\uB85C \uC77D\uC740 \uAC12\uC744\n // \uB4E4\uACE0 \uC788\uB2E4\uAC00 \uB3D9\uAE30\uB85C \uB2F5\uD55C\uB2E4 \u2014 \uC124\uC815\uC740 \uD504\uB85C\uC138\uC2A4 \uC2DC\uC791\uC5D0 \uD55C \uBC88 \uC77D\uD788\uACE0 \uAC70\uC758 \uC548 \uBCC0\uD55C\uB2E4.\n let cached: DexConfig | null = null;\n void configStore.read().then((c) => (cached = c)).catch(() => undefined);\n\n const ports: HostPorts<DexConfig> = {\n secrets: {\n get: (name) => credentials.getRaw(name),\n set: (name, value) => credentials.setRaw(name, value),\n },\n config: {\n load: () => cached ?? ({} as DexConfig),\n save: (patch) => {\n const next = { ...(cached ?? ({} as DexConfig)), ...patch } as DexConfig;\n cached = next;\n void configStore.write(next).catch(() => undefined);\n return next;\n },\n },\n paths: { dataRoot: () => dataDirectory() },\n interaction: terminalInteraction,\n };\n bindHost(ports);\n}\n\n/** \uBE44\uBC00 \uC800\uC7A5\uC740 \uD504\uB85C\uD30C\uC77C \uC138\uC158\uACFC \uAC19\uC740 \uBC31\uC5D4\uB4DC(keytar \u2192 \uD30C\uC77C \uD3F4\uBC31)\uB97C \uC4F4\uB2E4. */\nconst credentials = new KeytarCredentialStore();\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["stdin", "stdout", "stderr", "createInterface", "createInterface", "createInterface", "stdin", "stdin", "createInterface", "stdout", "stdin", "stderr", "run"]
|
|
7
|
+
}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
# XGEN Connector 기능 이관 가이드
|
|
2
|
+
|
|
3
|
+
이 문서는 `dex-cli`에 기능을 추가할 때 `xgen-connector`의 어떤 파일을 참고해야 하는지 판단하기
|
|
4
|
+
위한 개발 가이드다. 목표는 Connector 소스를 런타임 의존성으로 다시 연결하는 것이 아니라, 필요한
|
|
5
|
+
API 계약과 로직만 `dex-cli` 안으로 이관하는 것이다.
|
|
6
|
+
|
|
7
|
+
기준 디렉터리 구조는 다음과 같다.
|
|
8
|
+
|
|
9
|
+
```text
|
|
10
|
+
xgen-cli/
|
|
11
|
+
├── dex-cli/
|
|
12
|
+
└── xgen-connector/
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
문서에 표시된 Connector 경로는 모두 `dex-cli` 기준 `../xgen-connector/` 아래의 읽기 전용 참고
|
|
16
|
+
경로다. 기존 Electron 앱 파일은 수정하지 않는다.
|
|
17
|
+
|
|
18
|
+
## 이관 원칙
|
|
19
|
+
|
|
20
|
+
1. `xgen-connector/src/core/index.ts` 전체를 가져오지 않는다.
|
|
21
|
+
2. 기능이 사용하는 endpoint, 응답 매핑, 타입만 `dex-cli/src/xgen/`에 옮긴다.
|
|
22
|
+
3. `xgen-connector`를 가리키는 상대 import를 만들지 않는다.
|
|
23
|
+
4. Electron API가 필요한 부분은 VS Code API 또는 Node.js 구현으로 교체한다.
|
|
24
|
+
5. transport, engine, RPC, UI를 서로 분리한다.
|
|
25
|
+
6. 이관한 기능에는 mock server 기반 통합 테스트를 추가한다.
|
|
26
|
+
|
|
27
|
+
기능 구현의 기본 흐름은 다음과 같다.
|
|
28
|
+
|
|
29
|
+
```text
|
|
30
|
+
src/xgen/<feature>.ts XGEN HTTP/SSE/WebSocket 계약
|
|
31
|
+
↓
|
|
32
|
+
src/engine.ts 인증 재시도와 CLI 업무 로직
|
|
33
|
+
↓
|
|
34
|
+
src/rpc-server.ts VS Code용 JSON-RPC
|
|
35
|
+
↓
|
|
36
|
+
src/cli.ts / src/tui/ CLI와 TUI
|
|
37
|
+
↓
|
|
38
|
+
vscode-extension/ VS Code 화면
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## 현재 CLI에 이관된 기반 코드
|
|
42
|
+
|
|
43
|
+
| 기능 | CLI 파일 | 원래 참고한 Connector 파일 |
|
|
44
|
+
|---|---|---|
|
|
45
|
+
| 공통 HTTP와 `ApiError` | `src/xgen/client.ts` | `src/core/client.ts` |
|
|
46
|
+
| 로그인·검증·토큰 회전 | `src/xgen/auth.ts` | `src/core/auth.ts`, `src/core/hash.ts` |
|
|
47
|
+
| Agent 목록 | `src/xgen/agents.ts` | `src/core/agents.ts` |
|
|
48
|
+
| SSE 채팅 | `src/xgen/chat.ts`, `src/xgen/sse.ts` | `src/core/chat.ts`, `src/core/sse.ts` |
|
|
49
|
+
| 대화 목록·turn | `src/xgen/history.ts` | `src/core/history.ts`, `src/core/browser.ts`의 context 제거 로직 |
|
|
50
|
+
| 공통 타입 | `src/xgen/types.ts` | `src/core/types.ts` 중 사용 타입만 |
|
|
51
|
+
| 조립과 세션 상태 | `src/xgen/index.ts` | `src/core/index.ts`의 `XgenClient` 중 사용 기능만 |
|
|
52
|
+
|
|
53
|
+
현재 `HttpClient`가 제공하는 메서드는 `get`, `post`, `json`, `stream`이다. 새 기능이 필요로 할 때만
|
|
54
|
+
아래 메서드를 Connector의 `src/core/client.ts`에서 이관한다.
|
|
55
|
+
|
|
56
|
+
| 필요한 작업 | 추가할 메서드 |
|
|
57
|
+
|---|---|
|
|
58
|
+
| 설정 수정 | `put`, `patch` |
|
|
59
|
+
| 리소스 삭제 | `del` 또는 `json('DELETE', ...)` |
|
|
60
|
+
| multipart 업로드 | `upload` |
|
|
61
|
+
| 파일 다운로드 | `getBinary` |
|
|
62
|
+
| 음성 등 binary POST | `postBinary` |
|
|
63
|
+
|
|
64
|
+
## 서버 API 기능별 파일 지도
|
|
65
|
+
|
|
66
|
+
### 인증 확장
|
|
67
|
+
|
|
68
|
+
SSO 로그인이나 서버 세션 정책을 추가할 때 참고한다.
|
|
69
|
+
|
|
70
|
+
- `xgen-connector/src/core/auth.ts`
|
|
71
|
+
- `loginWithToken`
|
|
72
|
+
- `sessionConfig`
|
|
73
|
+
- `xgen-connector/src/core/types.ts`
|
|
74
|
+
- `LoginResult`, `CurrentUser`
|
|
75
|
+
- SSO 브라우저 창이 필요하면 `xgen-connector/src/main/sso-window-options.ts`는 동작 참고용으로만
|
|
76
|
+
사용한다. VS Code에서는 `vscode.env.openExternal`과 callback 처리로 다시 구현한다.
|
|
77
|
+
|
|
78
|
+
현재 비밀번호 로그인, validate, refresh, logout은 이미 이관되어 있다.
|
|
79
|
+
|
|
80
|
+
### Agent 상세·실행 데이터
|
|
81
|
+
|
|
82
|
+
Agent 상세 화면, trace, memory, task, job run, forged tool, server workspace를 추가할 때 사용한다.
|
|
83
|
+
|
|
84
|
+
- 핵심 파일: `xgen-connector/src/core/agent-data.ts`
|
|
85
|
+
- 공통 의존: `src/core/client.ts`
|
|
86
|
+
- 포함 기능:
|
|
87
|
+
- `basicInfo`
|
|
88
|
+
- `traceList`, `traceDetail`
|
|
89
|
+
- `memoryList`, `memoryRead`
|
|
90
|
+
- `tasksList`, `taskRuns`, `taskOutput`, `endSession`
|
|
91
|
+
- `toolsList`, `toolGet`
|
|
92
|
+
- `workspaceTree`, `workspaceFile`, `workspaceBinary`, `workspaceUpload`
|
|
93
|
+
|
|
94
|
+
읽기 전용 상세 기능은 대체로 `HttpClient.get/post`만 필요하다. Workspace binary 다운로드에는
|
|
95
|
+
`getBinary`, 업로드에는 `upload`가 추가로 필요하다. `agent-data.ts`의 모든 타입을 한 번에 옮기지
|
|
96
|
+
말고 화면에서 사용하는 타입만 `src/xgen/agent-data.ts`에 함께 둔다.
|
|
97
|
+
|
|
98
|
+
### 채팅 첨부 파일
|
|
99
|
+
|
|
100
|
+
- 채팅 요청 계약: `xgen-connector/src/core/chat.ts`
|
|
101
|
+
- 첨부 타입: `xgen-connector/src/core/types.ts`의 `HistoryAttachment`, `ChatRequest.selectedFiles`
|
|
102
|
+
- 업로드·다운로드: `xgen-connector/src/core/agent-data.ts`
|
|
103
|
+
- 추가 transport: `HttpClient.upload`, 필요 시 `getBinary`
|
|
104
|
+
|
|
105
|
+
VS Code 파일 선택은 Connector renderer를 옮기지 않고 `vscode.window.showOpenDialog`로 구현한다.
|
|
106
|
+
업로드 결과를 `selectedFiles` 형식으로 변환한 뒤 기존 `chat/start` RPC에 전달한다.
|
|
107
|
+
|
|
108
|
+
### 사용자 환경설정과 Avatar
|
|
109
|
+
|
|
110
|
+
- 사용자 설정: `xgen-connector/src/core/preferences.ts`
|
|
111
|
+
- Avatar asset·스토어: `xgen-connector/src/core/avatars.ts`
|
|
112
|
+
- Avatar 타입: 위 두 파일의 `AvatarConfig`, `AvatarDescriptor`, `StoreAvatar`
|
|
113
|
+
- 추가 transport: `put`, `upload`, `DELETE`
|
|
114
|
+
|
|
115
|
+
Avatar 렌더링 엔진 자체는 transport가 아니다. Live2D/Spine을 VS Code에서 보여주려면 Webview용
|
|
116
|
+
renderer와 CSP/resource 처리 설계가 별도로 필요하다.
|
|
117
|
+
|
|
118
|
+
### 음성 STT/TTS
|
|
119
|
+
|
|
120
|
+
- API: `xgen-connector/src/core/voice.ts`
|
|
121
|
+
- 타입: `xgen-connector/src/core/types.ts`의 `SttPref`, `TtsPref`, `VoiceConfig`, `TtsSpeakOptions`
|
|
122
|
+
- 추가 transport: `upload`, `postBinary`
|
|
123
|
+
|
|
124
|
+
`voice.ts`는 서버 통신만 담당한다. 실제 마이크 입력, 권한 요청, 음성 재생은 Connector 코드를
|
|
125
|
+
복사하지 않고 VS Code Webview의 MediaDevices/Audio API 또는 별도 Node helper로 구현한다.
|
|
126
|
+
|
|
127
|
+
### SSH 설정
|
|
128
|
+
|
|
129
|
+
- API와 타입: `xgen-connector/src/core/ssh.ts`
|
|
130
|
+
- 추가 transport: `put`, `del`
|
|
131
|
+
- 주요 기능: 설정 조회, 활성화, 서버 생성·수정·삭제, 연결 테스트
|
|
132
|
+
|
|
133
|
+
SSH 비밀번호와 key 같은 secret을 VS Code state나 Webview에 저장하지 않는다. 서버 API 또는 CLI
|
|
134
|
+
credential store가 소유하도록 한다.
|
|
135
|
+
|
|
136
|
+
### XGEN Teams
|
|
137
|
+
|
|
138
|
+
- 방·메시지·멤버·검색·리액션 HTTP API: `xgen-connector/src/core/teams.ts`
|
|
139
|
+
- Agent context와 공유 메시지 포맷: `xgen-connector/src/core/teams-bridge.ts`
|
|
140
|
+
- Teams 타입: `xgen-connector/src/core/types.ts`의 `Teams*`
|
|
141
|
+
- 실시간 사용자·방 WebSocket: `xgen-connector/src/main/teams-ws.ts`
|
|
142
|
+
- 첨부 파일 로컬 처리 참고: `xgen-connector/src/main/teams-files.ts`
|
|
143
|
+
- 추가 transport: `put`, `patch`, `del`, `upload`, `getBinary`
|
|
144
|
+
|
|
145
|
+
`teams.ts`만 이관하면 목록과 메시지 CRUD까지만 가능하다. 실시간 메시지와 알림까지 구현하려면
|
|
146
|
+
`teams-ws.ts`의 인증 header, heartbeat, reconnect, room socket lifecycle을 Node `ws` 기반으로 별도
|
|
147
|
+
이관해야 한다. Electron IPC 코드는 가져오지 않고 RPC notification으로 VS Code에 전달한다.
|
|
148
|
+
|
|
149
|
+
## 로컬·플랫폼 기능별 파일 지도
|
|
150
|
+
|
|
151
|
+
이 섹션의 파일들은 대부분 `src/core`가 아니라 Electron main process 코드다. 그대로 복사하지 않고
|
|
152
|
+
기능 계약과 안전장치만 참고한다.
|
|
153
|
+
|
|
154
|
+
### 로컬 도구 확장
|
|
155
|
+
|
|
156
|
+
현재 CLI에는 `Shell`, `ReadFile`, `WriteFile`, `ListDir`, `Search`, `Open`이 자체 구현되어 있다.
|
|
157
|
+
|
|
158
|
+
- Connector 전체 참고: `xgen-connector/src/main/local-tools.ts`
|
|
159
|
+
- 도구 bridge 참고: `xgen-connector/src/main/mcp-bridge.ts`
|
|
160
|
+
- CLI 현재 구현:
|
|
161
|
+
- `src/local-tools.ts`
|
|
162
|
+
- `src/local-tool-bridge.ts`
|
|
163
|
+
|
|
164
|
+
추가 도구별 참고 위치:
|
|
165
|
+
|
|
166
|
+
| 추가 도구 | Connector 참고 | CLI 대체 구현 |
|
|
167
|
+
|---|---|---|
|
|
168
|
+
| `ShellJob` | `local-tools.ts`의 background job·`ShellJob` | Node child process registry와 process-tree 종료 |
|
|
169
|
+
| `Clipboard` | `local-tools.ts`의 clipboard schema·handler | OS별 명령 또는 검증된 clipboard package |
|
|
170
|
+
| `Notify` | `local-tools.ts`, `notification-center.ts` | VS Code `showInformationMessage` 또는 OS notifier |
|
|
171
|
+
| Browser tools | `browser-tools.ts` | 별도 browser runtime이 먼저 필요 |
|
|
172
|
+
| Workspace bridge tools | `workspace-bridge-tools.ts` | local workspace sync가 먼저 필요 |
|
|
173
|
+
|
|
174
|
+
Electron의 `clipboard`, `Notification` import는 CLI에 가져오지 않는다.
|
|
175
|
+
|
|
176
|
+
### 외부 MCP 서버
|
|
177
|
+
|
|
178
|
+
로컬 stdio/HTTP/SSE MCP 서버를 CLI가 직접 연결하고 그 도구를 XGEN에 노출할 때 필요하다.
|
|
179
|
+
|
|
180
|
+
- MCP client lifecycle: `xgen-connector/src/main/mcp-manager.ts`
|
|
181
|
+
- XGEN WebSocket catalog/call bridge: `xgen-connector/src/main/mcp-bridge.ts`
|
|
182
|
+
- OAuth: `xgen-connector/src/main/mcp-oauth.ts`
|
|
183
|
+
- secret 저장: `xgen-connector/src/main/mcp-secrets.ts`
|
|
184
|
+
- 실행 로그: `xgen-connector/src/main/mcp-runtime-log.ts`
|
|
185
|
+
- 관리 도구 schema와 delegate: `xgen-connector/src/main/local-tools.ts`의 `Mcp*Server`
|
|
186
|
+
|
|
187
|
+
CLI의 `LocalToolBridge`가 XGEN WebSocket 계약을 이미 구현하므로 `mcp-bridge.ts` 전체를 다시 옮기지
|
|
188
|
+
않는다. MCPManager의 catalog를 기존 bridge catalog와 합치는 방식으로 확장한다. OAuth token은
|
|
189
|
+
CLI credential store 또는 별도 keychain service에 저장한다.
|
|
190
|
+
|
|
191
|
+
### 내장 브라우저와 Browser tools
|
|
192
|
+
|
|
193
|
+
- 순수 타입·URL·context: `xgen-connector/src/core/browser.ts`
|
|
194
|
+
- runtime: `xgen-connector/src/main/browser-runtime.ts`
|
|
195
|
+
- 보안 정책: `xgen-connector/src/main/browser-security.ts`
|
|
196
|
+
- element/region 선택: `xgen-connector/src/main/browser-selection.ts`
|
|
197
|
+
- Agent 도구: `xgen-connector/src/main/browser-tools.ts`
|
|
198
|
+
- history: `xgen-connector/src/main/browser-history.ts`
|
|
199
|
+
- 자동 실행: `xgen-connector/src/main/agent-browser-runner.ts`
|
|
200
|
+
- CDP proxy: `xgen-connector/src/main/cdp-page-proxy.ts`
|
|
201
|
+
|
|
202
|
+
`core/browser.ts`만으로는 브라우저가 실행되지 않는다. Connector runtime은 Electron WebContents/CDP에
|
|
203
|
+
의존하므로 CLI에서는 Playwright/Chromium 같은 별도 엔진을 선택해야 한다. URL scheme 제한,
|
|
204
|
+
사용자 승인, 다운로드 경로 제한은 `browser-security.ts`를 참고해 동일한 수준으로 다시 구현한다.
|
|
205
|
+
|
|
206
|
+
### 알림
|
|
207
|
+
|
|
208
|
+
- 순수 설정·mute/privacy 정책: `xgen-connector/src/core/notifications.ts`
|
|
209
|
+
- Electron 표시와 클릭 routing: `xgen-connector/src/main/notification-center.ts`
|
|
210
|
+
|
|
211
|
+
정책 코드는 이관할 수 있지만 표시 코드는 Electron `Notification`을 사용한다. VS Code 확장에서는
|
|
212
|
+
`showInformationMessage/showWarningMessage`와 command callback으로 대체한다. CLI 단독 실행에서는
|
|
213
|
+
OS별 notifier adapter가 필요하다.
|
|
214
|
+
|
|
215
|
+
### 로컬 Workspace 동기화
|
|
216
|
+
|
|
217
|
+
서버 Agent workspace를 로컬 폴더와 양방향 동기화하는 기능이다. 관련 범위가 넓으므로 파일 하나만
|
|
218
|
+
이관해서는 동작하지 않는다.
|
|
219
|
+
|
|
220
|
+
- 동기화 계약: `xgen-connector/src/main/sync-protocol.ts`
|
|
221
|
+
- 변경 계획·충돌 판단: `xgen-connector/src/main/sync-plan.ts`
|
|
222
|
+
- 원격 transport와 WebSocket: `xgen-connector/src/main/sync-transport.ts`
|
|
223
|
+
- 한 sync pair 실행: `xgen-connector/src/main/local-sync.ts`
|
|
224
|
+
- 여러 Agent lifecycle: `xgen-connector/src/main/local-sync-manager.ts`
|
|
225
|
+
- 로컬 폴더 명명: `xgen-connector/src/main/local-sync-folder.ts`
|
|
226
|
+
- 서버 workspace API: `xgen-connector/src/main/workspace-api.ts`
|
|
227
|
+
- workspace 모델·관리: `workspace.ts`, `workspace-manager.ts`, `workspace-backend.ts`
|
|
228
|
+
- Agent 내부 도구: `xgen-connector/src/main/workspace-bridge-tools.ts`
|
|
229
|
+
|
|
230
|
+
CLI에 추가할 때는 먼저 단방향 pull/push와 dry-run 계획부터 분리해 구현한다. 파일 watcher,
|
|
231
|
+
충돌 정책, 심볼릭 링크, 허용 root, 삭제 전파, reconnect를 모두 테스트하기 전에는 자동 양방향
|
|
232
|
+
동기화를 활성화하지 않는다.
|
|
233
|
+
|
|
234
|
+
### 화면 캡처와 시스템 상태
|
|
235
|
+
|
|
236
|
+
- 화면 캡처: `xgen-connector/src/main/screen-capture.ts`
|
|
237
|
+
- 시스템 메트릭 계약: `xgen-connector/src/core/system-metrics.ts`
|
|
238
|
+
- Electron 프로세스 메트릭 구현: `xgen-connector/src/main/system-metrics.ts`
|
|
239
|
+
|
|
240
|
+
화면 캡처는 Electron API 대신 VS Code/OS 권한과 도구를 새로 선택해야 한다. 시스템 메트릭은 타입만
|
|
241
|
+
재사용하고 Node `os`, `process.resourceUsage`, child process 정보를 기반으로 CLI용 구현을 만든다.
|
|
242
|
+
|
|
243
|
+
## 가져오지 않아야 하는 파일
|
|
244
|
+
|
|
245
|
+
아래 파일은 조립 지점 또는 UI/platform glue이므로 기능 하나를 위해 통째로 복사하지 않는다.
|
|
246
|
+
|
|
247
|
+
- `xgen-connector/src/core/index.ts`: 모든 core 기능을 한꺼번에 import한다.
|
|
248
|
+
- `xgen-connector/src/core/types.ts`: Teams·Voice·Avatar 등 미사용 타입까지 포함한다.
|
|
249
|
+
- `xgen-connector/src/main/index.ts`: Electron lifecycle, IPC, tray, updater를 모두 조립한다.
|
|
250
|
+
- `xgen-connector/src/main/ipc.ts`: Electron renderer-main 계약이다.
|
|
251
|
+
- `xgen-connector/src/preload/*`: VS Code에서는 사용하지 않는다.
|
|
252
|
+
- `xgen-connector/src/renderer/*`: UI를 Webview 구조에 맞게 새로 만든다.
|
|
253
|
+
- `xgen-connector/src/main/keychain.ts`: CLI는 이미 `src/credential-store.ts`를 사용한다.
|
|
254
|
+
|
|
255
|
+
`core/index.ts`에서 class 하나를 가져와야 하는 경우에도 그 class가 import하는 하위 모듈만 직접
|
|
256
|
+
확인하고 최소 단위로 이관한다.
|
|
257
|
+
|
|
258
|
+
## 기능 이관 체크리스트
|
|
259
|
+
|
|
260
|
+
### 1. 범위 확인
|
|
261
|
+
|
|
262
|
+
- 사용자 화면에서 필요한 동작을 먼저 목록화한다.
|
|
263
|
+
- 읽기만 필요한지, 수정·업로드·실시간 연결까지 필요한지 구분한다.
|
|
264
|
+
- Connector에서 endpoint와 wire type을 확인한다.
|
|
265
|
+
|
|
266
|
+
### 2. transport 이관
|
|
267
|
+
|
|
268
|
+
- `src/xgen/<feature>.ts`를 만든다.
|
|
269
|
+
- 필요한 타입만 같은 파일 또는 `src/xgen/types.ts`에 추가한다.
|
|
270
|
+
- 필요한 `HttpClient` 메서드만 확장한다.
|
|
271
|
+
- `src/xgen/index.ts`의 `XgenClient`에 feature API를 등록한다.
|
|
272
|
+
|
|
273
|
+
### 3. engine과 RPC
|
|
274
|
+
|
|
275
|
+
- `DexEngine`에 인증 재시도를 포함한 메서드를 추가한다.
|
|
276
|
+
- `rpc-server.ts`에 request method를 추가한다.
|
|
277
|
+
- 장기 실행·실시간 기능은 notification과 명시적 start/stop lifecycle을 제공한다.
|
|
278
|
+
- 비밀번호, token, API key, 파일 내용이 로그로 출력되지 않게 한다.
|
|
279
|
+
|
|
280
|
+
### 4. UI
|
|
281
|
+
|
|
282
|
+
- CLI 명령과 `--json` 출력을 먼저 만든다.
|
|
283
|
+
- TUI와 VS Code는 동일한 engine/RPC를 사용한다.
|
|
284
|
+
- Electron IPC 또는 renderer component를 복사하지 않는다.
|
|
285
|
+
|
|
286
|
+
### 5. 검증
|
|
287
|
+
|
|
288
|
+
- mock XGEN server로 endpoint, 인증 header, snake_case 매핑을 테스트한다.
|
|
289
|
+
- 401/403 token refresh 재시도를 테스트한다.
|
|
290
|
+
- WebSocket 기능은 reconnect, heartbeat, 중복 연결, shutdown을 테스트한다.
|
|
291
|
+
- upload/download 기능은 크기 제한과 허용 경로를 테스트한다.
|
|
292
|
+
- `rg 'xgen-connector|\.\./\.\./xgen-connector' src test` 결과가 비어 있어야 한다.
|
|
293
|
+
- esbuild metafile의 모든 로컬 input이 `dex-cli` 안에 있는지 확인한다.
|
|
294
|
+
- `npm run verify:all`과 `npm pack --dry-run`을 통과시킨다.
|
|
295
|
+
|
|
296
|
+
## 장기 공유가 필요한 경우
|
|
297
|
+
|
|
298
|
+
같은 API를 Electron 앱과 CLI 양쪽에서 반복적으로 수정하게 되면 복사 이관 대신 독립
|
|
299
|
+
`@xgen/core` 패키지를 만드는 것이 적합하다. 그때도 package에는 HTTP/SSE 타입과 매핑만 넣고,
|
|
300
|
+
Electron·VS Code·Node별 UI와 OS 기능은 각 host에 남겨야 한다.
|