@yr-kits/dev-copilot 0.1.0 → 0.1.1
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/dist/bin/bridge.mjs +57 -22
- package/dist/bin/bridge.mjs.map +1 -1
- package/dist/index.mjs +45 -31
- package/dist/index.mjs.map +1 -1
- package/package.json +8 -6
package/dist/bin/bridge.mjs
CHANGED
|
@@ -83,10 +83,27 @@ const buildAgentPrompt = (request) => {
|
|
|
83
83
|
//#endregion
|
|
84
84
|
//#region src/bridge/internal/agents/claude-adapter.ts
|
|
85
85
|
const execFileAsync$3 = promisify(execFile);
|
|
86
|
-
const
|
|
86
|
+
const AUTH_ERROR_PATTERN = /401|authentication|invalid authentication credentials|please run \/login|claude\s*\/login|로그인/i;
|
|
87
|
+
const extractErrorDetails = (error) => {
|
|
88
|
+
const unknownRecord = error && typeof error === "object" ? error : null;
|
|
87
89
|
const message = error instanceof Error ? error.message : String(error);
|
|
88
|
-
|
|
89
|
-
|
|
90
|
+
const stderr = typeof unknownRecord?.stderr === "string" ? unknownRecord.stderr : "";
|
|
91
|
+
const stdout = typeof unknownRecord?.stdout === "string" ? unknownRecord.stdout : "";
|
|
92
|
+
return {
|
|
93
|
+
message,
|
|
94
|
+
stderr,
|
|
95
|
+
stdout,
|
|
96
|
+
merged: [
|
|
97
|
+
message,
|
|
98
|
+
stderr,
|
|
99
|
+
stdout
|
|
100
|
+
].map((part) => part.trim()).filter(Boolean).join("\n")
|
|
101
|
+
};
|
|
102
|
+
};
|
|
103
|
+
const toClaudeErrorMessage = (error) => {
|
|
104
|
+
const { message, merged } = extractErrorDetails(error);
|
|
105
|
+
if (AUTH_ERROR_PATTERN.test(merged)) return "Claude Code 로그인이 필요합니다. 터미널에서 `claude /login`을 실행해 주세요.";
|
|
106
|
+
if (/ENOENT/.test(merged)) return "Claude CLI를 찾을 수 없습니다. Claude Code CLI 설치 상태를 확인해 주세요.";
|
|
90
107
|
return message;
|
|
91
108
|
};
|
|
92
109
|
const findFirstString = (value) => {
|
|
@@ -176,7 +193,7 @@ const claudeAdapter = {
|
|
|
176
193
|
"-p",
|
|
177
194
|
"--output-format",
|
|
178
195
|
"json",
|
|
179
|
-
"OK
|
|
196
|
+
"OK"
|
|
180
197
|
], {
|
|
181
198
|
cwd,
|
|
182
199
|
timeout: 15e3,
|
|
@@ -189,14 +206,14 @@ const claudeAdapter = {
|
|
|
189
206
|
message: "Claude Code CLI에 로그인되어 있습니다."
|
|
190
207
|
};
|
|
191
208
|
} catch (error) {
|
|
192
|
-
const
|
|
193
|
-
const unavailable = /ENOENT/.test(
|
|
194
|
-
const authError =
|
|
209
|
+
const { merged, message } = extractErrorDetails(error);
|
|
210
|
+
const unavailable = /ENOENT/.test(merged);
|
|
211
|
+
const authError = AUTH_ERROR_PATTERN.test(merged);
|
|
195
212
|
return {
|
|
196
213
|
available: !unavailable,
|
|
197
214
|
authenticated: false,
|
|
198
215
|
agent: "claude",
|
|
199
|
-
message: unavailable ? "Claude CLI를 찾을 수 없습니다." : authError ? "Claude Code 로그인이 필요합니다." :
|
|
216
|
+
message: unavailable ? "Claude CLI를 찾을 수 없습니다." : authError ? "Claude Code 로그인이 필요합니다." : "Claude Code 상태 확인에 실패했습니다. 터미널에서 `claude /login` 실행 후 다시 시도해 주세요.",
|
|
200
217
|
loginCommand: unavailable ? void 0 : "claude /login"
|
|
201
218
|
};
|
|
202
219
|
}
|
|
@@ -206,11 +223,24 @@ const claudeAdapter = {
|
|
|
206
223
|
//#endregion
|
|
207
224
|
//#region src/bridge/internal/agents/codex-adapter.ts
|
|
208
225
|
const execFileAsync$2 = promisify(execFile);
|
|
226
|
+
const codexBridgeConfigArgs = [
|
|
227
|
+
"-c",
|
|
228
|
+
"mcp_servers.notion.enabled=false",
|
|
229
|
+
"-c",
|
|
230
|
+
"mcp_servers.figma.enabled=false",
|
|
231
|
+
"-c",
|
|
232
|
+
"mcp_servers.linear.enabled=false",
|
|
233
|
+
"-c",
|
|
234
|
+
"mcp_servers.context7.enabled=false",
|
|
235
|
+
"-c",
|
|
236
|
+
"mcp_servers.playwright.enabled=false",
|
|
237
|
+
"-c",
|
|
238
|
+
"mcp_servers.zeplin.enabled=false"
|
|
239
|
+
];
|
|
209
240
|
const codexDiffResponseSchema = {
|
|
210
241
|
type: "object",
|
|
211
242
|
properties: {
|
|
212
243
|
message: { type: "string" },
|
|
213
|
-
patchPreview: { type: "string" },
|
|
214
244
|
warnings: {
|
|
215
245
|
type: "array",
|
|
216
246
|
items: { type: "string" }
|
|
@@ -233,7 +263,11 @@ const codexDiffResponseSchema = {
|
|
|
233
263
|
}
|
|
234
264
|
}
|
|
235
265
|
},
|
|
236
|
-
required: [
|
|
266
|
+
required: [
|
|
267
|
+
"message",
|
|
268
|
+
"warnings",
|
|
269
|
+
"changes"
|
|
270
|
+
],
|
|
237
271
|
additionalProperties: false
|
|
238
272
|
};
|
|
239
273
|
const parseAnswerResponse = (rawOutput) => {
|
|
@@ -252,6 +286,11 @@ const parseEditResponse = async (outputPath) => {
|
|
|
252
286
|
warnings: parsed.warnings ?? []
|
|
253
287
|
};
|
|
254
288
|
};
|
|
289
|
+
const isCodexAuthenticatedFromStatus = (statusOutput) => {
|
|
290
|
+
const normalized = statusOutput.toLowerCase();
|
|
291
|
+
if (/not logged in|login required|로그인 필요/.test(normalized)) return false;
|
|
292
|
+
return /logged in|로그인됨|로그인되어/.test(normalized);
|
|
293
|
+
};
|
|
255
294
|
const toCodexErrorMessage = (error) => {
|
|
256
295
|
const message = error instanceof Error ? error.message : String(error);
|
|
257
296
|
if (/not logged in|login required|authentication|unauthorized/i.test(message)) return "Codex CLI 로그인이 필요합니다. 터미널에서 `codex login`을 실행해 주세요.";
|
|
@@ -262,6 +301,7 @@ const getCodexModelName = async (cwd) => {
|
|
|
262
301
|
const outputPath = path.join(os.tmpdir(), `dev-copilot-codex-status-${Date.now()}-${Math.random().toString(16).slice(2)}.txt`);
|
|
263
302
|
try {
|
|
264
303
|
const { stdout, stderr } = await execFileAsync$2("codex", [
|
|
304
|
+
...codexBridgeConfigArgs,
|
|
265
305
|
"exec",
|
|
266
306
|
"--cd",
|
|
267
307
|
cwd,
|
|
@@ -295,6 +335,7 @@ const codexAdapter = {
|
|
|
295
335
|
await promises.writeFile(schemaPath, JSON.stringify(codexDiffResponseSchema), "utf-8");
|
|
296
336
|
const prompt = buildAgentPrompt(request);
|
|
297
337
|
const args = [
|
|
338
|
+
...codexBridgeConfigArgs,
|
|
298
339
|
"exec",
|
|
299
340
|
"--cd",
|
|
300
341
|
request.cwd,
|
|
@@ -337,7 +378,7 @@ const codexAdapter = {
|
|
|
337
378
|
maxBuffer: 1024 * 128
|
|
338
379
|
});
|
|
339
380
|
const output = `${stdout}\n${stderr}`.trim();
|
|
340
|
-
const authenticated =
|
|
381
|
+
const authenticated = isCodexAuthenticatedFromStatus(output);
|
|
341
382
|
const model = authenticated ? await getCodexModelName(cwd) : void 0;
|
|
342
383
|
return {
|
|
343
384
|
available: true,
|
|
@@ -557,11 +598,6 @@ const getChangedFiles = (patchPreview) => {
|
|
|
557
598
|
}
|
|
558
599
|
return [...changedFiles];
|
|
559
600
|
};
|
|
560
|
-
const normalizeUnifiedPatch = (patchPreview) => {
|
|
561
|
-
const withoutFence = patchPreview.replace(/^```(?:diff|patch)?\s*/i, "").replace(/\s*```$/i, "").trim();
|
|
562
|
-
const diffStartIndex = withoutFence.indexOf("diff --git ");
|
|
563
|
-
return diffStartIndex >= 0 ? withoutFence.slice(diffStartIndex).trim() : withoutFence;
|
|
564
|
-
};
|
|
565
601
|
const validatePatchPaths = (patchPreview, validatePath) => {
|
|
566
602
|
const changedFiles = getChangedFiles(patchPreview);
|
|
567
603
|
if (!changedFiles.length) throw new Error("패치에서 변경 파일을 찾을 수 없습니다.");
|
|
@@ -570,7 +606,7 @@ const validatePatchPaths = (patchPreview, validatePath) => {
|
|
|
570
606
|
};
|
|
571
607
|
const checkUnifiedPatch = async (patchPreview, cwd) => {
|
|
572
608
|
const tempFilePath = path.join(os.tmpdir(), `dev-copilot-check-${Date.now()}-${Math.random().toString(16).slice(2)}.patch`);
|
|
573
|
-
await promises.writeFile(tempFilePath,
|
|
609
|
+
await promises.writeFile(tempFilePath, patchPreview, "utf-8");
|
|
574
610
|
try {
|
|
575
611
|
await execFileAsync("git", [
|
|
576
612
|
"apply",
|
|
@@ -607,7 +643,7 @@ const createGitPatch = async (filePath, oldContent, newContent) => {
|
|
|
607
643
|
if (typedError.code === 1 && typedError.stdout) return { stdout: typedError.stdout };
|
|
608
644
|
throw error;
|
|
609
645
|
});
|
|
610
|
-
return stdout.replaceAll(`a/old/${filePath}`, `a/${filePath}`).replaceAll(`b/new/${filePath}`, `b/${filePath}`)
|
|
646
|
+
return stdout.replaceAll(`a/old/${filePath}`, `a/${filePath}`).replaceAll(`b/new/${filePath}`, `b/${filePath}`);
|
|
611
647
|
} finally {
|
|
612
648
|
await promises.rm(tempDir, {
|
|
613
649
|
force: true,
|
|
@@ -635,13 +671,13 @@ const createPatchFromTextReplacements = async (replacements, cwd, validatePath)
|
|
|
635
671
|
if (newContent === oldContent) continue;
|
|
636
672
|
patches.push(await createGitPatch(filePath, oldContent, newContent));
|
|
637
673
|
}
|
|
638
|
-
const patchPreview = patches.join("\n")
|
|
639
|
-
if (!patchPreview) throw new Error("변경할 내용이 없습니다.");
|
|
674
|
+
const patchPreview = patches.join("\n");
|
|
675
|
+
if (!patchPreview.trim()) throw new Error("변경할 내용이 없습니다.");
|
|
640
676
|
return patchPreview;
|
|
641
677
|
};
|
|
642
678
|
const applyUnifiedPatch = async (patchPreview, cwd, actor) => {
|
|
643
679
|
const tempFilePath = path.join(os.tmpdir(), `dev-copilot-${Date.now()}-${Math.random().toString(16).slice(2)}.patch`);
|
|
644
|
-
await promises.writeFile(tempFilePath,
|
|
680
|
+
await promises.writeFile(tempFilePath, patchPreview, "utf-8");
|
|
645
681
|
try {
|
|
646
682
|
await execFileAsync("git", [
|
|
647
683
|
"apply",
|
|
@@ -787,7 +823,6 @@ const createDevCopilotBridgeServerWithDependencies = (config, dependencies) => {
|
|
|
787
823
|
let patchPreview = "";
|
|
788
824
|
try {
|
|
789
825
|
if (parsed.changes?.length) patchPreview = await createPatchFromTextReplacements(parsed.changes, config.rootDir, (filePath) => resolveAndValidatePath(filePath, effectiveAllowedPaths, config.rootDir));
|
|
790
|
-
else if (parsed.patchPreview) patchPreview = normalizeUnifiedPatch(parsed.patchPreview);
|
|
791
826
|
} catch (error) {
|
|
792
827
|
sendJson(response, config, 200, {
|
|
793
828
|
message: parsed.message ?? "에이전트가 수정안을 만들었지만 실제 파일 내용과 매칭하지 못했습니다.",
|
package/dist/bin/bridge.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge.mjs","names":["execFileAsync","execFileAsync","fs","execFileAsync","fs","fs"],"sources":["../../src/bridge/lib/config.ts","../../src/bridge/lib/guards.ts","../../src/bridge/internal/prompts.ts","../../src/bridge/internal/agents/claude-adapter.ts","../../src/bridge/internal/agents/codex-adapter.ts","../../src/bridge/internal/agents/index.ts","../../src/bridge/internal/project-context.ts","../../src/bridge/lib/patch.ts","../../src/bridge/lib/store.ts","../../src/bridge/server/http-server.ts","../../src/bridge/cli/run-http.ts","../../src/bin/bridge.ts"],"sourcesContent":["import type { CopilotAgent } from \"../types\";\n\nexport interface DevCopilotBridgeConfig {\n rootDir: string;\n host: string;\n port: number;\n corsOrigin: string;\n agent: CopilotAgent;\n allowedPaths: string[];\n}\n\nconst DEFAULT_ALLOWED_PATHS = [\n \"app\",\n \"src\",\n \"widgets\",\n \"features\",\n \"entities\",\n \"shared\",\n \"components\",\n];\n\nexport const createDevCopilotBridgeConfig = (\n config: Partial<DevCopilotBridgeConfig> & { rootDir: string },\n): DevCopilotBridgeConfig => {\n return {\n rootDir: config.rootDir,\n host: config.host ?? \"127.0.0.1\",\n port: config.port ?? 3339,\n corsOrigin: config.corsOrigin ?? \"*\",\n agent: config.agent ?? \"codex\",\n allowedPaths: config.allowedPaths ?? DEFAULT_ALLOWED_PATHS,\n };\n};\n","import path from \"node:path\";\n\nimport type { DevCopilotBridgeConfig } from \"./config\";\n\nexport const resolveAndValidatePath = (\n filePath: string,\n allowedPaths: DevCopilotBridgeConfig[\"allowedPaths\"],\n rootDir?: string,\n) => {\n if (!filePath) {\n throw new Error(\"허용되지 않은 파일 경로입니다.\");\n }\n\n let normalizedInput = filePath.replaceAll(\"\\\\\", \"/\");\n\n if (rootDir) {\n const normalizedRootDir = rootDir.replaceAll(\"\\\\\", \"/\").replace(/\\/+$/, \"\");\n\n if (normalizedInput.startsWith(`${normalizedRootDir}/`)) {\n normalizedInput = normalizedInput.slice(normalizedRootDir.length + 1);\n }\n }\n\n if (\n path.isAbsolute(normalizedInput) ||\n normalizedInput.includes(\"..\")\n ) {\n throw new Error(\"허용되지 않은 파일 경로입니다.\");\n }\n\n const normalized = normalizedInput;\n const firstSegment = normalized.split(\"/\")[0];\n const allowAll = allowedPaths.includes(\".\") || allowedPaths.includes(\"*\");\n\n if (!allowAll && !allowedPaths.includes(firstSegment)) {\n throw new Error(`허용 경로 외 파일은 수정할 수 없습니다: ${firstSegment}`);\n }\n\n return normalized;\n};\n","import type { CopilotMode } from \"../types\";\n\nexport interface AgentPromptRequest {\n selectedText: string;\n prompt: string;\n mode: CopilotMode;\n route?: string;\n fileHints?: string[];\n projectContext?: string;\n previousResponse?: string;\n}\n\nexport const buildAgentPrompt = (request: AgentPromptRequest) => {\n if (request.mode === \"answer\") {\n return [\n \"Answer the user's web overlay request.\",\n \"Do not modify files.\",\n \"Use the provided project context when it is relevant.\",\n \"Respond in Korean.\",\n \"Keep the answer concise and actionable.\",\n \"\",\n `Current route: ${request.route ?? \"unknown\"}`,\n `Selected text:\\n${request.selectedText || \"(none)\"}`,\n `User prompt:\\n${request.prompt}`,\n `Previous AI response:\\n${request.previousResponse ?? \"(none)\"}`,\n `Project context:\\n${request.projectContext ?? \"(none)\"}`,\n ].join(\"\\n\");\n }\n\n return [\n \"You are a local code-edit proposal generator called from a web overlay.\",\n \"Do not modify files directly.\",\n \"Return JSON only.\",\n \"Do not generate unified diff directly.\",\n \"Put repository-relative file path(path), exact old text(oldText), and replacement text(newText) into the changes array.\",\n \"Never use absolute paths. The path must be relative to the repository root, for example src/features/article/model/data.ts.\",\n \"oldText must exactly match the file content and must not use ellipsis.\",\n \"Ground the proposal in the selected text, user prompt, and provided project context.\",\n \"Write the message field in Korean.\",\n \"\",\n `Current route: ${request.route ?? \"unknown\"}`,\n `Allowed path hints: ${(request.fileHints ?? []).join(\", \") || \"(none)\"}`,\n `Selected text:\\n${request.selectedText || \"(none)\"}`,\n `User prompt:\\n${request.prompt}`,\n `Previous AI response:\\n${request.previousResponse ?? \"(none)\"}`,\n `Project context:\\n${request.projectContext ?? \"(none)\"}`,\n ].join(\"\\n\");\n};\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nimport { buildAgentPrompt } from \"../prompts\";\nimport type {\n AgentAdapter,\n AgentBridgeRequest,\n AgentBridgeResponse,\n AgentStatus,\n} from \"./types\";\n\nconst execFileAsync = promisify(execFile);\n\ntype ClaudePrintJsonResponse = {\n result?: unknown;\n message?: unknown;\n output?: unknown;\n content?: unknown;\n};\n\nconst toClaudeErrorMessage = (error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n\n if (/401|authentication|invalid authentication credentials|please run \\/login/i.test(message)) {\n return \"Claude Code 로그인이 필요합니다. 터미널에서 `claude /login`을 실행해 주세요.\";\n }\n\n if (/ENOENT/.test(message)) {\n return \"Claude CLI를 찾을 수 없습니다. Claude Code CLI 설치 상태를 확인해 주세요.\";\n }\n\n return message;\n};\n\nconst findFirstString = (value: unknown): string | null => {\n if (typeof value === \"string\") {\n const text = value.trim();\n return text ? text : null;\n }\n\n if (Array.isArray(value)) {\n for (const item of value) {\n const found = findFirstString(item);\n if (found) {\n return found;\n }\n }\n return null;\n }\n\n if (value && typeof value === \"object\") {\n const record = value as Record<string, unknown>;\n const preferredKeys = [\"result\", \"message\", \"output\", \"text\", \"content\"];\n\n for (const key of preferredKeys) {\n if (key in record) {\n const found = findFirstString(record[key]);\n if (found) {\n return found;\n }\n }\n }\n\n for (const nestedValue of Object.values(record)) {\n const found = findFirstString(nestedValue);\n if (found) {\n return found;\n }\n }\n }\n\n return null;\n};\n\nconst parseClaudeJsonOutput = (raw: string) => {\n const parsed = JSON.parse(raw) as ClaudePrintJsonResponse;\n const text = findFirstString(parsed) ?? \"\";\n\n return {\n parsed,\n text,\n };\n};\n\nconst parseClaudeEditResponse = (raw: string): AgentBridgeResponse => {\n const { text } = parseClaudeJsonOutput(raw);\n const parsed = JSON.parse(text) as AgentBridgeResponse;\n\n return {\n message: parsed.message,\n patchPreview: parsed.patchPreview,\n changes: parsed.changes,\n warnings: parsed.warnings ?? [],\n };\n};\n\nexport const claudeAdapter: AgentAdapter = {\n agent: \"claude\",\n async run(request: AgentBridgeRequest): Promise<AgentBridgeResponse> {\n const prompt = buildAgentPrompt(request);\n const args = [\n \"-p\",\n \"--output-format\",\n \"json\",\n ...(request.mode === \"edit\"\n ? [\n \"--json-schema\",\n '{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\"},\"patchPreview\":{\"type\":\"string\"},\"warnings\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"changes\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"path\":{\"type\":\"string\"},\"oldText\":{\"type\":\"string\"},\"newText\":{\"type\":\"string\"}},\"required\":[\"path\",\"oldText\",\"newText\"],\"additionalProperties\":false}}},\"required\":[\"message\"],\"additionalProperties\":false}',\n ]\n : []),\n prompt,\n ];\n\n try {\n const { stdout } = await execFileAsync(\"claude\", args, {\n cwd: request.cwd,\n maxBuffer: 1024 * 1024 * 5,\n timeout: Number(process.env.DEV_COPILOT_AGENT_TIMEOUT_MS ?? 120_000),\n env: {\n ...process.env,\n NO_COLOR: \"1\",\n },\n });\n\n if (request.mode === \"answer\") {\n const { text } = parseClaudeJsonOutput(stdout);\n\n return {\n message: text,\n warnings: [],\n };\n }\n\n return parseClaudeEditResponse(stdout);\n } catch (error) {\n throw new Error(toClaudeErrorMessage(error));\n }\n },\n async getStatus(cwd: string): Promise<AgentStatus> {\n try {\n await execFileAsync(\"claude\", [\"-p\", \"--output-format\", \"json\", \"OK만 출력해줘.\"], {\n cwd,\n timeout: 15_000,\n maxBuffer: 1024 * 512,\n });\n\n return {\n available: true,\n authenticated: true,\n agent: \"claude\",\n message: \"Claude Code CLI에 로그인되어 있습니다.\",\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n const unavailable = /ENOENT/.test(message);\n const authError = /401|authentication|please run \\/login|invalid authentication credentials/i.test(\n message,\n );\n\n return {\n available: !unavailable,\n authenticated: false,\n agent: \"claude\",\n message: unavailable\n ? \"Claude CLI를 찾을 수 없습니다.\"\n : authError\n ? \"Claude Code 로그인이 필요합니다.\"\n : message,\n loginCommand: unavailable ? undefined : \"claude /login\",\n };\n }\n },\n};\n\nexport const __internal = {\n findFirstString,\n parseClaudeJsonOutput,\n parseClaudeEditResponse,\n toClaudeErrorMessage,\n};\n","import { execFile } from \"node:child_process\";\nimport { promises as fs } from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\n\nimport { buildAgentPrompt } from \"../prompts\";\nimport type {\n AgentAdapter,\n AgentBridgeRequest,\n AgentBridgeResponse,\n AgentStatus,\n} from \"./types\";\n\nconst execFileAsync = promisify(execFile);\nconst codexDiffResponseSchema = {\n type: \"object\",\n properties: {\n message: { type: \"string\" },\n patchPreview: { type: \"string\" },\n warnings: {\n type: \"array\",\n items: { type: \"string\" },\n },\n changes: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n path: { type: \"string\" },\n oldText: { type: \"string\" },\n newText: { type: \"string\" },\n },\n required: [\"path\", \"oldText\", \"newText\"],\n additionalProperties: false,\n },\n },\n },\n required: [\"message\"],\n additionalProperties: false,\n} as const;\n\nconst parseAnswerResponse = (rawOutput: string): AgentBridgeResponse => {\n return {\n message: rawOutput.trim(),\n warnings: [],\n };\n};\n\nconst parseEditResponse = async (outputPath: string) => {\n const content = await fs.readFile(outputPath, \"utf-8\");\n const parsed = JSON.parse(content) as AgentBridgeResponse;\n\n return {\n message: parsed.message,\n patchPreview: parsed.patchPreview,\n changes: parsed.changes,\n warnings: parsed.warnings ?? [],\n };\n};\n\nconst toCodexErrorMessage = (error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n\n if (/not logged in|login required|authentication|unauthorized/i.test(message)) {\n return \"Codex CLI 로그인이 필요합니다. 터미널에서 `codex login`을 실행해 주세요.\";\n }\n\n if (/ENOENT/.test(message)) {\n return \"Codex CLI를 찾을 수 없습니다. Codex CLI 설치 상태를 확인해 주세요.\";\n }\n\n return message;\n};\n\nconst getCodexModelName = async (cwd: string) => {\n const outputPath = path.join(\n os.tmpdir(),\n `dev-copilot-codex-status-${Date.now()}-${Math.random().toString(16).slice(2)}.txt`,\n );\n\n try {\n const { stdout, stderr } = await execFileAsync(\n \"codex\",\n [\n \"exec\",\n \"--cd\",\n cwd,\n \"--sandbox\",\n \"read-only\",\n \"--skip-git-repo-check\",\n \"--output-last-message\",\n outputPath,\n \"OK만 출력해줘.\",\n ],\n {\n cwd,\n timeout: 30_000,\n maxBuffer: 1024 * 512,\n env: {\n ...process.env,\n NO_COLOR: \"1\",\n },\n },\n );\n const output = `${stdout}\\n${stderr}`;\n const match = output.match(/^model:\\s*(.+)$/m);\n\n return match?.[1]?.trim();\n } catch {\n return undefined;\n } finally {\n await fs.rm(outputPath, { force: true });\n }\n};\n\nexport const codexAdapter: AgentAdapter = {\n agent: \"codex\",\n async run(request: AgentBridgeRequest): Promise<AgentBridgeResponse> {\n const outputPath = path.join(\n os.tmpdir(),\n `dev-copilot-codex-${Date.now()}-${Math.random().toString(16).slice(2)}.json`,\n );\n const schemaPath = path.join(\n os.tmpdir(),\n `dev-copilot-codex-schema-${Date.now()}-${Math.random().toString(16).slice(2)}.json`,\n );\n\n await fs.writeFile(\n schemaPath,\n JSON.stringify(codexDiffResponseSchema),\n \"utf-8\",\n );\n\n const prompt = buildAgentPrompt(request);\n const args = [\n \"exec\",\n \"--cd\",\n request.cwd,\n \"--sandbox\",\n \"read-only\",\n \"--skip-git-repo-check\",\n \"--output-last-message\",\n outputPath,\n ...(request.mode === \"edit\" ? [\"--output-schema\", schemaPath] : []),\n prompt,\n ];\n\n try {\n const { stdout } = await execFileAsync(\"codex\", args, {\n cwd: request.cwd,\n maxBuffer: 1024 * 1024 * 5,\n timeout: Number(process.env.DEV_COPILOT_AGENT_TIMEOUT_MS ?? 120_000),\n env: {\n ...process.env,\n NO_COLOR: \"1\",\n },\n });\n\n if (request.mode === \"answer\") {\n try {\n const output = await fs.readFile(outputPath, \"utf-8\");\n return parseAnswerResponse(output);\n } catch {\n return parseAnswerResponse(stdout);\n }\n }\n\n return await parseEditResponse(outputPath);\n } catch (error) {\n throw new Error(toCodexErrorMessage(error));\n } finally {\n await fs.rm(outputPath, { force: true });\n await fs.rm(schemaPath, { force: true });\n }\n },\n async getStatus(cwd: string): Promise<AgentStatus> {\n try {\n const { stdout, stderr } = await execFileAsync(\"codex\", [\"login\", \"status\"], {\n cwd,\n timeout: 10_000,\n maxBuffer: 1024 * 128,\n });\n const output = `${stdout}\\n${stderr}`.trim();\n const authenticated = /logged in/i.test(output);\n const model = authenticated ? await getCodexModelName(cwd) : undefined;\n\n return {\n available: true,\n authenticated,\n agent: \"codex\",\n message: authenticated\n ? output || \"Codex CLI에 로그인되어 있습니다.\"\n : output || \"Codex CLI 로그인이 필요합니다.\",\n model,\n loginCommand: authenticated ? undefined : \"codex login\",\n };\n } catch (error) {\n const message =\n error instanceof Error ? error.message : \"Codex CLI 상태 확인에 실패했습니다.\";\n\n return {\n available: !message.includes(\"ENOENT\"),\n authenticated: false,\n agent: \"codex\",\n message: message.includes(\"ENOENT\")\n ? \"Codex CLI를 찾을 수 없습니다.\"\n : message,\n loginCommand: message.includes(\"ENOENT\") ? undefined : \"codex login\",\n };\n }\n },\n};\n\nexport const __internal = {\n toCodexErrorMessage,\n};\n","import type { CopilotAgent } from \"../../types\";\nimport { __internal as claudeInternal, claudeAdapter } from \"./claude-adapter\";\nimport { __internal as codexInternal, codexAdapter } from \"./codex-adapter\";\nimport type { AgentAdapter } from \"./types\";\n\nconst adapters: Record<CopilotAgent, AgentAdapter> = {\n codex: codexAdapter,\n claude: claudeAdapter,\n};\n\nexport const resolveAgentAdapter = (agent: CopilotAgent): AgentAdapter => {\n return adapters[agent];\n};\n\nexport { claudeAdapter, codexAdapter };\nexport const agentTestInternals = {\n claude: claudeInternal,\n codex: codexInternal,\n};\nexport type { AgentAdapter, AgentBridgeRequest, AgentBridgeResponse, AgentStatus } from \"./types\";\n","import { execFile } from \"node:child_process\";\nimport { promises as fs } from \"node:fs\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\ninterface ProjectContextConfig {\n rootDir: string;\n allowedDirs: string[];\n ignoredDirs: string[];\n maxReadBytes: number;\n maxSearchResults: number;\n}\n\ninterface CopilotContextParams {\n selectedText?: string;\n route?: string;\n fileHints?: string[];\n}\n\ninterface SearchResult {\n path: string;\n line: number;\n text: string;\n}\n\nconst normalizeRelativePath = (filePath: string) => {\n return filePath.replaceAll(\"\\\\\", \"/\").replace(/^\\.\\/+/, \"\");\n};\n\nconst isIgnoredPath = (relativePath: string, config: ProjectContextConfig) => {\n const segments = normalizeRelativePath(relativePath).split(\"/\");\n return segments.some((segment) => config.ignoredDirs.includes(segment));\n};\n\nconst walk = async (\n relativeDir: string,\n config: ProjectContextConfig,\n results: string[],\n limit: number,\n) => {\n if (results.length >= limit || isIgnoredPath(relativeDir, config)) {\n return;\n }\n\n const absoluteDir = path.join(config.rootDir, relativeDir);\n const entries = await fs.readdir(absoluteDir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (results.length >= limit) {\n return;\n }\n\n const relativePath = normalizeRelativePath(path.join(relativeDir, entry.name));\n\n if (isIgnoredPath(relativePath, config)) {\n continue;\n }\n\n if (entry.isDirectory()) {\n await walk(relativePath, config, results, limit);\n continue;\n }\n\n if (entry.isFile()) {\n results.push(relativePath);\n }\n }\n};\n\nconst listProjectFiles = async (\n config: ProjectContextConfig,\n query?: string,\n limit = 80,\n) => {\n const results: string[] = [];\n\n for (const allowedDir of config.allowedDirs) {\n const absoluteDir = path.join(config.rootDir, allowedDir);\n\n try {\n const stat = await fs.stat(absoluteDir);\n\n if (stat.isDirectory()) {\n await walk(allowedDir, config, results, limit);\n }\n } catch {\n continue;\n }\n }\n\n if (!query) {\n return results;\n }\n\n return results.filter((filePath) => filePath.toLowerCase().includes(query.toLowerCase()));\n};\n\nconst readProjectFile = async (\n config: ProjectContextConfig,\n filePath: string,\n maxBytes = config.maxReadBytes,\n) => {\n const absolutePath = path.join(config.rootDir, normalizeRelativePath(filePath));\n const content = await fs.readFile(absolutePath, \"utf-8\");\n\n return {\n path: filePath,\n content: content.slice(0, maxBytes),\n };\n};\n\nconst searchWithRg = async (\n config: ProjectContextConfig,\n query: string,\n limit: number,\n) => {\n const { stdout } = await execFileAsync(\n \"rg\",\n [\n \"--line-number\",\n \"--fixed-strings\",\n \"--color\",\n \"never\",\n \"--glob\",\n \"!node_modules/**\",\n \"--glob\",\n \"!.next/**\",\n \"--glob\",\n \"!.git/**\",\n query,\n ...config.allowedDirs,\n ],\n {\n cwd: config.rootDir,\n maxBuffer: 1024 * 1024,\n },\n );\n\n return stdout\n .split(\"\\n\")\n .filter(Boolean)\n .slice(0, limit)\n .map((line) => {\n const [filePath, lineNumber, ...rest] = line.split(\":\");\n return {\n path: filePath,\n line: Number(lineNumber),\n text: rest.join(\":\").trim(),\n };\n });\n};\n\nconst searchWithNode = async (\n config: ProjectContextConfig,\n query: string,\n limit: number,\n) => {\n const files = await listProjectFiles(config, undefined, 500);\n const results: SearchResult[] = [];\n\n for (const filePath of files) {\n if (results.length >= limit) {\n break;\n }\n\n try {\n const file = await readProjectFile(config, filePath, 100_000);\n const lines = file.content.split(\"\\n\");\n\n lines.forEach((line, index) => {\n if (results.length < limit && line.includes(query)) {\n results.push({\n path: filePath,\n line: index + 1,\n text: line.trim(),\n });\n }\n });\n } catch {\n continue;\n }\n }\n\n return results;\n};\n\nconst searchProjectText = async (\n config: ProjectContextConfig,\n query: string,\n limit = config.maxSearchResults,\n) => {\n if (!query.trim()) {\n return [];\n }\n\n try {\n return await searchWithRg(config, query, limit);\n } catch {\n return searchWithNode(config, query, limit);\n }\n};\n\nconst findComponentByText = async (\n config: ProjectContextConfig,\n text: string,\n limit = 8,\n) => {\n const normalized = text.replace(/\\s+/g, \" \").trim();\n const candidates = [\n normalized,\n normalized.slice(0, 120),\n ...normalized.split(/[.!?]\\s+/).filter((item) => item.length > 12),\n ];\n\n for (const candidate of candidates) {\n const results = await searchProjectText(config, candidate, limit);\n\n if (results.length) {\n return {\n query: candidate,\n results,\n };\n }\n }\n\n return {\n query: normalized,\n results: [],\n };\n};\n\nconst readJsonFile = async (rootDir: string, filePath: string) => {\n try {\n const content = await fs.readFile(path.join(rootDir, filePath), \"utf-8\");\n return JSON.parse(content) as Record<string, unknown>;\n } catch {\n return null;\n }\n};\n\nconst getProjectContext = async (config: ProjectContextConfig) => {\n const packageJson = await readJsonFile(config.rootDir, \"package.json\");\n const tsconfig = await readJsonFile(config.rootDir, \"tsconfig.json\");\n\n return {\n rootDir: config.rootDir,\n allowedDirs: config.allowedDirs,\n packageName: packageJson?.name,\n scripts: packageJson?.scripts,\n dependencies: packageJson?.dependencies,\n devDependencies: packageJson?.devDependencies,\n tsconfigPaths: (tsconfig?.compilerOptions as { paths?: unknown } | undefined)\n ?.paths,\n };\n};\n\nexport const buildCopilotProjectContext = async (\n rootDir: string,\n allowedDirs: string[],\n params: CopilotContextParams,\n) => {\n const config: ProjectContextConfig = {\n rootDir,\n allowedDirs,\n ignoredDirs: [\".git\", \".next\", \"node_modules\", \"coverage\", \"public\", \"dist\", \"build\"],\n maxReadBytes: 30_000,\n maxSearchResults: 20,\n };\n\n const project = await getProjectContext(config);\n const selectedText = params.selectedText?.trim();\n const routeQuery = params.route?.startsWith(\"/\")\n ? `app${params.route === \"/\" ? \"\" : params.route}/page.tsx`\n : params.route;\n\n const textMatches = selectedText\n ? await findComponentByText(config, selectedText, 8)\n : { query: \"\", results: [] };\n const routeMatches = routeQuery\n ? await searchProjectText(config, routeQuery, 5)\n : [];\n\n return JSON.stringify(\n {\n project,\n requestContext: {\n route: params.route,\n fileHints: params.fileHints,\n },\n selectedTextLookup: textMatches,\n routeLookup: routeMatches,\n guidance:\n \"이 컨텍스트는 로컬 프로젝트에서 수집한 실제 코드 검색 결과입니다. 수정 제안은 이 결과를 우선 근거로 삼고 path/oldText/newText 기반으로 작성하세요.\",\n },\n null,\n 2,\n );\n};\n","import { execFile } from \"node:child_process\";\nimport { promises as fs } from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\nconst getChangedFiles = (patchPreview: string) => {\n const regex = /^\\+\\+\\+ b\\/(.+)$/gm;\n const changedFiles = new Set<string>();\n\n let match = regex.exec(patchPreview);\n while (match) {\n changedFiles.add(match[1]);\n match = regex.exec(patchPreview);\n }\n\n return [...changedFiles];\n};\n\ninterface TextReplacement {\n path: string;\n oldText: string;\n newText: string;\n}\n\nexport const normalizeUnifiedPatch = (patchPreview: string) => {\n const withoutFence = patchPreview\n .replace(/^```(?:diff|patch)?\\s*/i, \"\")\n .replace(/\\s*```$/i, \"\")\n .trim();\n const diffStartIndex = withoutFence.indexOf(\"diff --git \");\n\n return diffStartIndex >= 0\n ? withoutFence.slice(diffStartIndex).trim()\n : withoutFence;\n};\n\nexport const validatePatchPaths = (\n patchPreview: string,\n validatePath: (filePath: string) => string,\n) => {\n const changedFiles = getChangedFiles(patchPreview);\n\n if (!changedFiles.length) {\n throw new Error(\"패치에서 변경 파일을 찾을 수 없습니다.\");\n }\n\n changedFiles.forEach(validatePath);\n\n return changedFiles;\n};\n\nexport const checkUnifiedPatch = async (patchPreview: string, cwd: string) => {\n const tempFilePath = path.join(\n os.tmpdir(),\n `dev-copilot-check-${Date.now()}-${Math.random().toString(16).slice(2)}.patch`,\n );\n\n await fs.writeFile(tempFilePath, `${patchPreview.trimEnd()}\\n`, \"utf-8\");\n\n try {\n await execFileAsync(\"git\", [\"apply\", \"--check\", tempFilePath], { cwd });\n } catch (error) {\n const detail =\n error instanceof Error && \"stderr\" in error\n ? String((error as Error & { stderr?: unknown }).stderr)\n : error instanceof Error\n ? error.message\n : \"알 수 없는 patch 검증 오류\";\n\n throw new Error(`적용 가능한 diff를 생성하지 못했습니다: ${detail.trim()}`);\n } finally {\n await fs.rm(tempFilePath, { force: true });\n }\n};\n\nconst createGitPatch = async (\n filePath: string,\n oldContent: string,\n newContent: string,\n) => {\n const tempDir = path.join(\n os.tmpdir(),\n `dev-copilot-diff-${Date.now()}-${Math.random().toString(16).slice(2)}`,\n );\n const oldFilePath = path.join(tempDir, \"old\", filePath);\n const newFilePath = path.join(tempDir, \"new\", filePath);\n\n await fs.mkdir(path.dirname(oldFilePath), { recursive: true });\n await fs.mkdir(path.dirname(newFilePath), { recursive: true });\n await fs.writeFile(oldFilePath, oldContent, \"utf-8\");\n await fs.writeFile(newFilePath, newContent, \"utf-8\");\n\n try {\n const { stdout } = await execFileAsync(\n \"git\",\n [\n \"diff\",\n \"--no-index\",\n \"--no-ext-diff\",\n \"--src-prefix=a/\",\n \"--dst-prefix=b/\",\n \"--\",\n path.join(\"old\", filePath),\n path.join(\"new\", filePath),\n ],\n { cwd: tempDir },\n ).catch((error: unknown) => {\n const typedError = error as { stdout?: string; code?: number };\n\n if (typedError.code === 1 && typedError.stdout) {\n return { stdout: typedError.stdout };\n }\n\n throw error;\n });\n\n return stdout\n .replaceAll(`a/old/${filePath}`, `a/${filePath}`)\n .replaceAll(`b/new/${filePath}`, `b/${filePath}`)\n .trim();\n } finally {\n await fs.rm(tempDir, { force: true, recursive: true });\n }\n};\n\nexport const createPatchFromTextReplacements = async (\n replacements: TextReplacement[],\n cwd: string,\n validatePath: (filePath: string) => string,\n) => {\n const byPath = new Map<string, TextReplacement[]>();\n\n for (const replacement of replacements) {\n const normalizedPath = validatePath(replacement.path);\n const current = byPath.get(normalizedPath) ?? [];\n current.push(replacement);\n byPath.set(normalizedPath, current);\n }\n\n const patches: string[] = [];\n\n for (const [filePath, fileReplacements] of byPath.entries()) {\n const absolutePath = path.join(cwd, filePath);\n const oldContent = await fs.readFile(absolutePath, \"utf-8\");\n let newContent = oldContent;\n\n for (const replacement of fileReplacements) {\n if (!newContent.includes(replacement.oldText)) {\n throw new Error(`원문을 파일에서 찾을 수 없습니다: ${filePath}`);\n }\n\n newContent = newContent.replace(replacement.oldText, replacement.newText);\n }\n\n if (newContent === oldContent) {\n continue;\n }\n\n patches.push(await createGitPatch(filePath, oldContent, newContent));\n }\n\n const patchPreview = patches.join(\"\\n\").trim();\n\n if (!patchPreview) {\n throw new Error(\"변경할 내용이 없습니다.\");\n }\n\n return patchPreview;\n};\n\nexport const applyUnifiedPatch = async (\n patchPreview: string,\n cwd: string,\n actor: string,\n) => {\n const tempFilePath = path.join(\n os.tmpdir(),\n `dev-copilot-${Date.now()}-${Math.random().toString(16).slice(2)}.patch`,\n );\n\n await fs.writeFile(tempFilePath, `${patchPreview.trimEnd()}\\n`, \"utf-8\");\n\n try {\n await execFileAsync(\"git\", [\"apply\", \"--check\", tempFilePath], { cwd });\n await execFileAsync(\"git\", [\"apply\", tempFilePath], { cwd });\n\n const changedFiles = getChangedFiles(patchPreview);\n\n return {\n actor,\n changedFiles,\n };\n } catch (error) {\n const detail =\n error instanceof Error && \"stderr\" in error\n ? String((error as Error & { stderr?: unknown }).stderr)\n : error instanceof Error\n ? error.message\n : \"알 수 없는 patch 적용 오류\";\n\n throw new Error(`patch 적용에 실패했습니다: ${detail.trim()}`);\n } finally {\n await fs.rm(tempFilePath, { force: true });\n }\n};\n","import { randomUUID } from \"node:crypto\";\n\ninterface ProposedPatch {\n patchId: string;\n approvalToken: string;\n patchPreview: string;\n allowedPaths: string[];\n createdAt: number;\n}\n\nconst patchStore = new Map<string, ProposedPatch>();\nconst TTL_MS = 1000 * 60 * 15;\n\nconst pruneExpired = () => {\n const now = Date.now();\n\n for (const [key, value] of patchStore.entries()) {\n if (now - value.createdAt > TTL_MS) {\n patchStore.delete(key);\n }\n }\n};\n\nexport const createProposedPatch = (patchPreview: string, allowedPaths: string[]) => {\n pruneExpired();\n\n const patchId = randomUUID();\n const approvalToken = `approve:${patchId}`;\n\n patchStore.set(patchId, {\n patchId,\n approvalToken,\n patchPreview,\n allowedPaths,\n createdAt: Date.now(),\n });\n\n return {\n patchId,\n approvalToken,\n };\n};\n\nexport const getProposedPatch = (patchId: string, approvalToken: string) => {\n pruneExpired();\n\n const item = patchStore.get(patchId);\n\n if (!item || item.approvalToken !== approvalToken) {\n return null;\n }\n\n return item;\n};\n\nexport const deleteProposedPatch = (patchId: string) => {\n patchStore.delete(patchId);\n};\n","import { createServer, type IncomingMessage, type ServerResponse } from \"node:http\";\n\nimport type {\n CopilotAgent,\n CopilotApplyRequest,\n CopilotApplyResponse,\n CopilotChatRequest,\n CopilotChatResponse,\n} from \"../types\";\nimport type { DevCopilotBridgeConfig } from \"../lib/config\";\nimport { resolveAndValidatePath } from \"../lib/guards\";\nimport { resolveAgentAdapter } from \"../internal/agents\";\nimport type { AgentAdapter } from \"../internal/agents\";\nimport { buildCopilotProjectContext } from \"../internal/project-context\";\nimport {\n applyUnifiedPatch,\n checkUnifiedPatch,\n createPatchFromTextReplacements,\n normalizeUnifiedPatch,\n validatePatchPaths,\n} from \"../lib/patch\";\nimport {\n createProposedPatch,\n deleteProposedPatch,\n getProposedPatch,\n} from \"../lib/store\";\n\ntype ParsedEditPayload = {\n message?: string;\n patchPreview?: string;\n changes?: Array<{\n path: string;\n oldText: string;\n newText: string;\n }>;\n warnings?: string[];\n};\n\nconst createCorsHeaders = (config: DevCopilotBridgeConfig) => ({\n \"Access-Control-Allow-Origin\": config.corsOrigin,\n \"Access-Control-Allow-Methods\": \"GET,POST,OPTIONS\",\n \"Access-Control-Allow-Headers\": \"Content-Type\",\n \"Content-Type\": \"application/json; charset=utf-8\",\n});\n\nconst sendJson = (\n response: ServerResponse,\n config: DevCopilotBridgeConfig,\n statusCode: number,\n payload: unknown,\n) => {\n response.writeHead(statusCode, createCorsHeaders(config));\n response.end(JSON.stringify(payload));\n};\n\nconst readJsonBody = async <T>(request: IncomingMessage) => {\n const chunks: Buffer[] = [];\n\n for await (const chunk of request) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n\n const raw = Buffer.concat(chunks).toString(\"utf-8\");\n return JSON.parse(raw) as T;\n};\n\nconst createProjectContext = async (\n config: DevCopilotBridgeConfig,\n payload: CopilotChatRequest,\n allowedPaths: string[],\n) => {\n return buildCopilotProjectContext(\n config.rootDir,\n allowedPaths,\n {\n selectedText: payload.selectedText,\n route: payload.context?.route,\n fileHints: payload.context?.fileHints,\n },\n );\n};\n\nconst sanitizeAllowedPaths = (fileHints?: string[]) => {\n if (!fileHints?.length) {\n return null;\n }\n\n const sanitized = fileHints\n .map((value) => value.trim())\n .filter(Boolean)\n .filter((value) => value === \".\" || value === \"*\" || /^[A-Za-z0-9._-]+$/.test(value));\n\n return sanitized.length ? Array.from(new Set(sanitized)) : null;\n};\n\nconst toPatchSummary = (changedFiles: string[]) => {\n return changedFiles.length === 1\n ? \"1개 파일에 수정이 반영되었습니다.\"\n : `${changedFiles.length}개 파일에 수정이 반영되었습니다.`;\n};\n\nconst toEditPayload = (payload: ParsedEditPayload): ParsedEditPayload => {\n return payload;\n};\n\nconst isCopilotAgent = (value: string | null): value is CopilotAgent => {\n return value === \"codex\" || value === \"claude\";\n};\n\nconst resolveRequestedAgent = (\n config: DevCopilotBridgeConfig,\n requestAgent?: CopilotAgent,\n queryAgent?: string | null,\n): CopilotAgent => {\n if (requestAgent) {\n return requestAgent;\n }\n\n if (isCopilotAgent(queryAgent)) {\n return queryAgent;\n }\n\n return config.agent;\n};\n\nexport const createDevCopilotBridgeServer = (config: DevCopilotBridgeConfig) => {\n return createDevCopilotBridgeServerWithDependencies(config, { resolveAdapter: resolveAgentAdapter });\n};\n\nexport const createDevCopilotBridgeServerWithDependencies = (\n config: DevCopilotBridgeConfig,\n dependencies: {\n resolveAdapter: (agent: CopilotAgent) => AgentAdapter;\n },\n) => {\n const server = createServer(async (request, response) => {\n const method = request.method ?? \"GET\";\n const url = new URL(request.url ?? \"/\", `http://${config.host}:${config.port}`);\n\n if (method === \"OPTIONS\") {\n response.writeHead(204, createCorsHeaders(config));\n response.end();\n return;\n }\n\n try {\n if (method === \"GET\" && url.pathname === \"/status\") {\n const agent = resolveRequestedAgent(config, undefined, url.searchParams.get(\"agent\"));\n const adapter = dependencies.resolveAdapter(agent);\n\n sendJson(response, config, 200, await adapter.getStatus(config.rootDir));\n return;\n }\n\n if (method === \"POST\" && url.pathname === \"/chat\") {\n const payload = await readJsonBody<CopilotChatRequest>(request);\n const effectiveAllowedPaths =\n sanitizeAllowedPaths(payload.context?.fileHints) ?? config.allowedPaths;\n const agent = resolveRequestedAgent(config, payload.context?.agent);\n const adapter = dependencies.resolveAdapter(agent);\n\n if (!payload.prompt?.trim()) {\n sendJson(response, config, 400, { error: \"프롬프트를 입력해 주세요.\" });\n return;\n }\n\n const agentResponse = await adapter.run({\n selectedText: payload.selectedText ?? \"\",\n prompt: payload.prompt,\n mode: payload.mode,\n route: payload.context?.route,\n fileHints: payload.context?.fileHints,\n previousResponse: payload.context?.previousResponse,\n projectContext: await createProjectContext(config, payload, effectiveAllowedPaths),\n cwd: config.rootDir,\n });\n\n if (payload.mode === \"answer\") {\n const answerResponse: CopilotChatResponse = {\n message: agentResponse.message,\n warnings: agentResponse.warnings,\n };\n\n sendJson(response, config, 200, answerResponse);\n return;\n }\n\n const parsed = toEditPayload(agentResponse);\n let patchPreview = \"\";\n\n try {\n if (parsed.changes?.length) {\n patchPreview = await createPatchFromTextReplacements(\n parsed.changes,\n config.rootDir,\n (filePath) =>\n resolveAndValidatePath(filePath, effectiveAllowedPaths, config.rootDir),\n );\n } else if (parsed.patchPreview) {\n patchPreview = normalizeUnifiedPatch(parsed.patchPreview);\n }\n } catch (error) {\n const failedResponse: CopilotChatResponse = {\n message:\n parsed.message ??\n \"에이전트가 수정안을 만들었지만 실제 파일 내용과 매칭하지 못했습니다.\",\n warnings: [\n ...(parsed.warnings ?? []),\n error instanceof Error ? error.message : \"수정안 생성에 실패했습니다.\",\n ],\n };\n\n sendJson(response, config, 200, failedResponse);\n return;\n }\n\n if (!patchPreview) {\n const emptyPatchResponse: CopilotChatResponse = {\n message: parsed.message ?? \"패치 제안을 생성하지 못했습니다.\",\n warnings: [\n ...(parsed.warnings ?? []),\n \"적용 가능한 변경 목록이 없어 패치 미리보기와 적용 버튼을 만들 수 없습니다.\",\n ],\n };\n\n sendJson(response, config, 200, emptyPatchResponse);\n return;\n }\n\n try {\n validatePatchPaths(patchPreview, (filePath) =>\n resolveAndValidatePath(filePath, effectiveAllowedPaths, config.rootDir),\n );\n await checkUnifiedPatch(patchPreview, config.rootDir);\n } catch (error) {\n const invalidPatchResponse: CopilotChatResponse = {\n message:\n parsed.message ??\n \"에이전트가 수정안을 만들었지만 적용 가능한 diff 형식이 아닙니다.\",\n patchPreview,\n warnings: [\n ...(parsed.warnings ?? []),\n error instanceof Error ? error.message : \"patch 검증에 실패했습니다.\",\n ],\n };\n\n sendJson(response, config, 200, invalidPatchResponse);\n return;\n }\n\n const patch = createProposedPatch(patchPreview, effectiveAllowedPaths);\n const chatResponse: CopilotChatResponse = {\n message: parsed.message ?? \"에이전트가 패치 미리보기를 생성했습니다.\",\n patchPreview,\n patchId: patch.patchId,\n warnings: parsed.warnings ?? [],\n };\n\n sendJson(response, config, 200, chatResponse);\n return;\n }\n\n if (method === \"POST\" && url.pathname === \"/apply\") {\n const payload = await readJsonBody<CopilotApplyRequest>(request);\n\n if (!payload.patchId || !payload.approvalToken) {\n sendJson(response, config, 400, {\n error: \"patchId와 approvalToken이 필요합니다.\",\n });\n return;\n }\n\n const proposedPatch = getProposedPatch(payload.patchId, payload.approvalToken);\n\n if (!proposedPatch) {\n sendJson(response, config, 400, {\n error: \"유효하지 않거나 만료된 patchId입니다.\",\n });\n return;\n }\n\n validatePatchPaths(proposedPatch.patchPreview, (filePath) =>\n resolveAndValidatePath(\n filePath,\n proposedPatch.allowedPaths?.length\n ? proposedPatch.allowedPaths\n : config.allowedPaths,\n config.rootDir,\n ),\n );\n\n const result = await applyUnifiedPatch(\n proposedPatch.patchPreview,\n config.rootDir,\n \"local-codex-bridge\",\n );\n deleteProposedPatch(payload.patchId);\n\n const applyResponse: CopilotApplyResponse = {\n applied: true,\n changedFiles: result.changedFiles,\n summary: toPatchSummary(result.changedFiles),\n };\n\n sendJson(response, config, 200, applyResponse);\n return;\n }\n\n sendJson(response, config, 404, { error: \"지원하지 않는 경로입니다.\" });\n } catch (error) {\n sendJson(response, config, 500, {\n error:\n error instanceof Error\n ? error.message\n : \"브리지 서버 처리 중 오류가 발생했습니다.\",\n });\n }\n });\n\n return server;\n};\n","import { createDevCopilotBridgeConfig } from \"../lib/config\";\nimport { createDevCopilotBridgeServer } from \"../server/http-server\";\nimport type { CopilotAgent } from \"../types\";\n\nconst resolveAgent = (value: string | undefined): CopilotAgent => {\n if (value === \"claude\") {\n return \"claude\";\n }\n\n return \"codex\";\n};\n\nexport const runDevCopilotBridgeCli = async (argv: string[]) => {\n const portFlagIndex = argv.findIndex((value) => value === \"-p\");\n const portFlagValue =\n portFlagIndex >= 0 ? argv[portFlagIndex + 1] : undefined;\n const positionalPort = argv.find((value) => /^\\d+$/.test(value));\n const resolvedPort = portFlagValue\n ? Number(portFlagValue)\n : positionalPort\n ? Number(positionalPort)\n : Number(process.env.DEV_COPILOT_BRIDGE_PORT ?? 3339);\n const positionalAgent = argv.find((value) => value === \"codex\" || value === \"claude\");\n\n const config = createDevCopilotBridgeConfig({\n rootDir: process.cwd(),\n host: process.env.DEV_COPILOT_BRIDGE_HOST,\n port: Number.isFinite(resolvedPort) ? resolvedPort : 3339,\n corsOrigin: process.env.DEV_COPILOT_BRIDGE_CORS_ORIGIN ?? \"*\",\n agent: resolveAgent(positionalAgent),\n allowedPaths: (process.env.DEV_COPILOT_ALLOWED_PATHS ??\n \"app,src,widgets,features,entities,shared,components\")\n .split(\",\")\n .map((value) => value.trim())\n .filter(Boolean),\n });\n\n const server = createDevCopilotBridgeServer(config);\n\n await new Promise<void>((resolve) => {\n server.listen(config.port, config.host, () => {\n process.stdout.write(\n `[dev-copilot-bridge] listening on http://${config.host}:${config.port}\\n`,\n );\n resolve();\n });\n });\n};\n","#!/usr/bin/env node\n\nimport { runDevCopilotBridgeCli } from \"../bridge/cli/run-http\";\n\nrunDevCopilotBridgeCli(process.argv.slice(2)).catch((error) => {\n const message = error instanceof Error ? error.message : String(error);\n process.stderr.write(`[dev-copilot-bridge] ${message}\\n`);\n process.exit(1);\n});\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"bridge.mjs","names":["execFileAsync","execFileAsync","fs","execFileAsync","fs","fs"],"sources":["../../src/bridge/lib/config.ts","../../src/bridge/lib/guards.ts","../../src/bridge/internal/prompts.ts","../../src/bridge/internal/agents/claude-adapter.ts","../../src/bridge/internal/agents/codex-adapter.ts","../../src/bridge/internal/agents/index.ts","../../src/bridge/internal/project-context.ts","../../src/bridge/lib/patch.ts","../../src/bridge/lib/store.ts","../../src/bridge/server/http-server.ts","../../src/bridge/cli/run-http.ts","../../src/bin/bridge.ts"],"sourcesContent":["import type { CopilotAgent } from \"../types\";\n\nexport interface DevCopilotBridgeConfig {\n rootDir: string;\n host: string;\n port: number;\n corsOrigin: string;\n agent: CopilotAgent;\n allowedPaths: string[];\n}\n\nconst DEFAULT_ALLOWED_PATHS = [\n \"app\",\n \"src\",\n \"widgets\",\n \"features\",\n \"entities\",\n \"shared\",\n \"components\",\n];\n\nexport const createDevCopilotBridgeConfig = (\n config: Partial<DevCopilotBridgeConfig> & { rootDir: string },\n): DevCopilotBridgeConfig => {\n return {\n rootDir: config.rootDir,\n host: config.host ?? \"127.0.0.1\",\n port: config.port ?? 3339,\n corsOrigin: config.corsOrigin ?? \"*\",\n agent: config.agent ?? \"codex\",\n allowedPaths: config.allowedPaths ?? DEFAULT_ALLOWED_PATHS,\n };\n};\n","import path from \"node:path\";\n\nimport type { DevCopilotBridgeConfig } from \"./config\";\n\nexport const resolveAndValidatePath = (\n filePath: string,\n allowedPaths: DevCopilotBridgeConfig[\"allowedPaths\"],\n rootDir?: string,\n) => {\n if (!filePath) {\n throw new Error(\"허용되지 않은 파일 경로입니다.\");\n }\n\n let normalizedInput = filePath.replaceAll(\"\\\\\", \"/\");\n\n if (rootDir) {\n const normalizedRootDir = rootDir.replaceAll(\"\\\\\", \"/\").replace(/\\/+$/, \"\");\n\n if (normalizedInput.startsWith(`${normalizedRootDir}/`)) {\n normalizedInput = normalizedInput.slice(normalizedRootDir.length + 1);\n }\n }\n\n if (\n path.isAbsolute(normalizedInput) ||\n normalizedInput.includes(\"..\")\n ) {\n throw new Error(\"허용되지 않은 파일 경로입니다.\");\n }\n\n const normalized = normalizedInput;\n const firstSegment = normalized.split(\"/\")[0];\n const allowAll = allowedPaths.includes(\".\") || allowedPaths.includes(\"*\");\n\n if (!allowAll && !allowedPaths.includes(firstSegment)) {\n throw new Error(`허용 경로 외 파일은 수정할 수 없습니다: ${firstSegment}`);\n }\n\n return normalized;\n};\n","import type { CopilotMode } from \"../types\";\n\nexport interface AgentPromptRequest {\n selectedText: string;\n prompt: string;\n mode: CopilotMode;\n route?: string;\n fileHints?: string[];\n projectContext?: string;\n previousResponse?: string;\n}\n\nexport const buildAgentPrompt = (request: AgentPromptRequest) => {\n if (request.mode === \"answer\") {\n return [\n \"Answer the user's web overlay request.\",\n \"Do not modify files.\",\n \"Use the provided project context when it is relevant.\",\n \"Respond in Korean.\",\n \"Keep the answer concise and actionable.\",\n \"\",\n `Current route: ${request.route ?? \"unknown\"}`,\n `Selected text:\\n${request.selectedText || \"(none)\"}`,\n `User prompt:\\n${request.prompt}`,\n `Previous AI response:\\n${request.previousResponse ?? \"(none)\"}`,\n `Project context:\\n${request.projectContext ?? \"(none)\"}`,\n ].join(\"\\n\");\n }\n\n return [\n \"You are a local code-edit proposal generator called from a web overlay.\",\n \"Do not modify files directly.\",\n \"Return JSON only.\",\n \"Do not generate unified diff directly.\",\n \"Put repository-relative file path(path), exact old text(oldText), and replacement text(newText) into the changes array.\",\n \"Never use absolute paths. The path must be relative to the repository root, for example src/features/article/model/data.ts.\",\n \"oldText must exactly match the file content and must not use ellipsis.\",\n \"Ground the proposal in the selected text, user prompt, and provided project context.\",\n \"Write the message field in Korean.\",\n \"\",\n `Current route: ${request.route ?? \"unknown\"}`,\n `Allowed path hints: ${(request.fileHints ?? []).join(\", \") || \"(none)\"}`,\n `Selected text:\\n${request.selectedText || \"(none)\"}`,\n `User prompt:\\n${request.prompt}`,\n `Previous AI response:\\n${request.previousResponse ?? \"(none)\"}`,\n `Project context:\\n${request.projectContext ?? \"(none)\"}`,\n ].join(\"\\n\");\n};\n","import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nimport { buildAgentPrompt } from \"../prompts\";\nimport type {\n AgentAdapter,\n AgentBridgeRequest,\n AgentBridgeResponse,\n AgentStatus,\n} from \"./types\";\n\nconst execFileAsync = promisify(execFile);\n\ntype ClaudePrintJsonResponse = {\n result?: unknown;\n message?: unknown;\n output?: unknown;\n content?: unknown;\n};\n\nconst AUTH_ERROR_PATTERN =\n /401|authentication|invalid authentication credentials|please run \\/login|claude\\s*\\/login|로그인/i;\n\nconst extractErrorDetails = (error: unknown) => {\n const unknownRecord = error && typeof error === \"object\" ? (error as Record<string, unknown>) : null;\n const message = error instanceof Error ? error.message : String(error);\n const stderr = typeof unknownRecord?.stderr === \"string\" ? unknownRecord.stderr : \"\";\n const stdout = typeof unknownRecord?.stdout === \"string\" ? unknownRecord.stdout : \"\";\n const merged = [message, stderr, stdout]\n .map((part) => part.trim())\n .filter(Boolean)\n .join(\"\\n\");\n\n return {\n message,\n stderr,\n stdout,\n merged,\n };\n};\n\nconst toClaudeErrorMessage = (error: unknown) => {\n const { message, merged } = extractErrorDetails(error);\n\n if (AUTH_ERROR_PATTERN.test(merged)) {\n return \"Claude Code 로그인이 필요합니다. 터미널에서 `claude /login`을 실행해 주세요.\";\n }\n\n if (/ENOENT/.test(merged)) {\n return \"Claude CLI를 찾을 수 없습니다. Claude Code CLI 설치 상태를 확인해 주세요.\";\n }\n\n return message;\n};\n\nconst findFirstString = (value: unknown): string | null => {\n if (typeof value === \"string\") {\n const text = value.trim();\n return text ? text : null;\n }\n\n if (Array.isArray(value)) {\n for (const item of value) {\n const found = findFirstString(item);\n if (found) {\n return found;\n }\n }\n return null;\n }\n\n if (value && typeof value === \"object\") {\n const record = value as Record<string, unknown>;\n const preferredKeys = [\"result\", \"message\", \"output\", \"text\", \"content\"];\n\n for (const key of preferredKeys) {\n if (key in record) {\n const found = findFirstString(record[key]);\n if (found) {\n return found;\n }\n }\n }\n\n for (const nestedValue of Object.values(record)) {\n const found = findFirstString(nestedValue);\n if (found) {\n return found;\n }\n }\n }\n\n return null;\n};\n\nconst parseClaudeJsonOutput = (raw: string) => {\n const parsed = JSON.parse(raw) as ClaudePrintJsonResponse;\n const text = findFirstString(parsed) ?? \"\";\n\n return {\n parsed,\n text,\n };\n};\n\nconst parseClaudeEditResponse = (raw: string): AgentBridgeResponse => {\n const { text } = parseClaudeJsonOutput(raw);\n const parsed = JSON.parse(text) as AgentBridgeResponse;\n\n return {\n message: parsed.message,\n patchPreview: parsed.patchPreview,\n changes: parsed.changes,\n warnings: parsed.warnings ?? [],\n };\n};\n\nexport const claudeAdapter: AgentAdapter = {\n agent: \"claude\",\n async run(request: AgentBridgeRequest): Promise<AgentBridgeResponse> {\n const prompt = buildAgentPrompt(request);\n const args = [\n \"-p\",\n \"--output-format\",\n \"json\",\n ...(request.mode === \"edit\"\n ? [\n \"--json-schema\",\n '{\"type\":\"object\",\"properties\":{\"message\":{\"type\":\"string\"},\"patchPreview\":{\"type\":\"string\"},\"warnings\":{\"type\":\"array\",\"items\":{\"type\":\"string\"}},\"changes\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"path\":{\"type\":\"string\"},\"oldText\":{\"type\":\"string\"},\"newText\":{\"type\":\"string\"}},\"required\":[\"path\",\"oldText\",\"newText\"],\"additionalProperties\":false}}},\"required\":[\"message\"],\"additionalProperties\":false}',\n ]\n : []),\n prompt,\n ];\n\n try {\n const { stdout } = await execFileAsync(\"claude\", args, {\n cwd: request.cwd,\n maxBuffer: 1024 * 1024 * 5,\n timeout: Number(process.env.DEV_COPILOT_AGENT_TIMEOUT_MS ?? 120_000),\n env: {\n ...process.env,\n NO_COLOR: \"1\",\n },\n });\n\n if (request.mode === \"answer\") {\n const { text } = parseClaudeJsonOutput(stdout);\n\n return {\n message: text,\n warnings: [],\n };\n }\n\n return parseClaudeEditResponse(stdout);\n } catch (error) {\n throw new Error(toClaudeErrorMessage(error));\n }\n },\n async getStatus(cwd: string): Promise<AgentStatus> {\n try {\n await execFileAsync(\"claude\", [\"-p\", \"--output-format\", \"json\", \"OK\"], {\n cwd,\n timeout: 15_000,\n maxBuffer: 1024 * 512,\n });\n\n return {\n available: true,\n authenticated: true,\n agent: \"claude\",\n message: \"Claude Code CLI에 로그인되어 있습니다.\",\n };\n } catch (error) {\n const { merged, message } = extractErrorDetails(error);\n const unavailable = /ENOENT/.test(merged);\n const authError = AUTH_ERROR_PATTERN.test(merged);\n\n return {\n available: !unavailable,\n authenticated: false,\n agent: \"claude\",\n message: unavailable\n ? \"Claude CLI를 찾을 수 없습니다.\"\n : authError\n ? \"Claude Code 로그인이 필요합니다.\"\n : \"Claude Code 상태 확인에 실패했습니다. 터미널에서 `claude /login` 실행 후 다시 시도해 주세요.\",\n loginCommand: unavailable ? undefined : \"claude /login\",\n };\n }\n },\n};\n\nexport const __internal = {\n findFirstString,\n parseClaudeJsonOutput,\n parseClaudeEditResponse,\n toClaudeErrorMessage,\n};\n","import { execFile } from \"node:child_process\";\nimport { promises as fs } from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\n\nimport { buildAgentPrompt } from \"../prompts\";\nimport type {\n AgentAdapter,\n AgentBridgeRequest,\n AgentBridgeResponse,\n AgentStatus,\n} from \"./types\";\n\nconst execFileAsync = promisify(execFile);\nconst codexBridgeConfigArgs = [\n \"-c\",\n \"mcp_servers.notion.enabled=false\",\n \"-c\",\n \"mcp_servers.figma.enabled=false\",\n \"-c\",\n \"mcp_servers.linear.enabled=false\",\n \"-c\",\n \"mcp_servers.context7.enabled=false\",\n \"-c\",\n \"mcp_servers.playwright.enabled=false\",\n \"-c\",\n \"mcp_servers.zeplin.enabled=false\",\n] as const;\nconst codexDiffResponseSchema = {\n type: \"object\",\n properties: {\n message: { type: \"string\" },\n warnings: {\n type: \"array\",\n items: { type: \"string\" },\n },\n changes: {\n type: \"array\",\n items: {\n type: \"object\",\n properties: {\n path: { type: \"string\" },\n oldText: { type: \"string\" },\n newText: { type: \"string\" },\n },\n required: [\"path\", \"oldText\", \"newText\"],\n additionalProperties: false,\n },\n },\n },\n required: [\"message\", \"warnings\", \"changes\"],\n additionalProperties: false,\n} as const;\n\nconst parseAnswerResponse = (rawOutput: string): AgentBridgeResponse => {\n return {\n message: rawOutput.trim(),\n warnings: [],\n };\n};\n\nconst parseEditResponse = async (outputPath: string) => {\n const content = await fs.readFile(outputPath, \"utf-8\");\n const parsed = JSON.parse(content) as AgentBridgeResponse;\n\n return {\n message: parsed.message,\n patchPreview: parsed.patchPreview,\n changes: parsed.changes,\n warnings: parsed.warnings ?? [],\n };\n};\n\nconst isCodexAuthenticatedFromStatus = (statusOutput: string) => {\n const normalized = statusOutput.toLowerCase();\n\n if (/not logged in|login required|로그인 필요/.test(normalized)) {\n return false;\n }\n\n return /logged in|로그인됨|로그인되어/.test(normalized);\n};\n\nconst toCodexErrorMessage = (error: unknown) => {\n const message = error instanceof Error ? error.message : String(error);\n\n if (/not logged in|login required|authentication|unauthorized/i.test(message)) {\n return \"Codex CLI 로그인이 필요합니다. 터미널에서 `codex login`을 실행해 주세요.\";\n }\n\n if (/ENOENT/.test(message)) {\n return \"Codex CLI를 찾을 수 없습니다. Codex CLI 설치 상태를 확인해 주세요.\";\n }\n\n return message;\n};\n\nconst getCodexModelName = async (cwd: string) => {\n const outputPath = path.join(\n os.tmpdir(),\n `dev-copilot-codex-status-${Date.now()}-${Math.random().toString(16).slice(2)}.txt`,\n );\n\n try {\n const { stdout, stderr } = await execFileAsync(\n \"codex\",\n [\n ...codexBridgeConfigArgs,\n \"exec\",\n \"--cd\",\n cwd,\n \"--sandbox\",\n \"read-only\",\n \"--skip-git-repo-check\",\n \"--output-last-message\",\n outputPath,\n \"OK만 출력해줘.\",\n ],\n {\n cwd,\n timeout: 30_000,\n maxBuffer: 1024 * 512,\n env: {\n ...process.env,\n NO_COLOR: \"1\",\n },\n },\n );\n const output = `${stdout}\\n${stderr}`;\n const match = output.match(/^model:\\s*(.+)$/m);\n\n return match?.[1]?.trim();\n } catch {\n return undefined;\n } finally {\n await fs.rm(outputPath, { force: true });\n }\n};\n\nexport const codexAdapter: AgentAdapter = {\n agent: \"codex\",\n async run(request: AgentBridgeRequest): Promise<AgentBridgeResponse> {\n const outputPath = path.join(\n os.tmpdir(),\n `dev-copilot-codex-${Date.now()}-${Math.random().toString(16).slice(2)}.json`,\n );\n const schemaPath = path.join(\n os.tmpdir(),\n `dev-copilot-codex-schema-${Date.now()}-${Math.random().toString(16).slice(2)}.json`,\n );\n\n await fs.writeFile(\n schemaPath,\n JSON.stringify(codexDiffResponseSchema),\n \"utf-8\",\n );\n\n const prompt = buildAgentPrompt(request);\n const args = [\n ...codexBridgeConfigArgs,\n \"exec\",\n \"--cd\",\n request.cwd,\n \"--sandbox\",\n \"read-only\",\n \"--skip-git-repo-check\",\n \"--output-last-message\",\n outputPath,\n ...(request.mode === \"edit\" ? [\"--output-schema\", schemaPath] : []),\n prompt,\n ];\n\n try {\n const { stdout } = await execFileAsync(\"codex\", args, {\n cwd: request.cwd,\n maxBuffer: 1024 * 1024 * 5,\n timeout: Number(process.env.DEV_COPILOT_AGENT_TIMEOUT_MS ?? 120_000),\n env: {\n ...process.env,\n NO_COLOR: \"1\",\n },\n });\n\n if (request.mode === \"answer\") {\n try {\n const output = await fs.readFile(outputPath, \"utf-8\");\n return parseAnswerResponse(output);\n } catch {\n return parseAnswerResponse(stdout);\n }\n }\n\n return await parseEditResponse(outputPath);\n } catch (error) {\n throw new Error(toCodexErrorMessage(error));\n } finally {\n await fs.rm(outputPath, { force: true });\n await fs.rm(schemaPath, { force: true });\n }\n },\n async getStatus(cwd: string): Promise<AgentStatus> {\n try {\n const { stdout, stderr } = await execFileAsync(\"codex\", [\"login\", \"status\"], {\n cwd,\n timeout: 10_000,\n maxBuffer: 1024 * 128,\n });\n const output = `${stdout}\\n${stderr}`.trim();\n const authenticated = isCodexAuthenticatedFromStatus(output);\n const model = authenticated ? await getCodexModelName(cwd) : undefined;\n\n return {\n available: true,\n authenticated,\n agent: \"codex\",\n message: authenticated\n ? output || \"Codex CLI에 로그인되어 있습니다.\"\n : output || \"Codex CLI 로그인이 필요합니다.\",\n model,\n loginCommand: authenticated ? undefined : \"codex login\",\n };\n } catch (error) {\n const message =\n error instanceof Error ? error.message : \"Codex CLI 상태 확인에 실패했습니다.\";\n\n return {\n available: !message.includes(\"ENOENT\"),\n authenticated: false,\n agent: \"codex\",\n message: message.includes(\"ENOENT\")\n ? \"Codex CLI를 찾을 수 없습니다.\"\n : message,\n loginCommand: message.includes(\"ENOENT\") ? undefined : \"codex login\",\n };\n }\n },\n};\n\nexport const __internal = {\n toCodexErrorMessage,\n};\n","import type { CopilotAgent } from \"../../types\";\nimport { __internal as claudeInternal, claudeAdapter } from \"./claude-adapter\";\nimport { __internal as codexInternal, codexAdapter } from \"./codex-adapter\";\nimport type { AgentAdapter } from \"./types\";\n\nconst adapters: Record<CopilotAgent, AgentAdapter> = {\n codex: codexAdapter,\n claude: claudeAdapter,\n};\n\nexport const resolveAgentAdapter = (agent: CopilotAgent): AgentAdapter => {\n return adapters[agent];\n};\n\nexport { claudeAdapter, codexAdapter };\nexport const agentTestInternals = {\n claude: claudeInternal,\n codex: codexInternal,\n};\nexport type { AgentAdapter, AgentBridgeRequest, AgentBridgeResponse, AgentStatus } from \"./types\";\n","import { execFile } from \"node:child_process\";\nimport { promises as fs } from \"node:fs\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\ninterface ProjectContextConfig {\n rootDir: string;\n allowedDirs: string[];\n ignoredDirs: string[];\n maxReadBytes: number;\n maxSearchResults: number;\n}\n\ninterface CopilotContextParams {\n selectedText?: string;\n route?: string;\n fileHints?: string[];\n}\n\ninterface SearchResult {\n path: string;\n line: number;\n text: string;\n}\n\nconst normalizeRelativePath = (filePath: string) => {\n return filePath.replaceAll(\"\\\\\", \"/\").replace(/^\\.\\/+/, \"\");\n};\n\nconst isIgnoredPath = (relativePath: string, config: ProjectContextConfig) => {\n const segments = normalizeRelativePath(relativePath).split(\"/\");\n return segments.some((segment) => config.ignoredDirs.includes(segment));\n};\n\nconst walk = async (\n relativeDir: string,\n config: ProjectContextConfig,\n results: string[],\n limit: number,\n) => {\n if (results.length >= limit || isIgnoredPath(relativeDir, config)) {\n return;\n }\n\n const absoluteDir = path.join(config.rootDir, relativeDir);\n const entries = await fs.readdir(absoluteDir, { withFileTypes: true });\n\n for (const entry of entries) {\n if (results.length >= limit) {\n return;\n }\n\n const relativePath = normalizeRelativePath(path.join(relativeDir, entry.name));\n\n if (isIgnoredPath(relativePath, config)) {\n continue;\n }\n\n if (entry.isDirectory()) {\n await walk(relativePath, config, results, limit);\n continue;\n }\n\n if (entry.isFile()) {\n results.push(relativePath);\n }\n }\n};\n\nconst listProjectFiles = async (\n config: ProjectContextConfig,\n query?: string,\n limit = 80,\n) => {\n const results: string[] = [];\n\n for (const allowedDir of config.allowedDirs) {\n const absoluteDir = path.join(config.rootDir, allowedDir);\n\n try {\n const stat = await fs.stat(absoluteDir);\n\n if (stat.isDirectory()) {\n await walk(allowedDir, config, results, limit);\n }\n } catch {\n continue;\n }\n }\n\n if (!query) {\n return results;\n }\n\n return results.filter((filePath) => filePath.toLowerCase().includes(query.toLowerCase()));\n};\n\nconst readProjectFile = async (\n config: ProjectContextConfig,\n filePath: string,\n maxBytes = config.maxReadBytes,\n) => {\n const absolutePath = path.join(config.rootDir, normalizeRelativePath(filePath));\n const content = await fs.readFile(absolutePath, \"utf-8\");\n\n return {\n path: filePath,\n content: content.slice(0, maxBytes),\n };\n};\n\nconst searchWithRg = async (\n config: ProjectContextConfig,\n query: string,\n limit: number,\n) => {\n const { stdout } = await execFileAsync(\n \"rg\",\n [\n \"--line-number\",\n \"--fixed-strings\",\n \"--color\",\n \"never\",\n \"--glob\",\n \"!node_modules/**\",\n \"--glob\",\n \"!.next/**\",\n \"--glob\",\n \"!.git/**\",\n query,\n ...config.allowedDirs,\n ],\n {\n cwd: config.rootDir,\n maxBuffer: 1024 * 1024,\n },\n );\n\n return stdout\n .split(\"\\n\")\n .filter(Boolean)\n .slice(0, limit)\n .map((line) => {\n const [filePath, lineNumber, ...rest] = line.split(\":\");\n return {\n path: filePath,\n line: Number(lineNumber),\n text: rest.join(\":\").trim(),\n };\n });\n};\n\nconst searchWithNode = async (\n config: ProjectContextConfig,\n query: string,\n limit: number,\n) => {\n const files = await listProjectFiles(config, undefined, 500);\n const results: SearchResult[] = [];\n\n for (const filePath of files) {\n if (results.length >= limit) {\n break;\n }\n\n try {\n const file = await readProjectFile(config, filePath, 100_000);\n const lines = file.content.split(\"\\n\");\n\n lines.forEach((line, index) => {\n if (results.length < limit && line.includes(query)) {\n results.push({\n path: filePath,\n line: index + 1,\n text: line.trim(),\n });\n }\n });\n } catch {\n continue;\n }\n }\n\n return results;\n};\n\nconst searchProjectText = async (\n config: ProjectContextConfig,\n query: string,\n limit = config.maxSearchResults,\n) => {\n if (!query.trim()) {\n return [];\n }\n\n try {\n return await searchWithRg(config, query, limit);\n } catch {\n return searchWithNode(config, query, limit);\n }\n};\n\nconst findComponentByText = async (\n config: ProjectContextConfig,\n text: string,\n limit = 8,\n) => {\n const normalized = text.replace(/\\s+/g, \" \").trim();\n const candidates = [\n normalized,\n normalized.slice(0, 120),\n ...normalized.split(/[.!?]\\s+/).filter((item) => item.length > 12),\n ];\n\n for (const candidate of candidates) {\n const results = await searchProjectText(config, candidate, limit);\n\n if (results.length) {\n return {\n query: candidate,\n results,\n };\n }\n }\n\n return {\n query: normalized,\n results: [],\n };\n};\n\nconst readJsonFile = async (rootDir: string, filePath: string) => {\n try {\n const content = await fs.readFile(path.join(rootDir, filePath), \"utf-8\");\n return JSON.parse(content) as Record<string, unknown>;\n } catch {\n return null;\n }\n};\n\nconst getProjectContext = async (config: ProjectContextConfig) => {\n const packageJson = await readJsonFile(config.rootDir, \"package.json\");\n const tsconfig = await readJsonFile(config.rootDir, \"tsconfig.json\");\n\n return {\n rootDir: config.rootDir,\n allowedDirs: config.allowedDirs,\n packageName: packageJson?.name,\n scripts: packageJson?.scripts,\n dependencies: packageJson?.dependencies,\n devDependencies: packageJson?.devDependencies,\n tsconfigPaths: (tsconfig?.compilerOptions as { paths?: unknown } | undefined)\n ?.paths,\n };\n};\n\nexport const buildCopilotProjectContext = async (\n rootDir: string,\n allowedDirs: string[],\n params: CopilotContextParams,\n) => {\n const config: ProjectContextConfig = {\n rootDir,\n allowedDirs,\n ignoredDirs: [\".git\", \".next\", \"node_modules\", \"coverage\", \"public\", \"dist\", \"build\"],\n maxReadBytes: 30_000,\n maxSearchResults: 20,\n };\n\n const project = await getProjectContext(config);\n const selectedText = params.selectedText?.trim();\n const routeQuery = params.route?.startsWith(\"/\")\n ? `app${params.route === \"/\" ? \"\" : params.route}/page.tsx`\n : params.route;\n\n const textMatches = selectedText\n ? await findComponentByText(config, selectedText, 8)\n : { query: \"\", results: [] };\n const routeMatches = routeQuery\n ? await searchProjectText(config, routeQuery, 5)\n : [];\n\n return JSON.stringify(\n {\n project,\n requestContext: {\n route: params.route,\n fileHints: params.fileHints,\n },\n selectedTextLookup: textMatches,\n routeLookup: routeMatches,\n guidance:\n \"이 컨텍스트는 로컬 프로젝트에서 수집한 실제 코드 검색 결과입니다. 수정 제안은 이 결과를 우선 근거로 삼고 path/oldText/newText 기반으로 작성하세요.\",\n },\n null,\n 2,\n );\n};\n","import { execFile } from \"node:child_process\";\nimport { promises as fs } from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { promisify } from \"node:util\";\n\nconst execFileAsync = promisify(execFile);\n\nconst getChangedFiles = (patchPreview: string) => {\n const regex = /^\\+\\+\\+ b\\/(.+)$/gm;\n const changedFiles = new Set<string>();\n\n let match = regex.exec(patchPreview);\n while (match) {\n changedFiles.add(match[1]);\n match = regex.exec(patchPreview);\n }\n\n return [...changedFiles];\n};\n\ninterface TextReplacement {\n path: string;\n oldText: string;\n newText: string;\n}\n\nexport const normalizeUnifiedPatch = (patchPreview: string) => {\n const withoutFence = patchPreview\n .replace(/^```(?:diff|patch)?\\s*/i, \"\")\n .replace(/\\s*```$/i, \"\")\n .trim();\n const diffStartIndex = withoutFence.indexOf(\"diff --git \");\n\n return diffStartIndex >= 0\n ? withoutFence.slice(diffStartIndex).trim()\n : withoutFence;\n};\n\nexport const validatePatchPaths = (\n patchPreview: string,\n validatePath: (filePath: string) => string,\n) => {\n const changedFiles = getChangedFiles(patchPreview);\n\n if (!changedFiles.length) {\n throw new Error(\"패치에서 변경 파일을 찾을 수 없습니다.\");\n }\n\n changedFiles.forEach(validatePath);\n\n return changedFiles;\n};\n\nexport const checkUnifiedPatch = async (patchPreview: string, cwd: string) => {\n const tempFilePath = path.join(\n os.tmpdir(),\n `dev-copilot-check-${Date.now()}-${Math.random().toString(16).slice(2)}.patch`,\n );\n\n await fs.writeFile(tempFilePath, patchPreview, \"utf-8\");\n\n try {\n await execFileAsync(\"git\", [\"apply\", \"--check\", tempFilePath], { cwd });\n } catch (error) {\n const detail =\n error instanceof Error && \"stderr\" in error\n ? String((error as Error & { stderr?: unknown }).stderr)\n : error instanceof Error\n ? error.message\n : \"알 수 없는 patch 검증 오류\";\n\n throw new Error(`적용 가능한 diff를 생성하지 못했습니다: ${detail.trim()}`);\n } finally {\n await fs.rm(tempFilePath, { force: true });\n }\n};\n\nconst createGitPatch = async (\n filePath: string,\n oldContent: string,\n newContent: string,\n) => {\n const tempDir = path.join(\n os.tmpdir(),\n `dev-copilot-diff-${Date.now()}-${Math.random().toString(16).slice(2)}`,\n );\n const oldFilePath = path.join(tempDir, \"old\", filePath);\n const newFilePath = path.join(tempDir, \"new\", filePath);\n\n await fs.mkdir(path.dirname(oldFilePath), { recursive: true });\n await fs.mkdir(path.dirname(newFilePath), { recursive: true });\n await fs.writeFile(oldFilePath, oldContent, \"utf-8\");\n await fs.writeFile(newFilePath, newContent, \"utf-8\");\n\n try {\n const { stdout } = await execFileAsync(\n \"git\",\n [\n \"diff\",\n \"--no-index\",\n \"--no-ext-diff\",\n \"--src-prefix=a/\",\n \"--dst-prefix=b/\",\n \"--\",\n path.join(\"old\", filePath),\n path.join(\"new\", filePath),\n ],\n { cwd: tempDir },\n ).catch((error: unknown) => {\n const typedError = error as { stdout?: string; code?: number };\n\n if (typedError.code === 1 && typedError.stdout) {\n return { stdout: typedError.stdout };\n }\n\n throw error;\n });\n\n return stdout\n .replaceAll(`a/old/${filePath}`, `a/${filePath}`)\n .replaceAll(`b/new/${filePath}`, `b/${filePath}`);\n } finally {\n await fs.rm(tempDir, { force: true, recursive: true });\n }\n};\n\nexport const createPatchFromTextReplacements = async (\n replacements: TextReplacement[],\n cwd: string,\n validatePath: (filePath: string) => string,\n) => {\n const byPath = new Map<string, TextReplacement[]>();\n\n for (const replacement of replacements) {\n const normalizedPath = validatePath(replacement.path);\n const current = byPath.get(normalizedPath) ?? [];\n current.push(replacement);\n byPath.set(normalizedPath, current);\n }\n\n const patches: string[] = [];\n\n for (const [filePath, fileReplacements] of byPath.entries()) {\n const absolutePath = path.join(cwd, filePath);\n const oldContent = await fs.readFile(absolutePath, \"utf-8\");\n let newContent = oldContent;\n\n for (const replacement of fileReplacements) {\n if (!newContent.includes(replacement.oldText)) {\n throw new Error(`원문을 파일에서 찾을 수 없습니다: ${filePath}`);\n }\n\n newContent = newContent.replace(replacement.oldText, replacement.newText);\n }\n\n if (newContent === oldContent) {\n continue;\n }\n\n patches.push(await createGitPatch(filePath, oldContent, newContent));\n }\n\n const patchPreview = patches.join(\"\\n\");\n\n if (!patchPreview.trim()) {\n throw new Error(\"변경할 내용이 없습니다.\");\n }\n\n return patchPreview;\n};\n\nexport const applyUnifiedPatch = async (\n patchPreview: string,\n cwd: string,\n actor: string,\n) => {\n const tempFilePath = path.join(\n os.tmpdir(),\n `dev-copilot-${Date.now()}-${Math.random().toString(16).slice(2)}.patch`,\n );\n\n await fs.writeFile(tempFilePath, patchPreview, \"utf-8\");\n\n try {\n await execFileAsync(\"git\", [\"apply\", \"--check\", tempFilePath], { cwd });\n await execFileAsync(\"git\", [\"apply\", tempFilePath], { cwd });\n\n const changedFiles = getChangedFiles(patchPreview);\n\n return {\n actor,\n changedFiles,\n };\n } catch (error) {\n const detail =\n error instanceof Error && \"stderr\" in error\n ? String((error as Error & { stderr?: unknown }).stderr)\n : error instanceof Error\n ? error.message\n : \"알 수 없는 patch 적용 오류\";\n\n throw new Error(`patch 적용에 실패했습니다: ${detail.trim()}`);\n } finally {\n await fs.rm(tempFilePath, { force: true });\n }\n};\n","import { randomUUID } from \"node:crypto\";\n\ninterface ProposedPatch {\n patchId: string;\n approvalToken: string;\n patchPreview: string;\n allowedPaths: string[];\n createdAt: number;\n}\n\nconst patchStore = new Map<string, ProposedPatch>();\nconst TTL_MS = 1000 * 60 * 15;\n\nconst pruneExpired = () => {\n const now = Date.now();\n\n for (const [key, value] of patchStore.entries()) {\n if (now - value.createdAt > TTL_MS) {\n patchStore.delete(key);\n }\n }\n};\n\nexport const createProposedPatch = (patchPreview: string, allowedPaths: string[]) => {\n pruneExpired();\n\n const patchId = randomUUID();\n const approvalToken = `approve:${patchId}`;\n\n patchStore.set(patchId, {\n patchId,\n approvalToken,\n patchPreview,\n allowedPaths,\n createdAt: Date.now(),\n });\n\n return {\n patchId,\n approvalToken,\n };\n};\n\nexport const getProposedPatch = (patchId: string, approvalToken: string) => {\n pruneExpired();\n\n const item = patchStore.get(patchId);\n\n if (!item || item.approvalToken !== approvalToken) {\n return null;\n }\n\n return item;\n};\n\nexport const deleteProposedPatch = (patchId: string) => {\n patchStore.delete(patchId);\n};\n","import { createServer, type IncomingMessage, type ServerResponse } from \"node:http\";\n\nimport type {\n CopilotAgent,\n CopilotApplyRequest,\n CopilotApplyResponse,\n CopilotChatRequest,\n CopilotChatResponse,\n} from \"../types\";\nimport type { DevCopilotBridgeConfig } from \"../lib/config\";\nimport { resolveAndValidatePath } from \"../lib/guards\";\nimport { resolveAgentAdapter } from \"../internal/agents\";\nimport type { AgentAdapter } from \"../internal/agents\";\nimport { buildCopilotProjectContext } from \"../internal/project-context\";\nimport {\n applyUnifiedPatch,\n checkUnifiedPatch,\n createPatchFromTextReplacements,\n validatePatchPaths,\n} from \"../lib/patch\";\nimport {\n createProposedPatch,\n deleteProposedPatch,\n getProposedPatch,\n} from \"../lib/store\";\n\ntype ParsedEditPayload = {\n message?: string;\n patchPreview?: string;\n changes?: Array<{\n path: string;\n oldText: string;\n newText: string;\n }>;\n warnings?: string[];\n};\n\nconst createCorsHeaders = (config: DevCopilotBridgeConfig) => ({\n \"Access-Control-Allow-Origin\": config.corsOrigin,\n \"Access-Control-Allow-Methods\": \"GET,POST,OPTIONS\",\n \"Access-Control-Allow-Headers\": \"Content-Type\",\n \"Content-Type\": \"application/json; charset=utf-8\",\n});\n\nconst sendJson = (\n response: ServerResponse,\n config: DevCopilotBridgeConfig,\n statusCode: number,\n payload: unknown,\n) => {\n response.writeHead(statusCode, createCorsHeaders(config));\n response.end(JSON.stringify(payload));\n};\n\nconst readJsonBody = async <T>(request: IncomingMessage) => {\n const chunks: Buffer[] = [];\n\n for await (const chunk of request) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));\n }\n\n const raw = Buffer.concat(chunks).toString(\"utf-8\");\n return JSON.parse(raw) as T;\n};\n\nconst createProjectContext = async (\n config: DevCopilotBridgeConfig,\n payload: CopilotChatRequest,\n allowedPaths: string[],\n) => {\n return buildCopilotProjectContext(\n config.rootDir,\n allowedPaths,\n {\n selectedText: payload.selectedText,\n route: payload.context?.route,\n fileHints: payload.context?.fileHints,\n },\n );\n};\n\nconst sanitizeAllowedPaths = (fileHints?: string[]) => {\n if (!fileHints?.length) {\n return null;\n }\n\n const sanitized = fileHints\n .map((value) => value.trim())\n .filter(Boolean)\n .filter((value) => value === \".\" || value === \"*\" || /^[A-Za-z0-9._-]+$/.test(value));\n\n return sanitized.length ? Array.from(new Set(sanitized)) : null;\n};\n\nconst toPatchSummary = (changedFiles: string[]) => {\n return changedFiles.length === 1\n ? \"1개 파일에 수정이 반영되었습니다.\"\n : `${changedFiles.length}개 파일에 수정이 반영되었습니다.`;\n};\n\nconst toEditPayload = (payload: ParsedEditPayload): ParsedEditPayload => {\n return payload;\n};\n\nconst isCopilotAgent = (value: string | null): value is CopilotAgent => {\n return value === \"codex\" || value === \"claude\";\n};\n\nconst resolveRequestedAgent = (\n config: DevCopilotBridgeConfig,\n requestAgent?: CopilotAgent,\n queryAgent?: string | null,\n): CopilotAgent => {\n if (requestAgent) {\n return requestAgent;\n }\n\n if (isCopilotAgent(queryAgent)) {\n return queryAgent;\n }\n\n return config.agent;\n};\n\nexport const createDevCopilotBridgeServer = (config: DevCopilotBridgeConfig) => {\n return createDevCopilotBridgeServerWithDependencies(config, { resolveAdapter: resolveAgentAdapter });\n};\n\nexport const createDevCopilotBridgeServerWithDependencies = (\n config: DevCopilotBridgeConfig,\n dependencies: {\n resolveAdapter: (agent: CopilotAgent) => AgentAdapter;\n },\n) => {\n const server = createServer(async (request, response) => {\n const method = request.method ?? \"GET\";\n const url = new URL(request.url ?? \"/\", `http://${config.host}:${config.port}`);\n\n if (method === \"OPTIONS\") {\n response.writeHead(204, createCorsHeaders(config));\n response.end();\n return;\n }\n\n try {\n if (method === \"GET\" && url.pathname === \"/status\") {\n const agent = resolveRequestedAgent(config, undefined, url.searchParams.get(\"agent\"));\n const adapter = dependencies.resolveAdapter(agent);\n\n sendJson(response, config, 200, await adapter.getStatus(config.rootDir));\n return;\n }\n\n if (method === \"POST\" && url.pathname === \"/chat\") {\n const payload = await readJsonBody<CopilotChatRequest>(request);\n const effectiveAllowedPaths =\n sanitizeAllowedPaths(payload.context?.fileHints) ?? config.allowedPaths;\n const agent = resolveRequestedAgent(config, payload.context?.agent);\n const adapter = dependencies.resolveAdapter(agent);\n\n if (!payload.prompt?.trim()) {\n sendJson(response, config, 400, { error: \"프롬프트를 입력해 주세요.\" });\n return;\n }\n\n const agentResponse = await adapter.run({\n selectedText: payload.selectedText ?? \"\",\n prompt: payload.prompt,\n mode: payload.mode,\n route: payload.context?.route,\n fileHints: payload.context?.fileHints,\n previousResponse: payload.context?.previousResponse,\n projectContext: await createProjectContext(config, payload, effectiveAllowedPaths),\n cwd: config.rootDir,\n });\n\n if (payload.mode === \"answer\") {\n const answerResponse: CopilotChatResponse = {\n message: agentResponse.message,\n warnings: agentResponse.warnings,\n };\n\n sendJson(response, config, 200, answerResponse);\n return;\n }\n\n const parsed = toEditPayload(agentResponse);\n let patchPreview = \"\";\n\n try {\n if (parsed.changes?.length) {\n patchPreview = await createPatchFromTextReplacements(\n parsed.changes,\n config.rootDir,\n (filePath) =>\n resolveAndValidatePath(filePath, effectiveAllowedPaths, config.rootDir),\n );\n }\n } catch (error) {\n const failedResponse: CopilotChatResponse = {\n message:\n parsed.message ??\n \"에이전트가 수정안을 만들었지만 실제 파일 내용과 매칭하지 못했습니다.\",\n warnings: [\n ...(parsed.warnings ?? []),\n error instanceof Error ? error.message : \"수정안 생성에 실패했습니다.\",\n ],\n };\n\n sendJson(response, config, 200, failedResponse);\n return;\n }\n\n if (!patchPreview) {\n const emptyPatchResponse: CopilotChatResponse = {\n message: parsed.message ?? \"패치 제안을 생성하지 못했습니다.\",\n warnings: [\n ...(parsed.warnings ?? []),\n \"적용 가능한 변경 목록이 없어 패치 미리보기와 적용 버튼을 만들 수 없습니다.\",\n ],\n };\n\n sendJson(response, config, 200, emptyPatchResponse);\n return;\n }\n\n try {\n validatePatchPaths(patchPreview, (filePath) =>\n resolveAndValidatePath(filePath, effectiveAllowedPaths, config.rootDir),\n );\n await checkUnifiedPatch(patchPreview, config.rootDir);\n } catch (error) {\n const invalidPatchResponse: CopilotChatResponse = {\n message:\n parsed.message ??\n \"에이전트가 수정안을 만들었지만 적용 가능한 diff 형식이 아닙니다.\",\n patchPreview,\n warnings: [\n ...(parsed.warnings ?? []),\n error instanceof Error ? error.message : \"patch 검증에 실패했습니다.\",\n ],\n };\n\n sendJson(response, config, 200, invalidPatchResponse);\n return;\n }\n\n const patch = createProposedPatch(patchPreview, effectiveAllowedPaths);\n const chatResponse: CopilotChatResponse = {\n message: parsed.message ?? \"에이전트가 패치 미리보기를 생성했습니다.\",\n patchPreview,\n patchId: patch.patchId,\n warnings: parsed.warnings ?? [],\n };\n\n sendJson(response, config, 200, chatResponse);\n return;\n }\n\n if (method === \"POST\" && url.pathname === \"/apply\") {\n const payload = await readJsonBody<CopilotApplyRequest>(request);\n\n if (!payload.patchId || !payload.approvalToken) {\n sendJson(response, config, 400, {\n error: \"patchId와 approvalToken이 필요합니다.\",\n });\n return;\n }\n\n const proposedPatch = getProposedPatch(payload.patchId, payload.approvalToken);\n\n if (!proposedPatch) {\n sendJson(response, config, 400, {\n error: \"유효하지 않거나 만료된 patchId입니다.\",\n });\n return;\n }\n\n validatePatchPaths(proposedPatch.patchPreview, (filePath) =>\n resolveAndValidatePath(\n filePath,\n proposedPatch.allowedPaths?.length\n ? proposedPatch.allowedPaths\n : config.allowedPaths,\n config.rootDir,\n ),\n );\n\n const result = await applyUnifiedPatch(\n proposedPatch.patchPreview,\n config.rootDir,\n \"local-codex-bridge\",\n );\n deleteProposedPatch(payload.patchId);\n\n const applyResponse: CopilotApplyResponse = {\n applied: true,\n changedFiles: result.changedFiles,\n summary: toPatchSummary(result.changedFiles),\n };\n\n sendJson(response, config, 200, applyResponse);\n return;\n }\n\n sendJson(response, config, 404, { error: \"지원하지 않는 경로입니다.\" });\n } catch (error) {\n sendJson(response, config, 500, {\n error:\n error instanceof Error\n ? error.message\n : \"브리지 서버 처리 중 오류가 발생했습니다.\",\n });\n }\n });\n\n return server;\n};\n","import { createDevCopilotBridgeConfig } from \"../lib/config\";\nimport { createDevCopilotBridgeServer } from \"../server/http-server\";\nimport type { CopilotAgent } from \"../types\";\n\nconst resolveAgent = (value: string | undefined): CopilotAgent => {\n if (value === \"claude\") {\n return \"claude\";\n }\n\n return \"codex\";\n};\n\nexport const runDevCopilotBridgeCli = async (argv: string[]) => {\n const portFlagIndex = argv.findIndex((value) => value === \"-p\");\n const portFlagValue =\n portFlagIndex >= 0 ? argv[portFlagIndex + 1] : undefined;\n const positionalPort = argv.find((value) => /^\\d+$/.test(value));\n const resolvedPort = portFlagValue\n ? Number(portFlagValue)\n : positionalPort\n ? Number(positionalPort)\n : Number(process.env.DEV_COPILOT_BRIDGE_PORT ?? 3339);\n const positionalAgent = argv.find((value) => value === \"codex\" || value === \"claude\");\n\n const config = createDevCopilotBridgeConfig({\n rootDir: process.cwd(),\n host: process.env.DEV_COPILOT_BRIDGE_HOST,\n port: Number.isFinite(resolvedPort) ? resolvedPort : 3339,\n corsOrigin: process.env.DEV_COPILOT_BRIDGE_CORS_ORIGIN ?? \"*\",\n agent: resolveAgent(positionalAgent),\n allowedPaths: (process.env.DEV_COPILOT_ALLOWED_PATHS ??\n \"app,src,widgets,features,entities,shared,components\")\n .split(\",\")\n .map((value) => value.trim())\n .filter(Boolean),\n });\n\n const server = createDevCopilotBridgeServer(config);\n\n await new Promise<void>((resolve) => {\n server.listen(config.port, config.host, () => {\n process.stdout.write(\n `[dev-copilot-bridge] listening on http://${config.host}:${config.port}\\n`,\n );\n resolve();\n });\n });\n};\n","#!/usr/bin/env node\n\nimport { runDevCopilotBridgeCli } from \"../bridge/cli/run-http\";\n\nrunDevCopilotBridgeCli(process.argv.slice(2)).catch((error) => {\n const message = error instanceof Error ? error.message : String(error);\n process.stderr.write(`[dev-copilot-bridge] ${message}\\n`);\n process.exit(1);\n});\n"],"mappings":";;;;;;;;;;AAWA,MAAM,wBAAwB;CAC5B;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AAED,MAAa,gCACX,WAC2B;AAC3B,QAAO;EACL,SAAS,OAAO;EAChB,MAAM,OAAO,QAAQ;EACrB,MAAM,OAAO,QAAQ;EACrB,YAAY,OAAO,cAAc;EACjC,OAAO,OAAO,SAAS;EACvB,cAAc,OAAO,gBAAgB;EACtC;;;;;AC3BH,MAAa,0BACX,UACA,cACA,YACG;AACH,KAAI,CAAC,SACH,OAAM,IAAI,MAAM,oBAAoB;CAGtC,IAAI,kBAAkB,SAAS,WAAW,MAAM,IAAI;AAEpD,KAAI,SAAS;EACX,MAAM,oBAAoB,QAAQ,WAAW,MAAM,IAAI,CAAC,QAAQ,QAAQ,GAAG;AAE3E,MAAI,gBAAgB,WAAW,GAAG,kBAAkB,GAAG,CACrD,mBAAkB,gBAAgB,MAAM,kBAAkB,SAAS,EAAE;;AAIzE,KACE,KAAK,WAAW,gBAAgB,IAChC,gBAAgB,SAAS,KAAK,CAE9B,OAAM,IAAI,MAAM,oBAAoB;CAGtC,MAAM,aAAa;CACnB,MAAM,eAAe,WAAW,MAAM,IAAI,CAAC;AAG3C,KAAI,EAFa,aAAa,SAAS,IAAI,IAAI,aAAa,SAAS,IAAI,KAExD,CAAC,aAAa,SAAS,aAAa,CACnD,OAAM,IAAI,MAAM,2BAA2B,eAAe;AAG5D,QAAO;;;;;AC1BT,MAAa,oBAAoB,YAAgC;AAC/D,KAAI,QAAQ,SAAS,SACnB,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA,kBAAkB,QAAQ,SAAS;EACnC,mBAAmB,QAAQ,gBAAgB;EAC3C,iBAAiB,QAAQ;EACzB,0BAA0B,QAAQ,oBAAoB;EACtD,qBAAqB,QAAQ,kBAAkB;EAChD,CAAC,KAAK,KAAK;AAGd,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,kBAAkB,QAAQ,SAAS;EACnC,wBAAwB,QAAQ,aAAa,EAAE,EAAE,KAAK,KAAK,IAAI;EAC/D,mBAAmB,QAAQ,gBAAgB;EAC3C,iBAAiB,QAAQ;EACzB,0BAA0B,QAAQ,oBAAoB;EACtD,qBAAqB,QAAQ,kBAAkB;EAChD,CAAC,KAAK,KAAK;;;;;ACnCd,MAAMA,kBAAgB,UAAU,SAAS;AASzC,MAAM,qBACJ;AAEF,MAAM,uBAAuB,UAAmB;CAC9C,MAAM,gBAAgB,SAAS,OAAO,UAAU,WAAY,QAAoC;CAChG,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;CACtE,MAAM,SAAS,OAAO,eAAe,WAAW,WAAW,cAAc,SAAS;CAClF,MAAM,SAAS,OAAO,eAAe,WAAW,WAAW,cAAc,SAAS;AAMlF,QAAO;EACL;EACA;EACA;EACA,QATa;GAAC;GAAS;GAAQ;GAAO,CACrC,KAAK,SAAS,KAAK,MAAM,CAAC,CAC1B,OAAO,QAAQ,CACf,KAAK,KAAK;EAOZ;;AAGH,MAAM,wBAAwB,UAAmB;CAC/C,MAAM,EAAE,SAAS,WAAW,oBAAoB,MAAM;AAEtD,KAAI,mBAAmB,KAAK,OAAO,CACjC,QAAO;AAGT,KAAI,SAAS,KAAK,OAAO,CACvB,QAAO;AAGT,QAAO;;AAGT,MAAM,mBAAmB,UAAkC;AACzD,KAAI,OAAO,UAAU,UAAU;EAC7B,MAAM,OAAO,MAAM,MAAM;AACzB,SAAO,OAAO,OAAO;;AAGvB,KAAI,MAAM,QAAQ,MAAM,EAAE;AACxB,OAAK,MAAM,QAAQ,OAAO;GACxB,MAAM,QAAQ,gBAAgB,KAAK;AACnC,OAAI,MACF,QAAO;;AAGX,SAAO;;AAGT,KAAI,SAAS,OAAO,UAAU,UAAU;EACtC,MAAM,SAAS;AAGf,OAAK,MAAM,OAFW;GAAC;GAAU;GAAW;GAAU;GAAQ;GAAU,CAGtE,KAAI,OAAO,QAAQ;GACjB,MAAM,QAAQ,gBAAgB,OAAO,KAAK;AAC1C,OAAI,MACF,QAAO;;AAKb,OAAK,MAAM,eAAe,OAAO,OAAO,OAAO,EAAE;GAC/C,MAAM,QAAQ,gBAAgB,YAAY;AAC1C,OAAI,MACF,QAAO;;;AAKb,QAAO;;AAGT,MAAM,yBAAyB,QAAgB;CAC7C,MAAM,SAAS,KAAK,MAAM,IAAI;AAG9B,QAAO;EACL;EACA,MAJW,gBAAgB,OAAO,IAAI;EAKvC;;AAGH,MAAM,2BAA2B,QAAqC;CACpE,MAAM,EAAE,SAAS,sBAAsB,IAAI;CAC3C,MAAM,SAAS,KAAK,MAAM,KAAK;AAE/B,QAAO;EACL,SAAS,OAAO;EAChB,cAAc,OAAO;EACrB,SAAS,OAAO;EAChB,UAAU,OAAO,YAAY,EAAE;EAChC;;AAGH,MAAa,gBAA8B;CACzC,OAAO;CACP,MAAM,IAAI,SAA2D;EACnE,MAAM,SAAS,iBAAiB,QAAQ;EACxC,MAAM,OAAO;GACX;GACA;GACA;GACA,GAAI,QAAQ,SAAS,SACjB,CACE,iBACA,kfACD,GACD,EAAE;GACN;GACD;AAED,MAAI;GACF,MAAM,EAAE,WAAW,MAAMA,gBAAc,UAAU,MAAM;IACrD,KAAK,QAAQ;IACb,WAAW,OAAO,OAAO;IACzB,SAAS,OAAO,QAAQ,IAAI,gCAAgC,KAAQ;IACpE,KAAK;KACH,GAAG,QAAQ;KACX,UAAU;KACX;IACF,CAAC;AAEF,OAAI,QAAQ,SAAS,UAAU;IAC7B,MAAM,EAAE,SAAS,sBAAsB,OAAO;AAE9C,WAAO;KACL,SAAS;KACT,UAAU,EAAE;KACb;;AAGH,UAAO,wBAAwB,OAAO;WAC/B,OAAO;AACd,SAAM,IAAI,MAAM,qBAAqB,MAAM,CAAC;;;CAGhD,MAAM,UAAU,KAAmC;AACjD,MAAI;AACF,SAAMA,gBAAc,UAAU;IAAC;IAAM;IAAmB;IAAQ;IAAK,EAAE;IACrE;IACA,SAAS;IACT,WAAW,OAAO;IACnB,CAAC;AAEF,UAAO;IACL,WAAW;IACX,eAAe;IACf,OAAO;IACP,SAAS;IACV;WACM,OAAO;GACd,MAAM,EAAE,QAAQ,YAAY,oBAAoB,MAAM;GACtD,MAAM,cAAc,SAAS,KAAK,OAAO;GACzC,MAAM,YAAY,mBAAmB,KAAK,OAAO;AAEjD,UAAO;IACL,WAAW,CAAC;IACZ,eAAe;IACf,OAAO;IACP,SAAS,cACL,2BACA,YACE,4BACA;IACN,cAAc,cAAc,SAAY;IACzC;;;CAGN;;;;ACjLD,MAAMC,kBAAgB,UAAU,SAAS;AACzC,MAAM,wBAAwB;CAC5B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;AACD,MAAM,0BAA0B;CAC9B,MAAM;CACN,YAAY;EACV,SAAS,EAAE,MAAM,UAAU;EAC3B,UAAU;GACR,MAAM;GACN,OAAO,EAAE,MAAM,UAAU;GAC1B;EACD,SAAS;GACP,MAAM;GACN,OAAO;IACL,MAAM;IACN,YAAY;KACV,MAAM,EAAE,MAAM,UAAU;KACxB,SAAS,EAAE,MAAM,UAAU;KAC3B,SAAS,EAAE,MAAM,UAAU;KAC5B;IACD,UAAU;KAAC;KAAQ;KAAW;KAAU;IACxC,sBAAsB;IACvB;GACF;EACF;CACD,UAAU;EAAC;EAAW;EAAY;EAAU;CAC5C,sBAAsB;CACvB;AAED,MAAM,uBAAuB,cAA2C;AACtE,QAAO;EACL,SAAS,UAAU,MAAM;EACzB,UAAU,EAAE;EACb;;AAGH,MAAM,oBAAoB,OAAO,eAAuB;CACtD,MAAM,UAAU,MAAMC,SAAG,SAAS,YAAY,QAAQ;CACtD,MAAM,SAAS,KAAK,MAAM,QAAQ;AAElC,QAAO;EACL,SAAS,OAAO;EAChB,cAAc,OAAO;EACrB,SAAS,OAAO;EAChB,UAAU,OAAO,YAAY,EAAE;EAChC;;AAGH,MAAM,kCAAkC,iBAAyB;CAC/D,MAAM,aAAa,aAAa,aAAa;AAE7C,KAAI,sCAAsC,KAAK,WAAW,CACxD,QAAO;AAGT,QAAO,uBAAuB,KAAK,WAAW;;AAGhD,MAAM,uBAAuB,UAAmB;CAC9C,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AAEtE,KAAI,4DAA4D,KAAK,QAAQ,CAC3E,QAAO;AAGT,KAAI,SAAS,KAAK,QAAQ,CACxB,QAAO;AAGT,QAAO;;AAGT,MAAM,oBAAoB,OAAO,QAAgB;CAC/C,MAAM,aAAa,KAAK,KACtB,GAAG,QAAQ,EACX,4BAA4B,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,EAAE,CAAC,MAC/E;AAED,KAAI;EACF,MAAM,EAAE,QAAQ,WAAW,MAAMD,gBAC/B,SACA;GACE,GAAG;GACH;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACA;GACD,EACD;GACE;GACA,SAAS;GACT,WAAW,OAAO;GAClB,KAAK;IACH,GAAG,QAAQ;IACX,UAAU;IACX;GACF,CACF;AAID,SAHe,GAAG,OAAO,IAAI,SACR,MAAM,mBAAmB,GAE/B,IAAI,MAAM;SACnB;AACN;WACQ;AACR,QAAMC,SAAG,GAAG,YAAY,EAAE,OAAO,MAAM,CAAC;;;AAI5C,MAAa,eAA6B;CACxC,OAAO;CACP,MAAM,IAAI,SAA2D;EACnE,MAAM,aAAa,KAAK,KACtB,GAAG,QAAQ,EACX,qBAAqB,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,EAAE,CAAC,OACxE;EACD,MAAM,aAAa,KAAK,KACtB,GAAG,QAAQ,EACX,4BAA4B,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,EAAE,CAAC,OAC/E;AAED,QAAMA,SAAG,UACP,YACA,KAAK,UAAU,wBAAwB,EACvC,QACD;EAED,MAAM,SAAS,iBAAiB,QAAQ;EACxC,MAAM,OAAO;GACX,GAAG;GACH;GACA;GACA,QAAQ;GACR;GACA;GACA;GACA;GACA;GACA,GAAI,QAAQ,SAAS,SAAS,CAAC,mBAAmB,WAAW,GAAG,EAAE;GAClE;GACD;AAED,MAAI;GACF,MAAM,EAAE,WAAW,MAAMD,gBAAc,SAAS,MAAM;IACpD,KAAK,QAAQ;IACb,WAAW,OAAO,OAAO;IACzB,SAAS,OAAO,QAAQ,IAAI,gCAAgC,KAAQ;IACpE,KAAK;KACH,GAAG,QAAQ;KACX,UAAU;KACX;IACF,CAAC;AAEF,OAAI,QAAQ,SAAS,SACnB,KAAI;AAEF,WAAO,oBADQ,MAAMC,SAAG,SAAS,YAAY,QAAQ,CACnB;WAC5B;AACN,WAAO,oBAAoB,OAAO;;AAItC,UAAO,MAAM,kBAAkB,WAAW;WACnC,OAAO;AACd,SAAM,IAAI,MAAM,oBAAoB,MAAM,CAAC;YACnC;AACR,SAAMA,SAAG,GAAG,YAAY,EAAE,OAAO,MAAM,CAAC;AACxC,SAAMA,SAAG,GAAG,YAAY,EAAE,OAAO,MAAM,CAAC;;;CAG5C,MAAM,UAAU,KAAmC;AACjD,MAAI;GACF,MAAM,EAAE,QAAQ,WAAW,MAAMD,gBAAc,SAAS,CAAC,SAAS,SAAS,EAAE;IAC3E;IACA,SAAS;IACT,WAAW,OAAO;IACnB,CAAC;GACF,MAAM,SAAS,GAAG,OAAO,IAAI,SAAS,MAAM;GAC5C,MAAM,gBAAgB,+BAA+B,OAAO;GAC5D,MAAM,QAAQ,gBAAgB,MAAM,kBAAkB,IAAI,GAAG;AAE7D,UAAO;IACL,WAAW;IACX;IACA,OAAO;IACP,SAAS,gBACL,UAAU,2BACV,UAAU;IACd;IACA,cAAc,gBAAgB,SAAY;IAC3C;WACM,OAAO;GACd,MAAM,UACJ,iBAAiB,QAAQ,MAAM,UAAU;AAE3C,UAAO;IACL,WAAW,CAAC,QAAQ,SAAS,SAAS;IACtC,eAAe;IACf,OAAO;IACP,SAAS,QAAQ,SAAS,SAAS,GAC/B,0BACA;IACJ,cAAc,QAAQ,SAAS,SAAS,GAAG,SAAY;IACxD;;;CAGN;;;;ACxOD,MAAM,WAA+C;CACnD,OAAO;CACP,QAAQ;CACT;AAED,MAAa,uBAAuB,UAAsC;AACxE,QAAO,SAAS;;;;;ACNlB,MAAME,kBAAgB,UAAU,SAAS;AAsBzC,MAAM,yBAAyB,aAAqB;AAClD,QAAO,SAAS,WAAW,MAAM,IAAI,CAAC,QAAQ,UAAU,GAAG;;AAG7D,MAAM,iBAAiB,cAAsB,WAAiC;AAE5E,QADiB,sBAAsB,aAAa,CAAC,MAAM,IAAI,CAC/C,MAAM,YAAY,OAAO,YAAY,SAAS,QAAQ,CAAC;;AAGzE,MAAM,OAAO,OACX,aACA,QACA,SACA,UACG;AACH,KAAI,QAAQ,UAAU,SAAS,cAAc,aAAa,OAAO,CAC/D;CAGF,MAAM,cAAc,KAAK,KAAK,OAAO,SAAS,YAAY;CAC1D,MAAM,UAAU,MAAMC,SAAG,QAAQ,aAAa,EAAE,eAAe,MAAM,CAAC;AAEtE,MAAK,MAAM,SAAS,SAAS;AAC3B,MAAI,QAAQ,UAAU,MACpB;EAGF,MAAM,eAAe,sBAAsB,KAAK,KAAK,aAAa,MAAM,KAAK,CAAC;AAE9E,MAAI,cAAc,cAAc,OAAO,CACrC;AAGF,MAAI,MAAM,aAAa,EAAE;AACvB,SAAM,KAAK,cAAc,QAAQ,SAAS,MAAM;AAChD;;AAGF,MAAI,MAAM,QAAQ,CAChB,SAAQ,KAAK,aAAa;;;AAKhC,MAAM,mBAAmB,OACvB,QACA,OACA,QAAQ,OACL;CACH,MAAM,UAAoB,EAAE;AAE5B,MAAK,MAAM,cAAc,OAAO,aAAa;EAC3C,MAAM,cAAc,KAAK,KAAK,OAAO,SAAS,WAAW;AAEzD,MAAI;AAGF,QAFa,MAAMA,SAAG,KAAK,YAAY,EAE9B,aAAa,CACpB,OAAM,KAAK,YAAY,QAAQ,SAAS,MAAM;UAE1C;AACN;;;AAIJ,KAAI,CAAC,MACH,QAAO;AAGT,QAAO,QAAQ,QAAQ,aAAa,SAAS,aAAa,CAAC,SAAS,MAAM,aAAa,CAAC,CAAC;;AAG3F,MAAM,kBAAkB,OACtB,QACA,UACA,WAAW,OAAO,iBACf;CACH,MAAM,eAAe,KAAK,KAAK,OAAO,SAAS,sBAAsB,SAAS,CAAC;AAG/E,QAAO;EACL,MAAM;EACN,UAJc,MAAMA,SAAG,SAAS,cAAc,QAAQ,EAIrC,MAAM,GAAG,SAAS;EACpC;;AAGH,MAAM,eAAe,OACnB,QACA,OACA,UACG;CACH,MAAM,EAAE,WAAW,MAAMD,gBACvB,MACA;EACE;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA,GAAG,OAAO;EACX,EACD;EACE,KAAK,OAAO;EACZ,WAAW,OAAO;EACnB,CACF;AAED,QAAO,OACJ,MAAM,KAAK,CACX,OAAO,QAAQ,CACf,MAAM,GAAG,MAAM,CACf,KAAK,SAAS;EACb,MAAM,CAAC,UAAU,YAAY,GAAG,QAAQ,KAAK,MAAM,IAAI;AACvD,SAAO;GACL,MAAM;GACN,MAAM,OAAO,WAAW;GACxB,MAAM,KAAK,KAAK,IAAI,CAAC,MAAM;GAC5B;GACD;;AAGN,MAAM,iBAAiB,OACrB,QACA,OACA,UACG;CACH,MAAM,QAAQ,MAAM,iBAAiB,QAAQ,QAAW,IAAI;CAC5D,MAAM,UAA0B,EAAE;AAElC,MAAK,MAAM,YAAY,OAAO;AAC5B,MAAI,QAAQ,UAAU,MACpB;AAGF,MAAI;AAIF,IAHa,MAAM,gBAAgB,QAAQ,UAAU,IAAQ,EAC1C,QAAQ,MAAM,KAAK,CAEhC,SAAS,MAAM,UAAU;AAC7B,QAAI,QAAQ,SAAS,SAAS,KAAK,SAAS,MAAM,CAChD,SAAQ,KAAK;KACX,MAAM;KACN,MAAM,QAAQ;KACd,MAAM,KAAK,MAAM;KAClB,CAAC;KAEJ;UACI;AACN;;;AAIJ,QAAO;;AAGT,MAAM,oBAAoB,OACxB,QACA,OACA,QAAQ,OAAO,qBACZ;AACH,KAAI,CAAC,MAAM,MAAM,CACf,QAAO,EAAE;AAGX,KAAI;AACF,SAAO,MAAM,aAAa,QAAQ,OAAO,MAAM;SACzC;AACN,SAAO,eAAe,QAAQ,OAAO,MAAM;;;AAI/C,MAAM,sBAAsB,OAC1B,QACA,MACA,QAAQ,MACL;CACH,MAAM,aAAa,KAAK,QAAQ,QAAQ,IAAI,CAAC,MAAM;CACnD,MAAM,aAAa;EACjB;EACA,WAAW,MAAM,GAAG,IAAI;EACxB,GAAG,WAAW,MAAM,WAAW,CAAC,QAAQ,SAAS,KAAK,SAAS,GAAG;EACnE;AAED,MAAK,MAAM,aAAa,YAAY;EAClC,MAAM,UAAU,MAAM,kBAAkB,QAAQ,WAAW,MAAM;AAEjE,MAAI,QAAQ,OACV,QAAO;GACL,OAAO;GACP;GACD;;AAIL,QAAO;EACL,OAAO;EACP,SAAS,EAAE;EACZ;;AAGH,MAAM,eAAe,OAAO,SAAiB,aAAqB;AAChE,KAAI;EACF,MAAM,UAAU,MAAMC,SAAG,SAAS,KAAK,KAAK,SAAS,SAAS,EAAE,QAAQ;AACxE,SAAO,KAAK,MAAM,QAAQ;SACpB;AACN,SAAO;;;AAIX,MAAM,oBAAoB,OAAO,WAAiC;CAChE,MAAM,cAAc,MAAM,aAAa,OAAO,SAAS,eAAe;CACtE,MAAM,WAAW,MAAM,aAAa,OAAO,SAAS,gBAAgB;AAEpE,QAAO;EACL,SAAS,OAAO;EAChB,aAAa,OAAO;EACpB,aAAa,aAAa;EAC1B,SAAS,aAAa;EACtB,cAAc,aAAa;EAC3B,iBAAiB,aAAa;EAC9B,gBAAgB,UAAU,kBACtB;EACL;;AAGH,MAAa,6BAA6B,OACxC,SACA,aACA,WACG;CACH,MAAM,SAA+B;EACnC;EACA;EACA,aAAa;GAAC;GAAQ;GAAS;GAAgB;GAAY;GAAU;GAAQ;GAAQ;EACrF,cAAc;EACd,kBAAkB;EACnB;CAED,MAAM,UAAU,MAAM,kBAAkB,OAAO;CAC/C,MAAM,eAAe,OAAO,cAAc,MAAM;CAChD,MAAM,aAAa,OAAO,OAAO,WAAW,IAAI,GAC5C,MAAM,OAAO,UAAU,MAAM,KAAK,OAAO,MAAM,aAC/C,OAAO;CAEX,MAAM,cAAc,eAChB,MAAM,oBAAoB,QAAQ,cAAc,EAAE,GAClD;EAAE,OAAO;EAAI,SAAS,EAAE;EAAE;CAC9B,MAAM,eAAe,aACjB,MAAM,kBAAkB,QAAQ,YAAY,EAAE,GAC9C,EAAE;AAEN,QAAO,KAAK,UACV;EACE;EACA,gBAAgB;GACd,OAAO,OAAO;GACd,WAAW,OAAO;GACnB;EACD,oBAAoB;EACpB,aAAa;EACb,UACE;EACH,EACD,MACA,EACD;;;;;ACpSH,MAAM,gBAAgB,UAAU,SAAS;AAEzC,MAAM,mBAAmB,iBAAyB;CAChD,MAAM,QAAQ;CACd,MAAM,+BAAe,IAAI,KAAa;CAEtC,IAAI,QAAQ,MAAM,KAAK,aAAa;AACpC,QAAO,OAAO;AACZ,eAAa,IAAI,MAAM,GAAG;AAC1B,UAAQ,MAAM,KAAK,aAAa;;AAGlC,QAAO,CAAC,GAAG,aAAa;;AAqB1B,MAAa,sBACX,cACA,iBACG;CACH,MAAM,eAAe,gBAAgB,aAAa;AAElD,KAAI,CAAC,aAAa,OAChB,OAAM,IAAI,MAAM,yBAAyB;AAG3C,cAAa,QAAQ,aAAa;AAElC,QAAO;;AAGT,MAAa,oBAAoB,OAAO,cAAsB,QAAgB;CAC5E,MAAM,eAAe,KAAK,KACxB,GAAG,QAAQ,EACX,qBAAqB,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,EAAE,CAAC,QACxE;AAED,OAAMC,SAAG,UAAU,cAAc,cAAc,QAAQ;AAEvD,KAAI;AACF,QAAM,cAAc,OAAO;GAAC;GAAS;GAAW;GAAa,EAAE,EAAE,KAAK,CAAC;UAChE,OAAO;EACd,MAAM,SACJ,iBAAiB,SAAS,YAAY,QAClC,OAAQ,MAAuC,OAAO,GACtD,iBAAiB,QACf,MAAM,UACN;AAER,QAAM,IAAI,MAAM,4BAA4B,OAAO,MAAM,GAAG;WACpD;AACR,QAAMA,SAAG,GAAG,cAAc,EAAE,OAAO,MAAM,CAAC;;;AAI9C,MAAM,iBAAiB,OACrB,UACA,YACA,eACG;CACH,MAAM,UAAU,KAAK,KACnB,GAAG,QAAQ,EACX,oBAAoB,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,EAAE,GACtE;CACD,MAAM,cAAc,KAAK,KAAK,SAAS,OAAO,SAAS;CACvD,MAAM,cAAc,KAAK,KAAK,SAAS,OAAO,SAAS;AAEvD,OAAMA,SAAG,MAAM,KAAK,QAAQ,YAAY,EAAE,EAAE,WAAW,MAAM,CAAC;AAC9D,OAAMA,SAAG,MAAM,KAAK,QAAQ,YAAY,EAAE,EAAE,WAAW,MAAM,CAAC;AAC9D,OAAMA,SAAG,UAAU,aAAa,YAAY,QAAQ;AACpD,OAAMA,SAAG,UAAU,aAAa,YAAY,QAAQ;AAEpD,KAAI;EACF,MAAM,EAAE,WAAW,MAAM,cACvB,OACA;GACE;GACA;GACA;GACA;GACA;GACA;GACA,KAAK,KAAK,OAAO,SAAS;GAC1B,KAAK,KAAK,OAAO,SAAS;GAC3B,EACD,EAAE,KAAK,SAAS,CACjB,CAAC,OAAO,UAAmB;GAC1B,MAAM,aAAa;AAEnB,OAAI,WAAW,SAAS,KAAK,WAAW,OACtC,QAAO,EAAE,QAAQ,WAAW,QAAQ;AAGtC,SAAM;IACN;AAEF,SAAO,OACJ,WAAW,SAAS,YAAY,KAAK,WAAW,CAChD,WAAW,SAAS,YAAY,KAAK,WAAW;WAC3C;AACR,QAAMA,SAAG,GAAG,SAAS;GAAE,OAAO;GAAM,WAAW;GAAM,CAAC;;;AAI1D,MAAa,kCAAkC,OAC7C,cACA,KACA,iBACG;CACH,MAAM,yBAAS,IAAI,KAAgC;AAEnD,MAAK,MAAM,eAAe,cAAc;EACtC,MAAM,iBAAiB,aAAa,YAAY,KAAK;EACrD,MAAM,UAAU,OAAO,IAAI,eAAe,IAAI,EAAE;AAChD,UAAQ,KAAK,YAAY;AACzB,SAAO,IAAI,gBAAgB,QAAQ;;CAGrC,MAAM,UAAoB,EAAE;AAE5B,MAAK,MAAM,CAAC,UAAU,qBAAqB,OAAO,SAAS,EAAE;EAC3D,MAAM,eAAe,KAAK,KAAK,KAAK,SAAS;EAC7C,MAAM,aAAa,MAAMA,SAAG,SAAS,cAAc,QAAQ;EAC3D,IAAI,aAAa;AAEjB,OAAK,MAAM,eAAe,kBAAkB;AAC1C,OAAI,CAAC,WAAW,SAAS,YAAY,QAAQ,CAC3C,OAAM,IAAI,MAAM,uBAAuB,WAAW;AAGpD,gBAAa,WAAW,QAAQ,YAAY,SAAS,YAAY,QAAQ;;AAG3E,MAAI,eAAe,WACjB;AAGF,UAAQ,KAAK,MAAM,eAAe,UAAU,YAAY,WAAW,CAAC;;CAGtE,MAAM,eAAe,QAAQ,KAAK,KAAK;AAEvC,KAAI,CAAC,aAAa,MAAM,CACtB,OAAM,IAAI,MAAM,gBAAgB;AAGlC,QAAO;;AAGT,MAAa,oBAAoB,OAC/B,cACA,KACA,UACG;CACH,MAAM,eAAe,KAAK,KACxB,GAAG,QAAQ,EACX,eAAe,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ,CAAC,SAAS,GAAG,CAAC,MAAM,EAAE,CAAC,QAClE;AAED,OAAMA,SAAG,UAAU,cAAc,cAAc,QAAQ;AAEvD,KAAI;AACF,QAAM,cAAc,OAAO;GAAC;GAAS;GAAW;GAAa,EAAE,EAAE,KAAK,CAAC;AACvE,QAAM,cAAc,OAAO,CAAC,SAAS,aAAa,EAAE,EAAE,KAAK,CAAC;AAI5D,SAAO;GACL;GACA,cAJmB,gBAAgB,aAAa;GAKjD;UACM,OAAO;EACd,MAAM,SACJ,iBAAiB,SAAS,YAAY,QAClC,OAAQ,MAAuC,OAAO,GACtD,iBAAiB,QACf,MAAM,UACN;AAER,QAAM,IAAI,MAAM,qBAAqB,OAAO,MAAM,GAAG;WAC7C;AACR,QAAMA,SAAG,GAAG,cAAc,EAAE,OAAO,MAAM,CAAC;;;;;;AClM9C,MAAM,6BAAa,IAAI,KAA4B;AACnD,MAAM,SAAS,MAAO,KAAK;AAE3B,MAAM,qBAAqB;CACzB,MAAM,MAAM,KAAK,KAAK;AAEtB,MAAK,MAAM,CAAC,KAAK,UAAU,WAAW,SAAS,CAC7C,KAAI,MAAM,MAAM,YAAY,OAC1B,YAAW,OAAO,IAAI;;AAK5B,MAAa,uBAAuB,cAAsB,iBAA2B;AACnF,eAAc;CAEd,MAAM,UAAU,YAAY;CAC5B,MAAM,gBAAgB,WAAW;AAEjC,YAAW,IAAI,SAAS;EACtB;EACA;EACA;EACA;EACA,WAAW,KAAK,KAAK;EACtB,CAAC;AAEF,QAAO;EACL;EACA;EACD;;AAGH,MAAa,oBAAoB,SAAiB,kBAA0B;AAC1E,eAAc;CAEd,MAAM,OAAO,WAAW,IAAI,QAAQ;AAEpC,KAAI,CAAC,QAAQ,KAAK,kBAAkB,cAClC,QAAO;AAGT,QAAO;;AAGT,MAAa,uBAAuB,YAAoB;AACtD,YAAW,OAAO,QAAQ;;;;;ACnB5B,MAAM,qBAAqB,YAAoC;CAC7D,+BAA+B,OAAO;CACtC,gCAAgC;CAChC,gCAAgC;CAChC,gBAAgB;CACjB;AAED,MAAM,YACJ,UACA,QACA,YACA,YACG;AACH,UAAS,UAAU,YAAY,kBAAkB,OAAO,CAAC;AACzD,UAAS,IAAI,KAAK,UAAU,QAAQ,CAAC;;AAGvC,MAAM,eAAe,OAAU,YAA6B;CAC1D,MAAM,SAAmB,EAAE;AAE3B,YAAW,MAAM,SAAS,QACxB,QAAO,KAAK,OAAO,SAAS,MAAM,GAAG,QAAQ,OAAO,KAAK,MAAM,CAAC;CAGlE,MAAM,MAAM,OAAO,OAAO,OAAO,CAAC,SAAS,QAAQ;AACnD,QAAO,KAAK,MAAM,IAAI;;AAGxB,MAAM,uBAAuB,OAC3B,QACA,SACA,iBACG;AACH,QAAO,2BACL,OAAO,SACP,cACA;EACE,cAAc,QAAQ;EACtB,OAAO,QAAQ,SAAS;EACxB,WAAW,QAAQ,SAAS;EAC7B,CACF;;AAGH,MAAM,wBAAwB,cAAyB;AACrD,KAAI,CAAC,WAAW,OACd,QAAO;CAGT,MAAM,YAAY,UACf,KAAK,UAAU,MAAM,MAAM,CAAC,CAC5B,OAAO,QAAQ,CACf,QAAQ,UAAU,UAAU,OAAO,UAAU,OAAO,oBAAoB,KAAK,MAAM,CAAC;AAEvF,QAAO,UAAU,SAAS,MAAM,KAAK,IAAI,IAAI,UAAU,CAAC,GAAG;;AAG7D,MAAM,kBAAkB,iBAA2B;AACjD,QAAO,aAAa,WAAW,IAC3B,wBACA,GAAG,aAAa,OAAO;;AAG7B,MAAM,iBAAiB,YAAkD;AACvE,QAAO;;AAGT,MAAM,kBAAkB,UAAgD;AACtE,QAAO,UAAU,WAAW,UAAU;;AAGxC,MAAM,yBACJ,QACA,cACA,eACiB;AACjB,KAAI,aACF,QAAO;AAGT,KAAI,eAAe,WAAW,CAC5B,QAAO;AAGT,QAAO,OAAO;;AAGhB,MAAa,gCAAgC,WAAmC;AAC9E,QAAO,6CAA6C,QAAQ,EAAE,gBAAgB,qBAAqB,CAAC;;AAGtG,MAAa,gDACX,QACA,iBAGG;AAuLH,QAtLe,aAAa,OAAO,SAAS,aAAa;EACvD,MAAM,SAAS,QAAQ,UAAU;EACjC,MAAM,MAAM,IAAI,IAAI,QAAQ,OAAO,KAAK,UAAU,OAAO,KAAK,GAAG,OAAO,OAAO;AAE/E,MAAI,WAAW,WAAW;AACxB,YAAS,UAAU,KAAK,kBAAkB,OAAO,CAAC;AAClD,YAAS,KAAK;AACd;;AAGF,MAAI;AACF,OAAI,WAAW,SAAS,IAAI,aAAa,WAAW;IAClD,MAAM,QAAQ,sBAAsB,QAAQ,QAAW,IAAI,aAAa,IAAI,QAAQ,CAAC;AAGrF,aAAS,UAAU,QAAQ,KAAK,MAFhB,aAAa,eAAe,MAAM,CAEJ,UAAU,OAAO,QAAQ,CAAC;AACxE;;AAGF,OAAI,WAAW,UAAU,IAAI,aAAa,SAAS;IACjD,MAAM,UAAU,MAAM,aAAiC,QAAQ;IAC/D,MAAM,wBACJ,qBAAqB,QAAQ,SAAS,UAAU,IAAI,OAAO;IAC7D,MAAM,QAAQ,sBAAsB,QAAQ,QAAQ,SAAS,MAAM;IACnE,MAAM,UAAU,aAAa,eAAe,MAAM;AAElD,QAAI,CAAC,QAAQ,QAAQ,MAAM,EAAE;AAC3B,cAAS,UAAU,QAAQ,KAAK,EAAE,OAAO,kBAAkB,CAAC;AAC5D;;IAGF,MAAM,gBAAgB,MAAM,QAAQ,IAAI;KACtC,cAAc,QAAQ,gBAAgB;KACtC,QAAQ,QAAQ;KAChB,MAAM,QAAQ;KACd,OAAO,QAAQ,SAAS;KACxB,WAAW,QAAQ,SAAS;KAC5B,kBAAkB,QAAQ,SAAS;KACnC,gBAAgB,MAAM,qBAAqB,QAAQ,SAAS,sBAAsB;KAClF,KAAK,OAAO;KACb,CAAC;AAEF,QAAI,QAAQ,SAAS,UAAU;AAM7B,cAAS,UAAU,QAAQ,KALiB;MAC1C,SAAS,cAAc;MACvB,UAAU,cAAc;MACzB,CAE8C;AAC/C;;IAGF,MAAM,SAAS,cAAc,cAAc;IAC3C,IAAI,eAAe;AAEnB,QAAI;AACF,SAAI,OAAO,SAAS,OAClB,gBAAe,MAAM,gCACnB,OAAO,SACP,OAAO,UACN,aACC,uBAAuB,UAAU,uBAAuB,OAAO,QAAQ,CAC1E;aAEI,OAAO;AAWd,cAAS,UAAU,QAAQ,KAViB;MAC1C,SACE,OAAO,WACP;MACF,UAAU,CACR,GAAI,OAAO,YAAY,EAAE,EACzB,iBAAiB,QAAQ,MAAM,UAAU,kBAC1C;MACF,CAE8C;AAC/C;;AAGF,QAAI,CAAC,cAAc;AASjB,cAAS,UAAU,QAAQ,KARqB;MAC9C,SAAS,OAAO,WAAW;MAC3B,UAAU,CACR,GAAI,OAAO,YAAY,EAAE,EACzB,8CACD;MACF,CAEkD;AACnD;;AAGF,QAAI;AACF,wBAAmB,eAAe,aAChC,uBAAuB,UAAU,uBAAuB,OAAO,QAAQ,CACxE;AACD,WAAM,kBAAkB,cAAc,OAAO,QAAQ;aAC9C,OAAO;AAYd,cAAS,UAAU,QAAQ,KAXuB;MAChD,SACE,OAAO,WACP;MACF;MACA,UAAU,CACR,GAAI,OAAO,YAAY,EAAE,EACzB,iBAAiB,QAAQ,MAAM,UAAU,oBAC1C;MACF,CAEoD;AACrD;;IAGF,MAAM,QAAQ,oBAAoB,cAAc,sBAAsB;AAQtE,aAAS,UAAU,QAAQ,KAPe;KACxC,SAAS,OAAO,WAAW;KAC3B;KACA,SAAS,MAAM;KACf,UAAU,OAAO,YAAY,EAAE;KAChC,CAE4C;AAC7C;;AAGF,OAAI,WAAW,UAAU,IAAI,aAAa,UAAU;IAClD,MAAM,UAAU,MAAM,aAAkC,QAAQ;AAEhE,QAAI,CAAC,QAAQ,WAAW,CAAC,QAAQ,eAAe;AAC9C,cAAS,UAAU,QAAQ,KAAK,EAC9B,OAAO,kCACR,CAAC;AACF;;IAGF,MAAM,gBAAgB,iBAAiB,QAAQ,SAAS,QAAQ,cAAc;AAE9E,QAAI,CAAC,eAAe;AAClB,cAAS,UAAU,QAAQ,KAAK,EAC9B,OAAO,4BACR,CAAC;AACF;;AAGF,uBAAmB,cAAc,eAAe,aAC9C,uBACE,UACA,cAAc,cAAc,SACxB,cAAc,eACd,OAAO,cACX,OAAO,QACR,CACF;IAED,MAAM,SAAS,MAAM,kBACnB,cAAc,cACd,OAAO,SACP,qBACD;AACD,wBAAoB,QAAQ,QAAQ;AAQpC,aAAS,UAAU,QAAQ,KANiB;KAC1C,SAAS;KACT,cAAc,OAAO;KACrB,SAAS,eAAe,OAAO,aAAa;KAC7C,CAE6C;AAC9C;;AAGF,YAAS,UAAU,QAAQ,KAAK,EAAE,OAAO,kBAAkB,CAAC;WACrD,OAAO;AACd,YAAS,UAAU,QAAQ,KAAK,EAC9B,OACE,iBAAiB,QACb,MAAM,UACN,2BACP,CAAC;;GAEJ;;;;;ACtTJ,MAAM,gBAAgB,UAA4C;AAChE,KAAI,UAAU,SACZ,QAAO;AAGT,QAAO;;AAGT,MAAa,yBAAyB,OAAO,SAAmB;CAC9D,MAAM,gBAAgB,KAAK,WAAW,UAAU,UAAU,KAAK;CAC/D,MAAM,gBACJ,iBAAiB,IAAI,KAAK,gBAAgB,KAAK;CACjD,MAAM,iBAAiB,KAAK,MAAM,UAAU,QAAQ,KAAK,MAAM,CAAC;CAChE,MAAM,eAAe,gBACjB,OAAO,cAAc,GACrB,iBACE,OAAO,eAAe,GACtB,OAAO,QAAQ,IAAI,2BAA2B,KAAK;CACzD,MAAM,kBAAkB,KAAK,MAAM,UAAU,UAAU,WAAW,UAAU,SAAS;CAErF,MAAM,SAAS,6BAA6B;EAC1C,SAAS,QAAQ,KAAK;EACtB,MAAM,QAAQ,IAAI;EAClB,MAAM,OAAO,SAAS,aAAa,GAAG,eAAe;EACrD,YAAY,QAAQ,IAAI,kCAAkC;EAC1D,OAAO,aAAa,gBAAgB;EACpC,eAAe,QAAQ,IAAI,6BACzB,uDACC,MAAM,IAAI,CACV,KAAK,UAAU,MAAM,MAAM,CAAC,CAC5B,OAAO,QAAQ;EACnB,CAAC;CAEF,MAAM,SAAS,6BAA6B,OAAO;AAEnD,OAAM,IAAI,SAAe,YAAY;AACnC,SAAO,OAAO,OAAO,MAAM,OAAO,YAAY;AAC5C,WAAQ,OAAO,MACb,4CAA4C,OAAO,KAAK,GAAG,OAAO,KAAK,IACxE;AACD,YAAS;IACT;GACF;;;;;AC1CJ,uBAAuB,QAAQ,KAAK,MAAM,EAAE,CAAC,CAAC,OAAO,UAAU;CAC7D,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AACtE,SAAQ,OAAO,MAAM,wBAAwB,QAAQ,IAAI;AACzD,SAAQ,KAAK,EAAE;EACf"}
|
package/dist/index.mjs
CHANGED
|
@@ -33,6 +33,7 @@ const useDevCopilotConfig = () => {
|
|
|
33
33
|
//#endregion
|
|
34
34
|
//#region src/lib/api-client.ts
|
|
35
35
|
const API_BASE_URL = "http://127.0.0.1:3339";
|
|
36
|
+
const BRIDGE_CONNECTION_ERROR_MESSAGE = "브릿지 서버에 연결하지 못했습니다. 브릿지 서버를 켜주세요.";
|
|
36
37
|
const parseResponse = async (response) => {
|
|
37
38
|
const payload = await response.json();
|
|
38
39
|
if (!response.ok) {
|
|
@@ -41,18 +42,31 @@ const parseResponse = async (response) => {
|
|
|
41
42
|
}
|
|
42
43
|
return payload;
|
|
43
44
|
};
|
|
45
|
+
const normalizeNetworkError = (error) => {
|
|
46
|
+
if (error instanceof TypeError && error.message.toLowerCase().includes("failed to fetch")) return new Error(BRIDGE_CONNECTION_ERROR_MESSAGE);
|
|
47
|
+
if (error instanceof Error) return error;
|
|
48
|
+
return /* @__PURE__ */ new Error("요청 처리 중 오류가 발생했습니다.");
|
|
49
|
+
};
|
|
44
50
|
const createCopilotApiClient = () => {
|
|
45
51
|
const post = async (path, body) => {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
try {
|
|
53
|
+
return parseResponse(await fetch(`${API_BASE_URL}${path}`, {
|
|
54
|
+
method: "POST",
|
|
55
|
+
headers: { "Content-Type": "application/json" },
|
|
56
|
+
body: JSON.stringify(body)
|
|
57
|
+
}));
|
|
58
|
+
} catch (error) {
|
|
59
|
+
throw normalizeNetworkError(error);
|
|
60
|
+
}
|
|
51
61
|
};
|
|
52
62
|
return {
|
|
53
63
|
status: async (agent) => {
|
|
54
64
|
const query = agent ? `?agent=${agent}` : "";
|
|
55
|
-
|
|
65
|
+
try {
|
|
66
|
+
return parseResponse(await fetch(`${API_BASE_URL}/status${query}`));
|
|
67
|
+
} catch (error) {
|
|
68
|
+
throw normalizeNetworkError(error);
|
|
69
|
+
}
|
|
56
70
|
},
|
|
57
71
|
chat: (payload) => post("/chat", payload),
|
|
58
72
|
apply: (payload) => post("/apply", payload)
|
|
@@ -424,30 +438,6 @@ function DevCopilotOverlay() {
|
|
|
424
438
|
}) }), open ? /* @__PURE__ */ jsxs("section", {
|
|
425
439
|
style: panelStyle,
|
|
426
440
|
children: [
|
|
427
|
-
/* @__PURE__ */ jsx("div", {
|
|
428
|
-
style: railStyle,
|
|
429
|
-
children: /* @__PURE__ */ jsx("button", {
|
|
430
|
-
type: "button",
|
|
431
|
-
className: "yrdc-pressable",
|
|
432
|
-
style: railToggleStyle,
|
|
433
|
-
onClick: () => setShowResponsePanel((prev) => !prev),
|
|
434
|
-
"aria-label": showResponsePanel ? "응답 패널 닫기" : "응답 패널 열기",
|
|
435
|
-
title: showResponsePanel ? "응답 패널 닫기" : "응답 패널 열기",
|
|
436
|
-
children: showResponsePanel ? /* @__PURE__ */ jsx(PanelRightCloseIcon, {
|
|
437
|
-
"aria-hidden": true,
|
|
438
|
-
style: {
|
|
439
|
-
width: 14,
|
|
440
|
-
height: 14
|
|
441
|
-
}
|
|
442
|
-
}) : /* @__PURE__ */ jsx(PanelRightOpenIcon, {
|
|
443
|
-
"aria-hidden": true,
|
|
444
|
-
style: {
|
|
445
|
-
width: 14,
|
|
446
|
-
height: 14
|
|
447
|
-
}
|
|
448
|
-
})
|
|
449
|
-
})
|
|
450
|
-
}),
|
|
451
441
|
/* @__PURE__ */ jsxs("div", {
|
|
452
442
|
style: inputPanelStyle,
|
|
453
443
|
children: [
|
|
@@ -474,7 +464,7 @@ function DevCopilotOverlay() {
|
|
|
474
464
|
...statusLineStyle,
|
|
475
465
|
color: agentStatus.authenticated ? "#15803d" : "#dc2626"
|
|
476
466
|
},
|
|
477
|
-
children: [agentStatus.message, agentStatus.loginCommand ? ` 터미널에서 ${agentStatus.loginCommand} 실행` : ""]
|
|
467
|
+
children: [agentStatus.message, agentStatus.loginCommand && !agentStatus.message.includes(agentStatus.loginCommand) ? ` 터미널에서 ${agentStatus.loginCommand} 실행` : ""]
|
|
478
468
|
}) : null
|
|
479
469
|
]
|
|
480
470
|
}),
|
|
@@ -554,6 +544,30 @@ function DevCopilotOverlay() {
|
|
|
554
544
|
})
|
|
555
545
|
]
|
|
556
546
|
}),
|
|
547
|
+
/* @__PURE__ */ jsx("div", {
|
|
548
|
+
style: railStyle,
|
|
549
|
+
children: /* @__PURE__ */ jsx("button", {
|
|
550
|
+
type: "button",
|
|
551
|
+
className: "yrdc-pressable",
|
|
552
|
+
style: railToggleStyle,
|
|
553
|
+
onClick: () => setShowResponsePanel((prev) => !prev),
|
|
554
|
+
"aria-label": showResponsePanel ? "응답 패널 닫기" : "응답 패널 열기",
|
|
555
|
+
title: showResponsePanel ? "응답 패널 닫기" : "응답 패널 열기",
|
|
556
|
+
children: showResponsePanel ? /* @__PURE__ */ jsx(PanelRightCloseIcon, {
|
|
557
|
+
"aria-hidden": true,
|
|
558
|
+
style: {
|
|
559
|
+
width: 14,
|
|
560
|
+
height: 14
|
|
561
|
+
}
|
|
562
|
+
}) : /* @__PURE__ */ jsx(PanelRightOpenIcon, {
|
|
563
|
+
"aria-hidden": true,
|
|
564
|
+
style: {
|
|
565
|
+
width: 14,
|
|
566
|
+
height: 14
|
|
567
|
+
}
|
|
568
|
+
})
|
|
569
|
+
})
|
|
570
|
+
}),
|
|
557
571
|
showResponsePanel ? /* @__PURE__ */ jsx("aside", {
|
|
558
572
|
style: responsePanelStyle,
|
|
559
573
|
children: /* @__PURE__ */ jsxs("div", {
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/lib/config.ts","../src/components/dev-copilot-provider.tsx","../src/lib/api-client.ts","../src/hooks/use-selection-capture.ts","../src/components/icons.tsx","../src/components/dev-copilot-overlay.tsx"],"sourcesContent":["export interface DevCopilotConfig {\n enabled: boolean;\n allowedPaths: string[];\n}\n\nconst defaultConfig: DevCopilotConfig = {\n enabled: process.env.NODE_ENV === \"development\",\n allowedPaths: [\".\"],\n};\n\nexport const resolveDevCopilotConfig = (\n config?: Partial<DevCopilotConfig>,\n): DevCopilotConfig => {\n const nextConfig = config ?? {};\n\n return {\n ...defaultConfig,\n ...nextConfig,\n };\n};\n","\"use client\";\n\nimport { createContext, useContext, type ReactNode } from \"react\";\n\nimport {\n resolveDevCopilotConfig,\n type DevCopilotConfig,\n} from \"../lib/config\";\n\nconst DevCopilotContext = createContext<DevCopilotConfig | null>(null);\n\ninterface DevCopilotProviderProps {\n config?: Partial<DevCopilotConfig>;\n children: ReactNode;\n}\n\nexport function DevCopilotProvider({\n config,\n children,\n}: DevCopilotProviderProps) {\n const resolvedConfig = resolveDevCopilotConfig(config);\n\n return (\n <DevCopilotContext.Provider value={resolvedConfig}>\n {children}\n </DevCopilotContext.Provider>\n );\n}\n\nexport const useDevCopilotConfig = () => {\n const context = useContext(DevCopilotContext);\n\n if (!context) {\n throw new Error(\"DevCopilotProvider 내부에서만 사용할 수 있습니다.\");\n }\n\n return context;\n};\n","import type {\n CopilotAgent,\n CopilotApplyRequest,\n CopilotApplyResponse,\n CopilotAgentStatusResponse,\n CopilotChatRequest,\n CopilotChatResponse,\n CopilotErrorResponse,\n} from \"../types\";\n\nconst API_BASE_URL = \"http://127.0.0.1:3339\";\n\nconst parseResponse = async <T>(response: Response): Promise<T> => {\n const payload = (await response.json()) as T | CopilotErrorResponse;\n\n if (!response.ok) {\n const errorMessage =\n typeof payload === \"object\" && payload && \"error\" in payload\n ? payload.error\n : \"요청 처리 중 오류가 발생했습니다.\";\n throw new Error(errorMessage);\n }\n\n return payload as T;\n};\n\nexport const createCopilotApiClient = () => {\n const post = async <T, U>(path: string, body: T): Promise<U> => {\n const response = await fetch(`${API_BASE_URL}${path}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n return parseResponse<U>(response);\n };\n\n return {\n status: async (agent?: CopilotAgent) => {\n const query = agent ? `?agent=${agent}` : \"\";\n const response = await fetch(`${API_BASE_URL}/status${query}`);\n return parseResponse<CopilotAgentStatusResponse>(response);\n },\n chat: (payload: CopilotChatRequest) =>\n post<CopilotChatRequest, CopilotChatResponse>(\"/chat\", payload),\n apply: (payload: CopilotApplyRequest) =>\n post<CopilotApplyRequest, CopilotApplyResponse>(\"/apply\", payload),\n };\n};\n","\"use client\";\n\nimport { useCallback, useEffect, useState } from \"react\";\n\nconst OVERLAY_ATTRIBUTE = \"data-dev-copilot-overlay\";\n\nconst toSelectionText = () => {\n const selection = window.getSelection();\n const value = selection?.toString().trim() ?? \"\";\n return value;\n};\n\nconst isNodeInsideOverlay = (node: Node | null) => {\n if (!node) {\n return false;\n }\n\n const element = node instanceof Element ? node : node.parentElement;\n\n return Boolean(element?.closest(`[${OVERLAY_ATTRIBUTE}]`));\n};\n\nconst isInsideOverlay = (target: EventTarget | null) => {\n return (\n target instanceof Element &&\n Boolean(target.closest(`[${OVERLAY_ATTRIBUTE}]`))\n );\n};\n\nexport const useSelectionCapture = () => {\n const [selectedText, setSelectedText] = useState(\"\");\n\n const syncSelection = useCallback((event: MouseEvent | KeyboardEvent) => {\n const selection = window.getSelection();\n const startedInsideOverlay = isNodeInsideOverlay(selection?.anchorNode ?? null);\n const endedInsideOverlay = isNodeInsideOverlay(selection?.focusNode ?? null);\n\n if (isInsideOverlay(event.target) || startedInsideOverlay || endedInsideOverlay) {\n return;\n }\n\n const nextSelectedText = selection?.toString().trim() ?? toSelectionText();\n\n if (!nextSelectedText) {\n return;\n }\n\n setSelectedText(nextSelectedText);\n }, []);\n\n useEffect(() => {\n document.addEventListener(\"mouseup\", syncSelection);\n document.addEventListener(\"keyup\", syncSelection);\n\n return () => {\n document.removeEventListener(\"mouseup\", syncSelection);\n document.removeEventListener(\"keyup\", syncSelection);\n };\n }, [syncSelection]);\n\n return {\n selectedText,\n setSelectedText,\n clearSelection: () => setSelectedText(\"\"),\n };\n};\n\nexport { OVERLAY_ATTRIBUTE };\n","import type { ComponentPropsWithoutRef } from \"react\";\n\ntype SvgIconProps = ComponentPropsWithoutRef<\"svg\">;\n\nexport const SparklesIcon = (props: SvgIconProps) => {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.8\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <path d=\"M12 3.75 13.68 8.32 18.25 10 13.68 11.68 12 16.25 10.32 11.68 5.75 10 10.32 8.32 12 3.75Z\" />\n <path d=\"M18.5 3.75 19.08 5.42 20.75 6 19.08 6.58 18.5 8.25 17.92 6.58 16.25 6 17.92 5.42 18.5 3.75Z\" />\n <path d=\"M6 15.5 7 18.25 9.75 19.25 7 20.25 6 23 5 20.25 2.25 19.25 5 18.25 6 15.5Z\" />\n </svg>\n );\n};\n\nexport const PanelRightOpenIcon = (props: SvgIconProps) => {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.8\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <rect x=\"3.75\" y=\"4.75\" width=\"16.5\" height=\"14.5\" rx=\"2.25\" />\n <path d=\"M8.75 4.75v14.5\" />\n <path d=\"m13 9.25 3 2.75-3 2.75\" />\n </svg>\n );\n};\n\nexport const PanelRightCloseIcon = (props: SvgIconProps) => {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.8\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <rect x=\"3.75\" y=\"4.75\" width=\"16.5\" height=\"14.5\" rx=\"2.25\" />\n <path d=\"M15.25 4.75v14.5\" />\n <path d=\"m11 9.25-3 2.75 3 2.75\" />\n </svg>\n );\n};\n","\"use client\";\n\nimport {\n useEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n} from \"react\";\n\nimport { createCopilotApiClient } from \"../lib/api-client\";\nimport type {\n CopilotAgent,\n CopilotAgentStatusResponse,\n CopilotChatResponse,\n CopilotMode,\n} from \"../types\";\nimport {\n OVERLAY_ATTRIBUTE,\n useSelectionCapture,\n} from \"../hooks/use-selection-capture\";\nimport {\n PanelRightCloseIcon,\n PanelRightOpenIcon,\n SparklesIcon,\n} from \"./icons\";\nimport { useDevCopilotConfig } from \"./dev-copilot-provider\";\n\nconst FLOATING_BUTTON_SIZE = 48;\nconst DRAG_CLICK_THRESHOLD = 4;\nconst PANEL_WIDTH = \"min(1028px, calc(100vw - 48px))\";\nconst INPUT_PANEL_WIDTH = 420;\nconst RAIL_WIDTH = 36;\nconst AGENT_LABELS: Record<CopilotAgent, string> = {\n codex: \"Codex CLI\",\n claude: \"Claude Code CLI\",\n};\nconst UI_LABELS = {\n title: \"Dev Copilot\",\n subtitle: \"텍스트를 선택한 뒤 프롬프트를 입력하세요.\",\n promptPlaceholder: \"무엇을 도와드릴까요?\",\n askButton: \"질문\",\n editButton: \"코드 수정 제안\",\n applyButton: \"미리보기 적용\",\n} as const;\n\nconst styleText = `\n.yrdc-trigger{transition:transform 150ms ease-out, box-shadow 150ms ease-out}\n.yrdc-trigger:hover{transform:scale(1.05)}\n.yrdc-pressable{transition:transform 150ms ease-out, background-color 150ms ease-out, opacity 150ms ease-out}\n.yrdc-pressable:hover{transform:translateY(-1px)}\n.yrdc-spinner{animation:yrdc-spin 1s linear infinite}\n.yrdc-field:focus{outline:none;box-shadow:0 0 0 3px rgba(59,130,246,.14);border-color:#93c5fd}\n@keyframes yrdc-spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}\n`;\n\nexport function DevCopilotOverlay() {\n const config = useDevCopilotConfig();\n const apiClient = useMemo(() => createCopilotApiClient(), []);\n const { selectedText, setSelectedText } = useSelectionCapture();\n\n const [open, setOpen] = useState(false);\n const [prompt, setPrompt] = useState(\"\");\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [chatResult, setChatResult] = useState<CopilotChatResponse | null>(null);\n const [toastMessage, setToastMessage] = useState<string | null>(null);\n const [agentStatus, setAgentStatus] =\n useState<CopilotAgentStatusResponse | null>(null);\n const [selectedAgent, setSelectedAgent] = useState<CopilotAgent>(\"codex\");\n const [position, setPosition] = useState<{ x: number; y: number } | null>(null);\n const [showResponsePanel, setShowResponsePanel] = useState(false);\n\n const containerRef = useRef<HTMLDivElement>(null);\n const suppressMainToggleClickRef = useRef(false);\n const dragStateRef = useRef<{\n pointerId: number;\n startX: number;\n startY: number;\n originX: number;\n originY: number;\n moved: boolean;\n } | null>(null);\n\n useEffect(() => {\n const onResize = () => {\n setPosition((prev) => {\n if (!prev) {\n return prev;\n }\n\n return {\n x: Math.min(\n Math.max(prev.x, 0),\n Math.max(0, window.innerWidth - FLOATING_BUTTON_SIZE),\n ),\n y: Math.min(\n Math.max(prev.y, 0),\n Math.max(0, window.innerHeight - FLOATING_BUTTON_SIZE),\n ),\n };\n });\n };\n\n window.addEventListener(\"resize\", onResize);\n return () => window.removeEventListener(\"resize\", onResize);\n }, []);\n\n useEffect(() => {\n if (!open) {\n return;\n }\n\n let ignore = false;\n\n apiClient\n .status(selectedAgent)\n .then((status) => {\n if (!ignore) {\n setAgentStatus(status);\n }\n })\n .catch((caughtError) => {\n if (!ignore) {\n setAgentStatus({\n available: false,\n authenticated: false,\n agent: selectedAgent,\n message:\n caughtError instanceof Error\n ? caughtError.message\n : \"로컬 에이전트 상태 확인에 실패했습니다.\",\n });\n }\n });\n\n return () => {\n ignore = true;\n };\n }, [apiClient, open, selectedAgent]);\n\n useEffect(() => {\n if (!open) {\n return;\n }\n\n const onPointerDownOutside = (event: PointerEvent) => {\n const target = event.target;\n\n if (!(target instanceof Node)) {\n return;\n }\n\n if (containerRef.current?.contains(target)) {\n return;\n }\n\n setOpen(false);\n setShowResponsePanel(false);\n dragStateRef.current = null;\n suppressMainToggleClickRef.current = false;\n };\n\n document.addEventListener(\"pointerdown\", onPointerDownOutside);\n\n return () => {\n document.removeEventListener(\"pointerdown\", onPointerDownOutside);\n };\n }, [open]);\n\n if (!config.enabled) {\n return null;\n }\n\n const onPointerDown = (event: React.PointerEvent<HTMLButtonElement>) => {\n if (event.button !== 0) {\n return;\n }\n\n const wrapperRect = containerRef.current?.getBoundingClientRect();\n const originX = position?.x ?? wrapperRect?.left ?? 0;\n const originY = position?.y ?? wrapperRect?.top ?? 0;\n\n event.currentTarget.setPointerCapture(event.pointerId);\n dragStateRef.current = {\n pointerId: event.pointerId,\n startX: event.clientX,\n startY: event.clientY,\n originX,\n originY,\n moved: false,\n };\n };\n\n const onPointerMove = (event: React.PointerEvent<HTMLButtonElement>) => {\n const dragState = dragStateRef.current;\n if (!dragState || dragState.pointerId !== event.pointerId) {\n return;\n }\n\n const deltaX = event.clientX - dragState.startX;\n const deltaY = event.clientY - dragState.startY;\n const moved =\n Math.abs(deltaX) > DRAG_CLICK_THRESHOLD ||\n Math.abs(deltaY) > DRAG_CLICK_THRESHOLD;\n\n if (!moved && !dragState.moved) {\n return;\n }\n\n dragState.moved = true;\n\n setPosition({\n x: Math.min(\n Math.max(dragState.originX + deltaX, 0),\n Math.max(0, window.innerWidth - FLOATING_BUTTON_SIZE),\n ),\n y: Math.min(\n Math.max(dragState.originY + deltaY, 0),\n Math.max(0, window.innerHeight - FLOATING_BUTTON_SIZE),\n ),\n });\n };\n\n const onPointerEnd = (event: React.PointerEvent<HTMLButtonElement>) => {\n if (dragStateRef.current?.pointerId !== event.pointerId) {\n return;\n }\n\n suppressMainToggleClickRef.current = dragStateRef.current.moved;\n dragStateRef.current = null;\n event.currentTarget.releasePointerCapture(event.pointerId);\n };\n\n const onMainToggleClick = () => {\n if (suppressMainToggleClickRef.current) {\n suppressMainToggleClickRef.current = false;\n return;\n }\n\n setOpen((prev) => {\n const next = !prev;\n if (!next) {\n setPosition(null);\n dragStateRef.current = null;\n suppressMainToggleClickRef.current = false;\n }\n return next;\n });\n };\n\n const previousResponse = chatResult\n ? [\n `message:\\n${chatResult.message}`,\n chatResult.patchPreview\n ? `patchPreview:\\n${chatResult.patchPreview}`\n : \"\",\n ]\n .filter(Boolean)\n .join(\"\\n\\n\")\n : undefined;\n\n const onSubmit = async (mode: CopilotMode) => {\n if (agentStatus && (!agentStatus.available || !agentStatus.authenticated)) {\n setError(agentStatus.message);\n return;\n }\n\n if (!prompt.trim()) {\n setError(\"프롬프트를 입력해 주세요.\");\n return;\n }\n\n setBusy(true);\n setError(null);\n setShowResponsePanel(true);\n\n try {\n const currentRoute =\n typeof window !== \"undefined\" ? window.location.pathname : undefined;\n const result = await apiClient.chat({\n selectedText,\n prompt,\n mode,\n context: {\n route: currentRoute,\n fileHints: config.allowedPaths,\n previousResponse,\n agent: selectedAgent,\n },\n });\n\n setChatResult(result);\n } catch (caughtError) {\n setError(\n caughtError instanceof Error\n ? caughtError.message\n : \"요청 처리 중 오류가 발생했습니다.\",\n );\n } finally {\n setBusy(false);\n }\n };\n\n const onApply = async () => {\n if (!chatResult?.patchId) {\n setError(\"적용 가능한 패치가 없습니다.\");\n return;\n }\n\n setBusy(true);\n setError(null);\n setShowResponsePanel(true);\n\n try {\n const result = await apiClient.apply({\n patchId: chatResult.patchId,\n approvalToken: `approve:${chatResult.patchId}`,\n });\n\n if (result.applied) {\n setToastMessage(result.summary);\n window.setTimeout(() => {\n setToastMessage(null);\n }, 3000);\n }\n } catch (caughtError) {\n setError(\n caughtError instanceof Error\n ? caughtError.message\n : \"패치 적용 중 오류가 발생했습니다.\",\n );\n } finally {\n setBusy(false);\n }\n };\n\n const floatingWrapperStyle: CSSProperties = position\n ? {\n position: \"fixed\",\n zIndex: 2147483000,\n left: `${position.x}px`,\n top: `${position.y}px`,\n }\n : {\n position: \"fixed\",\n right: 24,\n bottom: 24,\n zIndex: 2147483000,\n };\n\n const panelStyle: CSSProperties = {\n position: \"relative\",\n marginTop: 12,\n display: \"grid\",\n gridTemplateColumns: showResponsePanel\n ? `${INPUT_PANEL_WIDTH}px ${RAIL_WIDTH}px minmax(0, 1fr)`\n : `${INPUT_PANEL_WIDTH}px ${RAIL_WIDTH}px`,\n width: showResponsePanel ? PANEL_WIDTH : INPUT_PANEL_WIDTH + RAIL_WIDTH,\n maxHeight: \"calc(100vh - 96px)\",\n overflow: \"hidden\",\n border: \"1px solid #e5e7eb\",\n borderRadius: 24,\n background: \"#f8fafc\",\n boxShadow: \"0 18px 60px rgba(15, 23, 42, 0.16)\",\n color: \"#0f172a\",\n fontFamily:\n \"Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif\",\n };\n\n return (\n <>\n <style>{styleText}</style>\n\n {toastMessage ? (\n <div style={toastStyle}>\n <p aria-live=\"polite\" style={{ margin: 0 }}>\n {toastMessage}\n </p>\n </div>\n ) : null}\n\n <div ref={containerRef} style={floatingWrapperStyle} {...{ [OVERLAY_ATTRIBUTE]: \"\" }}>\n <div>\n <button\n type=\"button\"\n className=\"yrdc-trigger\"\n style={triggerButtonStyle}\n onClick={onMainToggleClick}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerEnd}\n onPointerCancel={onPointerEnd}\n aria-label=\"Dev Copilot 열기\"\n title=\"Dev Copilot 열기/닫기 / 드래그해서 이동\"\n >\n <SparklesIcon aria-hidden style={{ width: 16, height: 16 }} />\n </button>\n </div>\n\n {open ? (\n <section style={panelStyle}>\n <div style={railStyle}>\n <button\n type=\"button\"\n className=\"yrdc-pressable\"\n style={railToggleStyle}\n onClick={() => setShowResponsePanel((prev) => !prev)}\n aria-label={showResponsePanel ? \"응답 패널 닫기\" : \"응답 패널 열기\"}\n title={showResponsePanel ? \"응답 패널 닫기\" : \"응답 패널 열기\"}\n >\n {showResponsePanel ? (\n <PanelRightCloseIcon aria-hidden style={{ width: 14, height: 14 }} />\n ) : (\n <PanelRightOpenIcon aria-hidden style={{ width: 14, height: 14 }} />\n )}\n </button>\n </div>\n\n <div style={inputPanelStyle}>\n <header>\n <p style={titleStyle}>{UI_LABELS.title}</p>\n <p style={subtitleStyle}>{UI_LABELS.subtitle}</p>\n </header>\n\n <div style={statusBoxStyle}>\n <p style={statusLineStyle}>로컬 에이전트: {AGENT_LABELS[selectedAgent]}</p>\n {agentStatus?.model ? (\n <p style={statusLineStyle}>모델: {agentStatus.model}</p>\n ) : null}\n {agentStatus ? (\n <p\n style={{\n ...statusLineStyle,\n color: agentStatus.authenticated ? \"#15803d\" : \"#dc2626\",\n }}\n >\n {agentStatus.message}\n {agentStatus.loginCommand\n ? ` 터미널에서 ${agentStatus.loginCommand} 실행`\n : \"\"}\n </p>\n ) : null}\n </div>\n\n <label style={labelStyle}>에이전트</label>\n <div style={agentToggleRowStyle}>\n {([\"codex\", \"claude\"] as CopilotAgent[]).map((agent) => {\n const active = selectedAgent === agent;\n return (\n <button\n key={agent}\n type=\"button\"\n className=\"yrdc-pressable\"\n onClick={() => setSelectedAgent(agent)}\n disabled={busy}\n style={{\n ...agentToggleButtonStyle,\n background: active ? \"#111827\" : \"#e2e8f0\",\n color: active ? \"#ffffff\" : \"#1e293b\",\n opacity: busy ? 0.6 : 1,\n }}\n >\n {AGENT_LABELS[agent]}\n </button>\n );\n })}\n </div>\n\n <label style={labelStyle}>선택 텍스트</label>\n <textarea\n value={selectedText}\n onChange={(event) => setSelectedText(event.target.value)}\n rows={5}\n className=\"yrdc-field\"\n style={textareaStyle}\n />\n\n <label style={labelStyle}>프롬프트</label>\n <textarea\n value={prompt}\n onChange={(event) => setPrompt(event.target.value)}\n rows={4}\n placeholder={UI_LABELS.promptPlaceholder}\n className=\"yrdc-field\"\n style={textareaStyle}\n />\n\n <div style={buttonRowStyle}>\n <button\n type=\"button\"\n className=\"yrdc-pressable\"\n onClick={() => onSubmit(\"answer\")}\n disabled={busy || Boolean(agentStatus && !agentStatus.authenticated)}\n style={{\n ...buttonStyle,\n background: \"#111827\",\n color: \"#ffffff\",\n opacity: busy || Boolean(agentStatus && !agentStatus.authenticated) ? 0.55 : 1,\n }}\n >\n {UI_LABELS.askButton}\n </button>\n <button\n type=\"button\"\n className=\"yrdc-pressable\"\n onClick={() => onSubmit(\"edit\")}\n disabled={busy || Boolean(agentStatus && !agentStatus.authenticated)}\n style={{\n ...buttonStyle,\n background: \"#2563eb\",\n color: \"#ffffff\",\n opacity: busy || Boolean(agentStatus && !agentStatus.authenticated) ? 0.55 : 1,\n }}\n >\n {UI_LABELS.editButton}\n </button>\n </div>\n </div>\n\n {showResponsePanel ? (\n <aside style={responsePanelStyle}>\n <div style={{ display: \"flex\", flexDirection: \"column\", minHeight: 320, height: \"100%\" }}>\n <div>\n <p style={titleStyle}>응답</p>\n </div>\n\n <div style={responseCardStyle}>\n {busy ? (\n <div style={busyContainerStyle}>\n <span className=\"yrdc-spinner\" style={spinnerStyle} />\n <span>{AGENT_LABELS[selectedAgent]} 응답을 기다리는 중입니다.</span>\n </div>\n ) : chatResult ? (\n <article style={articleStyle}>\n {error ? (\n <p style={errorBoxStyle}>{error}</p>\n ) : null}\n {chatResult.warnings.length ? (\n <div style={warningBoxStyle}>\n {chatResult.warnings.map((warning) => (\n <p key={warning} style={{ margin: 0 }}>\n {warning}\n </p>\n ))}\n </div>\n ) : null}\n <p style={messageStyle}>{chatResult.message}</p>\n\n {chatResult.patchPreview ? (\n <>\n <pre style={patchPreviewStyle}>{chatResult.patchPreview}</pre>\n {chatResult.patchId ? (\n <button\n type=\"button\"\n className=\"yrdc-pressable\"\n onClick={onApply}\n disabled={busy}\n style={{\n ...buttonStyle,\n alignSelf: \"flex-start\",\n background: \"#15803d\",\n color: \"#ffffff\",\n opacity: busy ? 0.55 : 1,\n }}\n >\n {UI_LABELS.applyButton}\n </button>\n ) : null}\n </>\n ) : null}\n </article>\n ) : error ? (\n <p style={errorBoxStyle}>{error}</p>\n ) : (\n <p style={placeholderStyle}>\n 질문 또는 코드 수정 제안을 실행하면 이 영역에 결과가 표시됩니다.\n </p>\n )}\n </div>\n </div>\n </aside>\n ) : null}\n </section>\n ) : null}\n </div>\n </>\n );\n}\n\nconst triggerButtonStyle: CSSProperties = {\n display: \"flex\",\n width: FLOATING_BUTTON_SIZE,\n height: FLOATING_BUTTON_SIZE,\n borderRadius: 9999,\n border: \"1px solid #f2d675\",\n background: \"#fff4b8\",\n color: \"#ffb03d\",\n alignItems: \"center\",\n justifyContent: \"center\",\n boxShadow: \"0 12px 28px rgba(15, 23, 42, 0.18)\",\n cursor: \"grab\",\n};\n\nconst railStyle: CSSProperties = {\n display: \"flex\",\n minHeight: 320,\n alignItems: \"center\",\n justifyContent: \"center\",\n borderLeft: \"1px solid #e5e7eb\",\n background: \"#f8fafc\",\n};\n\nconst railToggleStyle: CSSProperties = {\n display: \"flex\",\n width: 24,\n height: 24,\n border: \"0\",\n background: \"transparent\",\n color: \"#64748b\",\n cursor: \"pointer\",\n alignItems: \"center\",\n justifyContent: \"center\",\n};\n\nconst inputPanelStyle: CSSProperties = {\n padding: 16,\n minHeight: 0,\n overflowY: \"auto\",\n background: \"#ffffff\",\n};\n\nconst responsePanelStyle: CSSProperties = {\n minHeight: 0,\n overflow: \"hidden\",\n padding: 16,\n borderLeft: \"1px solid #e5e7eb\",\n background: \"#f8fafc\",\n};\n\nconst titleStyle: CSSProperties = {\n margin: 0,\n fontSize: 18,\n fontWeight: 700,\n lineHeight: 1.4,\n color: \"#111827\",\n};\n\nconst subtitleStyle: CSSProperties = {\n margin: \"4px 0 0\",\n fontSize: 14,\n lineHeight: 1.5,\n color: \"#6b7280\",\n};\n\nconst statusBoxStyle: CSSProperties = {\n marginTop: 12,\n border: \"1px solid #e5e7eb\",\n borderRadius: 16,\n background: \"#ffffff\",\n padding: \"12px 14px\",\n fontSize: 14,\n lineHeight: 1.6,\n color: \"#6b7280\",\n};\n\nconst statusLineStyle: CSSProperties = {\n margin: 0,\n};\n\nconst labelStyle: CSSProperties = {\n display: \"block\",\n marginTop: 16,\n fontSize: 14,\n lineHeight: 1.5,\n color: \"#6b7280\",\n};\n\nconst textareaStyle: CSSProperties = {\n width: \"100%\",\n resize: \"vertical\",\n borderRadius: 16,\n border: \"1px solid #e5e7eb\",\n background: \"#ffffff\",\n padding: \"12px 14px\",\n marginTop: 6,\n fontSize: 15,\n lineHeight: 1.7,\n color: \"#111827\",\n boxSizing: \"border-box\",\n minHeight: 132,\n};\n\nconst agentToggleRowStyle: CSSProperties = {\n display: \"flex\",\n gap: 8,\n marginTop: 6,\n};\n\nconst agentToggleButtonStyle: CSSProperties = {\n border: 0,\n borderRadius: 10,\n padding: \"8px 12px\",\n fontSize: 13,\n fontWeight: 600,\n lineHeight: 1.2,\n cursor: \"pointer\",\n};\n\nconst buttonRowStyle: CSSProperties = {\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n marginTop: 16,\n};\n\nconst buttonStyle: CSSProperties = {\n border: 0,\n borderRadius: 12,\n padding: \"10px 14px\",\n fontSize: 14,\n fontWeight: 600,\n lineHeight: 1,\n cursor: \"pointer\",\n};\n\nconst responseCardStyle: CSSProperties = {\n marginTop: 12,\n minHeight: 0,\n flex: 1,\n overflowY: \"auto\",\n borderRadius: 18,\n border: \"1px solid #e5e7eb\",\n background: \"#ffffff\",\n padding: 16,\n};\n\nconst busyContainerStyle: CSSProperties = {\n display: \"flex\",\n height: \"100%\",\n minHeight: 192,\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: 12,\n color: \"#6b7280\",\n fontSize: 14,\n};\n\nconst spinnerStyle: CSSProperties = {\n display: \"inline-block\",\n width: 18,\n height: 18,\n borderRadius: 9999,\n border: \"2px solid #d1d5db\",\n borderTopColor: \"#2563eb\",\n};\n\nconst articleStyle: CSSProperties = {\n display: \"flex\",\n flexDirection: \"column\",\n gap: 12,\n};\n\nconst errorBoxStyle: CSSProperties = {\n margin: 0,\n border: \"1px solid #fca5a5\",\n borderRadius: 12,\n background: \"#fef2f2\",\n padding: \"10px 12px\",\n fontSize: 14,\n lineHeight: 1.6,\n color: \"#dc2626\",\n whiteSpace: \"pre-wrap\",\n};\n\nconst warningBoxStyle: CSSProperties = {\n display: \"flex\",\n flexDirection: \"column\",\n gap: 6,\n border: \"1px solid #fca5a5\",\n borderRadius: 12,\n background: \"#fef2f2\",\n padding: \"10px 12px\",\n fontSize: 14,\n lineHeight: 1.6,\n color: \"#dc2626\",\n};\n\nconst messageStyle: CSSProperties = {\n margin: 0,\n whiteSpace: \"pre-wrap\",\n fontSize: 15,\n lineHeight: 1.7,\n color: \"#111827\",\n};\n\nconst patchPreviewStyle: CSSProperties = {\n margin: 0,\n maxHeight: 288,\n overflow: \"auto\",\n borderRadius: 12,\n border: \"1px solid #e5e7eb\",\n background: \"#f8fafc\",\n padding: 12,\n fontSize: 13,\n lineHeight: 1.6,\n color: \"#111827\",\n fontFamily:\n \"ui-monospace, SFMono-Regular, SFMono-Regular, Menlo, Consolas, monospace\",\n whiteSpace: \"pre-wrap\",\n wordBreak: \"break-word\",\n};\n\nconst placeholderStyle: CSSProperties = {\n margin: 0,\n fontSize: 14,\n lineHeight: 1.6,\n color: \"#6b7280\",\n};\n\nconst toastStyle: CSSProperties = {\n position: \"fixed\",\n top: 16,\n right: 16,\n zIndex: 2147483600,\n maxWidth: 360,\n borderRadius: 16,\n border: \"1px solid #bbf7d0\",\n background: \"#f0fdf4\",\n color: \"#15803d\",\n padding: \"12px 14px\",\n boxShadow: \"0 12px 28px rgba(15, 23, 42, 0.12)\",\n fontFamily:\n \"Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif\",\n fontSize: 14,\n lineHeight: 1.6,\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/lib/config.ts","../src/components/dev-copilot-provider.tsx","../src/lib/api-client.ts","../src/hooks/use-selection-capture.ts","../src/components/icons.tsx","../src/components/dev-copilot-overlay.tsx"],"sourcesContent":["export interface DevCopilotConfig {\n enabled: boolean;\n allowedPaths: string[];\n}\n\nconst defaultConfig: DevCopilotConfig = {\n enabled: process.env.NODE_ENV === \"development\",\n allowedPaths: [\".\"],\n};\n\nexport const resolveDevCopilotConfig = (\n config?: Partial<DevCopilotConfig>,\n): DevCopilotConfig => {\n const nextConfig = config ?? {};\n\n return {\n ...defaultConfig,\n ...nextConfig,\n };\n};\n","\"use client\";\n\nimport { createContext, useContext, type ReactNode } from \"react\";\n\nimport {\n resolveDevCopilotConfig,\n type DevCopilotConfig,\n} from \"../lib/config\";\n\nconst DevCopilotContext = createContext<DevCopilotConfig | null>(null);\n\ninterface DevCopilotProviderProps {\n config?: Partial<DevCopilotConfig>;\n children: ReactNode;\n}\n\nexport function DevCopilotProvider({\n config,\n children,\n}: DevCopilotProviderProps) {\n const resolvedConfig = resolveDevCopilotConfig(config);\n\n return (\n <DevCopilotContext.Provider value={resolvedConfig}>\n {children}\n </DevCopilotContext.Provider>\n );\n}\n\nexport const useDevCopilotConfig = () => {\n const context = useContext(DevCopilotContext);\n\n if (!context) {\n throw new Error(\"DevCopilotProvider 내부에서만 사용할 수 있습니다.\");\n }\n\n return context;\n};\n","import type {\n CopilotAgent,\n CopilotApplyRequest,\n CopilotApplyResponse,\n CopilotAgentStatusResponse,\n CopilotChatRequest,\n CopilotChatResponse,\n CopilotErrorResponse,\n} from \"../types\";\n\nconst API_BASE_URL = \"http://127.0.0.1:3339\";\nconst BRIDGE_CONNECTION_ERROR_MESSAGE =\n \"브릿지 서버에 연결하지 못했습니다. 브릿지 서버를 켜주세요.\";\n\nconst parseResponse = async <T>(response: Response): Promise<T> => {\n const payload = (await response.json()) as T | CopilotErrorResponse;\n\n if (!response.ok) {\n const errorMessage =\n typeof payload === \"object\" && payload && \"error\" in payload\n ? payload.error\n : \"요청 처리 중 오류가 발생했습니다.\";\n throw new Error(errorMessage);\n }\n\n return payload as T;\n};\n\nconst normalizeNetworkError = (error: unknown): Error => {\n if (\n error instanceof TypeError &&\n error.message.toLowerCase().includes(\"failed to fetch\")\n ) {\n return new Error(BRIDGE_CONNECTION_ERROR_MESSAGE);\n }\n\n if (error instanceof Error) {\n return error;\n }\n\n return new Error(\"요청 처리 중 오류가 발생했습니다.\");\n};\n\nexport const createCopilotApiClient = () => {\n const post = async <T, U>(path: string, body: T): Promise<U> => {\n try {\n const response = await fetch(`${API_BASE_URL}${path}`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(body),\n });\n\n return parseResponse<U>(response);\n } catch (error) {\n throw normalizeNetworkError(error);\n }\n };\n\n return {\n status: async (agent?: CopilotAgent) => {\n const query = agent ? `?agent=${agent}` : \"\";\n try {\n const response = await fetch(`${API_BASE_URL}/status${query}`);\n return parseResponse<CopilotAgentStatusResponse>(response);\n } catch (error) {\n throw normalizeNetworkError(error);\n }\n },\n chat: (payload: CopilotChatRequest) =>\n post<CopilotChatRequest, CopilotChatResponse>(\"/chat\", payload),\n apply: (payload: CopilotApplyRequest) =>\n post<CopilotApplyRequest, CopilotApplyResponse>(\"/apply\", payload),\n };\n};\n","\"use client\";\n\nimport { useCallback, useEffect, useState } from \"react\";\n\nconst OVERLAY_ATTRIBUTE = \"data-dev-copilot-overlay\";\n\nconst toSelectionText = () => {\n const selection = window.getSelection();\n const value = selection?.toString().trim() ?? \"\";\n return value;\n};\n\nconst isNodeInsideOverlay = (node: Node | null) => {\n if (!node) {\n return false;\n }\n\n const element = node instanceof Element ? node : node.parentElement;\n\n return Boolean(element?.closest(`[${OVERLAY_ATTRIBUTE}]`));\n};\n\nconst isInsideOverlay = (target: EventTarget | null) => {\n return (\n target instanceof Element &&\n Boolean(target.closest(`[${OVERLAY_ATTRIBUTE}]`))\n );\n};\n\nexport const useSelectionCapture = () => {\n const [selectedText, setSelectedText] = useState(\"\");\n\n const syncSelection = useCallback((event: MouseEvent | KeyboardEvent) => {\n const selection = window.getSelection();\n const startedInsideOverlay = isNodeInsideOverlay(selection?.anchorNode ?? null);\n const endedInsideOverlay = isNodeInsideOverlay(selection?.focusNode ?? null);\n\n if (isInsideOverlay(event.target) || startedInsideOverlay || endedInsideOverlay) {\n return;\n }\n\n const nextSelectedText = selection?.toString().trim() ?? toSelectionText();\n\n if (!nextSelectedText) {\n return;\n }\n\n setSelectedText(nextSelectedText);\n }, []);\n\n useEffect(() => {\n document.addEventListener(\"mouseup\", syncSelection);\n document.addEventListener(\"keyup\", syncSelection);\n\n return () => {\n document.removeEventListener(\"mouseup\", syncSelection);\n document.removeEventListener(\"keyup\", syncSelection);\n };\n }, [syncSelection]);\n\n return {\n selectedText,\n setSelectedText,\n clearSelection: () => setSelectedText(\"\"),\n };\n};\n\nexport { OVERLAY_ATTRIBUTE };\n","import type { ComponentPropsWithoutRef } from \"react\";\n\ntype SvgIconProps = ComponentPropsWithoutRef<\"svg\">;\n\nexport const SparklesIcon = (props: SvgIconProps) => {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.8\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <path d=\"M12 3.75 13.68 8.32 18.25 10 13.68 11.68 12 16.25 10.32 11.68 5.75 10 10.32 8.32 12 3.75Z\" />\n <path d=\"M18.5 3.75 19.08 5.42 20.75 6 19.08 6.58 18.5 8.25 17.92 6.58 16.25 6 17.92 5.42 18.5 3.75Z\" />\n <path d=\"M6 15.5 7 18.25 9.75 19.25 7 20.25 6 23 5 20.25 2.25 19.25 5 18.25 6 15.5Z\" />\n </svg>\n );\n};\n\nexport const PanelRightOpenIcon = (props: SvgIconProps) => {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.8\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <rect x=\"3.75\" y=\"4.75\" width=\"16.5\" height=\"14.5\" rx=\"2.25\" />\n <path d=\"M8.75 4.75v14.5\" />\n <path d=\"m13 9.25 3 2.75-3 2.75\" />\n </svg>\n );\n};\n\nexport const PanelRightCloseIcon = (props: SvgIconProps) => {\n return (\n <svg\n viewBox=\"0 0 24 24\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth=\"1.8\"\n strokeLinecap=\"round\"\n strokeLinejoin=\"round\"\n {...props}\n >\n <rect x=\"3.75\" y=\"4.75\" width=\"16.5\" height=\"14.5\" rx=\"2.25\" />\n <path d=\"M15.25 4.75v14.5\" />\n <path d=\"m11 9.25-3 2.75 3 2.75\" />\n </svg>\n );\n};\n","\"use client\";\n\nimport {\n useEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n} from \"react\";\n\nimport { createCopilotApiClient } from \"../lib/api-client\";\nimport type {\n CopilotAgent,\n CopilotAgentStatusResponse,\n CopilotChatResponse,\n CopilotMode,\n} from \"../types\";\nimport {\n OVERLAY_ATTRIBUTE,\n useSelectionCapture,\n} from \"../hooks/use-selection-capture\";\nimport {\n PanelRightCloseIcon,\n PanelRightOpenIcon,\n SparklesIcon,\n} from \"./icons\";\nimport { useDevCopilotConfig } from \"./dev-copilot-provider\";\n\nconst FLOATING_BUTTON_SIZE = 48;\nconst DRAG_CLICK_THRESHOLD = 4;\nconst PANEL_WIDTH = \"min(1028px, calc(100vw - 48px))\";\nconst INPUT_PANEL_WIDTH = 420;\nconst RAIL_WIDTH = 36;\nconst AGENT_LABELS: Record<CopilotAgent, string> = {\n codex: \"Codex CLI\",\n claude: \"Claude Code CLI\",\n};\nconst UI_LABELS = {\n title: \"Dev Copilot\",\n subtitle: \"텍스트를 선택한 뒤 프롬프트를 입력하세요.\",\n promptPlaceholder: \"무엇을 도와드릴까요?\",\n askButton: \"질문\",\n editButton: \"코드 수정 제안\",\n applyButton: \"미리보기 적용\",\n} as const;\n\nconst styleText = `\n.yrdc-trigger{transition:transform 150ms ease-out, box-shadow 150ms ease-out}\n.yrdc-trigger:hover{transform:scale(1.05)}\n.yrdc-pressable{transition:transform 150ms ease-out, background-color 150ms ease-out, opacity 150ms ease-out}\n.yrdc-pressable:hover{transform:translateY(-1px)}\n.yrdc-spinner{animation:yrdc-spin 1s linear infinite}\n.yrdc-field:focus{outline:none;box-shadow:0 0 0 3px rgba(59,130,246,.14);border-color:#93c5fd}\n@keyframes yrdc-spin{from{transform:rotate(0deg)}to{transform:rotate(360deg)}}\n`;\n\nexport function DevCopilotOverlay() {\n const config = useDevCopilotConfig();\n const apiClient = useMemo(() => createCopilotApiClient(), []);\n const { selectedText, setSelectedText } = useSelectionCapture();\n\n const [open, setOpen] = useState(false);\n const [prompt, setPrompt] = useState(\"\");\n const [busy, setBusy] = useState(false);\n const [error, setError] = useState<string | null>(null);\n const [chatResult, setChatResult] = useState<CopilotChatResponse | null>(null);\n const [toastMessage, setToastMessage] = useState<string | null>(null);\n const [agentStatus, setAgentStatus] =\n useState<CopilotAgentStatusResponse | null>(null);\n const [selectedAgent, setSelectedAgent] = useState<CopilotAgent>(\"codex\");\n const [position, setPosition] = useState<{ x: number; y: number } | null>(null);\n const [showResponsePanel, setShowResponsePanel] = useState(false);\n\n const containerRef = useRef<HTMLDivElement>(null);\n const suppressMainToggleClickRef = useRef(false);\n const dragStateRef = useRef<{\n pointerId: number;\n startX: number;\n startY: number;\n originX: number;\n originY: number;\n moved: boolean;\n } | null>(null);\n\n useEffect(() => {\n const onResize = () => {\n setPosition((prev) => {\n if (!prev) {\n return prev;\n }\n\n return {\n x: Math.min(\n Math.max(prev.x, 0),\n Math.max(0, window.innerWidth - FLOATING_BUTTON_SIZE),\n ),\n y: Math.min(\n Math.max(prev.y, 0),\n Math.max(0, window.innerHeight - FLOATING_BUTTON_SIZE),\n ),\n };\n });\n };\n\n window.addEventListener(\"resize\", onResize);\n return () => window.removeEventListener(\"resize\", onResize);\n }, []);\n\n useEffect(() => {\n if (!open) {\n return;\n }\n\n let ignore = false;\n\n apiClient\n .status(selectedAgent)\n .then((status) => {\n if (!ignore) {\n setAgentStatus(status);\n }\n })\n .catch((caughtError) => {\n if (!ignore) {\n setAgentStatus({\n available: false,\n authenticated: false,\n agent: selectedAgent,\n message:\n caughtError instanceof Error\n ? caughtError.message\n : \"로컬 에이전트 상태 확인에 실패했습니다.\",\n });\n }\n });\n\n return () => {\n ignore = true;\n };\n }, [apiClient, open, selectedAgent]);\n\n useEffect(() => {\n if (!open) {\n return;\n }\n\n const onPointerDownOutside = (event: PointerEvent) => {\n const target = event.target;\n\n if (!(target instanceof Node)) {\n return;\n }\n\n if (containerRef.current?.contains(target)) {\n return;\n }\n\n setOpen(false);\n setShowResponsePanel(false);\n dragStateRef.current = null;\n suppressMainToggleClickRef.current = false;\n };\n\n document.addEventListener(\"pointerdown\", onPointerDownOutside);\n\n return () => {\n document.removeEventListener(\"pointerdown\", onPointerDownOutside);\n };\n }, [open]);\n\n if (!config.enabled) {\n return null;\n }\n\n const onPointerDown = (event: React.PointerEvent<HTMLButtonElement>) => {\n if (event.button !== 0) {\n return;\n }\n\n const wrapperRect = containerRef.current?.getBoundingClientRect();\n const originX = position?.x ?? wrapperRect?.left ?? 0;\n const originY = position?.y ?? wrapperRect?.top ?? 0;\n\n event.currentTarget.setPointerCapture(event.pointerId);\n dragStateRef.current = {\n pointerId: event.pointerId,\n startX: event.clientX,\n startY: event.clientY,\n originX,\n originY,\n moved: false,\n };\n };\n\n const onPointerMove = (event: React.PointerEvent<HTMLButtonElement>) => {\n const dragState = dragStateRef.current;\n if (!dragState || dragState.pointerId !== event.pointerId) {\n return;\n }\n\n const deltaX = event.clientX - dragState.startX;\n const deltaY = event.clientY - dragState.startY;\n const moved =\n Math.abs(deltaX) > DRAG_CLICK_THRESHOLD ||\n Math.abs(deltaY) > DRAG_CLICK_THRESHOLD;\n\n if (!moved && !dragState.moved) {\n return;\n }\n\n dragState.moved = true;\n\n setPosition({\n x: Math.min(\n Math.max(dragState.originX + deltaX, 0),\n Math.max(0, window.innerWidth - FLOATING_BUTTON_SIZE),\n ),\n y: Math.min(\n Math.max(dragState.originY + deltaY, 0),\n Math.max(0, window.innerHeight - FLOATING_BUTTON_SIZE),\n ),\n });\n };\n\n const onPointerEnd = (event: React.PointerEvent<HTMLButtonElement>) => {\n if (dragStateRef.current?.pointerId !== event.pointerId) {\n return;\n }\n\n suppressMainToggleClickRef.current = dragStateRef.current.moved;\n dragStateRef.current = null;\n event.currentTarget.releasePointerCapture(event.pointerId);\n };\n\n const onMainToggleClick = () => {\n if (suppressMainToggleClickRef.current) {\n suppressMainToggleClickRef.current = false;\n return;\n }\n\n setOpen((prev) => {\n const next = !prev;\n if (!next) {\n setPosition(null);\n dragStateRef.current = null;\n suppressMainToggleClickRef.current = false;\n }\n return next;\n });\n };\n\n const previousResponse = chatResult\n ? [\n `message:\\n${chatResult.message}`,\n chatResult.patchPreview\n ? `patchPreview:\\n${chatResult.patchPreview}`\n : \"\",\n ]\n .filter(Boolean)\n .join(\"\\n\\n\")\n : undefined;\n\n const onSubmit = async (mode: CopilotMode) => {\n if (agentStatus && (!agentStatus.available || !agentStatus.authenticated)) {\n setError(agentStatus.message);\n return;\n }\n\n if (!prompt.trim()) {\n setError(\"프롬프트를 입력해 주세요.\");\n return;\n }\n\n setBusy(true);\n setError(null);\n setShowResponsePanel(true);\n\n try {\n const currentRoute =\n typeof window !== \"undefined\" ? window.location.pathname : undefined;\n const result = await apiClient.chat({\n selectedText,\n prompt,\n mode,\n context: {\n route: currentRoute,\n fileHints: config.allowedPaths,\n previousResponse,\n agent: selectedAgent,\n },\n });\n\n setChatResult(result);\n } catch (caughtError) {\n setError(\n caughtError instanceof Error\n ? caughtError.message\n : \"요청 처리 중 오류가 발생했습니다.\",\n );\n } finally {\n setBusy(false);\n }\n };\n\n const onApply = async () => {\n if (!chatResult?.patchId) {\n setError(\"적용 가능한 패치가 없습니다.\");\n return;\n }\n\n setBusy(true);\n setError(null);\n setShowResponsePanel(true);\n\n try {\n const result = await apiClient.apply({\n patchId: chatResult.patchId,\n approvalToken: `approve:${chatResult.patchId}`,\n });\n\n if (result.applied) {\n setToastMessage(result.summary);\n window.setTimeout(() => {\n setToastMessage(null);\n }, 3000);\n }\n } catch (caughtError) {\n setError(\n caughtError instanceof Error\n ? caughtError.message\n : \"패치 적용 중 오류가 발생했습니다.\",\n );\n } finally {\n setBusy(false);\n }\n };\n\n const floatingWrapperStyle: CSSProperties = position\n ? {\n position: \"fixed\",\n zIndex: 2147483000,\n left: `${position.x}px`,\n top: `${position.y}px`,\n }\n : {\n position: \"fixed\",\n right: 24,\n bottom: 24,\n zIndex: 2147483000,\n };\n\n const panelStyle: CSSProperties = {\n position: \"relative\",\n marginTop: 12,\n display: \"grid\",\n gridTemplateColumns: showResponsePanel\n ? `${INPUT_PANEL_WIDTH}px ${RAIL_WIDTH}px minmax(0, 1fr)`\n : `${INPUT_PANEL_WIDTH}px ${RAIL_WIDTH}px`,\n width: showResponsePanel ? PANEL_WIDTH : INPUT_PANEL_WIDTH + RAIL_WIDTH,\n maxHeight: \"calc(100vh - 96px)\",\n overflow: \"hidden\",\n border: \"1px solid #e5e7eb\",\n borderRadius: 24,\n background: \"#f8fafc\",\n boxShadow: \"0 18px 60px rgba(15, 23, 42, 0.16)\",\n color: \"#0f172a\",\n fontFamily:\n \"Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif\",\n };\n\n return (\n <>\n <style>{styleText}</style>\n\n {toastMessage ? (\n <div style={toastStyle}>\n <p aria-live=\"polite\" style={{ margin: 0 }}>\n {toastMessage}\n </p>\n </div>\n ) : null}\n\n <div ref={containerRef} style={floatingWrapperStyle} {...{ [OVERLAY_ATTRIBUTE]: \"\" }}>\n <div>\n <button\n type=\"button\"\n className=\"yrdc-trigger\"\n style={triggerButtonStyle}\n onClick={onMainToggleClick}\n onPointerDown={onPointerDown}\n onPointerMove={onPointerMove}\n onPointerUp={onPointerEnd}\n onPointerCancel={onPointerEnd}\n aria-label=\"Dev Copilot 열기\"\n title=\"Dev Copilot 열기/닫기 / 드래그해서 이동\"\n >\n <SparklesIcon aria-hidden style={{ width: 16, height: 16 }} />\n </button>\n </div>\n\n {open ? (\n <section style={panelStyle}>\n <div style={inputPanelStyle}>\n <header>\n <p style={titleStyle}>{UI_LABELS.title}</p>\n <p style={subtitleStyle}>{UI_LABELS.subtitle}</p>\n </header>\n\n <div style={statusBoxStyle}>\n <p style={statusLineStyle}>로컬 에이전트: {AGENT_LABELS[selectedAgent]}</p>\n {agentStatus?.model ? (\n <p style={statusLineStyle}>모델: {agentStatus.model}</p>\n ) : null}\n {agentStatus ? (\n <p\n style={{\n ...statusLineStyle,\n color: agentStatus.authenticated ? \"#15803d\" : \"#dc2626\",\n }}\n >\n {agentStatus.message}\n {agentStatus.loginCommand &&\n !agentStatus.message.includes(agentStatus.loginCommand)\n ? ` 터미널에서 ${agentStatus.loginCommand} 실행`\n : \"\"}\n </p>\n ) : null}\n </div>\n\n <label style={labelStyle}>에이전트</label>\n <div style={agentToggleRowStyle}>\n {([\"codex\", \"claude\"] as CopilotAgent[]).map((agent) => {\n const active = selectedAgent === agent;\n return (\n <button\n key={agent}\n type=\"button\"\n className=\"yrdc-pressable\"\n onClick={() => setSelectedAgent(agent)}\n disabled={busy}\n style={{\n ...agentToggleButtonStyle,\n background: active ? \"#111827\" : \"#e2e8f0\",\n color: active ? \"#ffffff\" : \"#1e293b\",\n opacity: busy ? 0.6 : 1,\n }}\n >\n {AGENT_LABELS[agent]}\n </button>\n );\n })}\n </div>\n\n <label style={labelStyle}>선택 텍스트</label>\n <textarea\n value={selectedText}\n onChange={(event) => setSelectedText(event.target.value)}\n rows={5}\n className=\"yrdc-field\"\n style={textareaStyle}\n />\n\n <label style={labelStyle}>프롬프트</label>\n <textarea\n value={prompt}\n onChange={(event) => setPrompt(event.target.value)}\n rows={4}\n placeholder={UI_LABELS.promptPlaceholder}\n className=\"yrdc-field\"\n style={textareaStyle}\n />\n\n <div style={buttonRowStyle}>\n <button\n type=\"button\"\n className=\"yrdc-pressable\"\n onClick={() => onSubmit(\"answer\")}\n disabled={busy || Boolean(agentStatus && !agentStatus.authenticated)}\n style={{\n ...buttonStyle,\n background: \"#111827\",\n color: \"#ffffff\",\n opacity: busy || Boolean(agentStatus && !agentStatus.authenticated) ? 0.55 : 1,\n }}\n >\n {UI_LABELS.askButton}\n </button>\n <button\n type=\"button\"\n className=\"yrdc-pressable\"\n onClick={() => onSubmit(\"edit\")}\n disabled={busy || Boolean(agentStatus && !agentStatus.authenticated)}\n style={{\n ...buttonStyle,\n background: \"#2563eb\",\n color: \"#ffffff\",\n opacity: busy || Boolean(agentStatus && !agentStatus.authenticated) ? 0.55 : 1,\n }}\n >\n {UI_LABELS.editButton}\n </button>\n </div>\n </div>\n\n <div style={railStyle}>\n <button\n type=\"button\"\n className=\"yrdc-pressable\"\n style={railToggleStyle}\n onClick={() => setShowResponsePanel((prev) => !prev)}\n aria-label={showResponsePanel ? \"응답 패널 닫기\" : \"응답 패널 열기\"}\n title={showResponsePanel ? \"응답 패널 닫기\" : \"응답 패널 열기\"}\n >\n {showResponsePanel ? (\n <PanelRightCloseIcon aria-hidden style={{ width: 14, height: 14 }} />\n ) : (\n <PanelRightOpenIcon aria-hidden style={{ width: 14, height: 14 }} />\n )}\n </button>\n </div>\n\n {showResponsePanel ? (\n <aside style={responsePanelStyle}>\n <div style={{ display: \"flex\", flexDirection: \"column\", minHeight: 320, height: \"100%\" }}>\n <div>\n <p style={titleStyle}>응답</p>\n </div>\n\n <div style={responseCardStyle}>\n {busy ? (\n <div style={busyContainerStyle}>\n <span className=\"yrdc-spinner\" style={spinnerStyle} />\n <span>{AGENT_LABELS[selectedAgent]} 응답을 기다리는 중입니다.</span>\n </div>\n ) : chatResult ? (\n <article style={articleStyle}>\n {error ? (\n <p style={errorBoxStyle}>{error}</p>\n ) : null}\n {chatResult.warnings.length ? (\n <div style={warningBoxStyle}>\n {chatResult.warnings.map((warning) => (\n <p key={warning} style={{ margin: 0 }}>\n {warning}\n </p>\n ))}\n </div>\n ) : null}\n <p style={messageStyle}>{chatResult.message}</p>\n\n {chatResult.patchPreview ? (\n <>\n <pre style={patchPreviewStyle}>{chatResult.patchPreview}</pre>\n {chatResult.patchId ? (\n <button\n type=\"button\"\n className=\"yrdc-pressable\"\n onClick={onApply}\n disabled={busy}\n style={{\n ...buttonStyle,\n alignSelf: \"flex-start\",\n background: \"#15803d\",\n color: \"#ffffff\",\n opacity: busy ? 0.55 : 1,\n }}\n >\n {UI_LABELS.applyButton}\n </button>\n ) : null}\n </>\n ) : null}\n </article>\n ) : error ? (\n <p style={errorBoxStyle}>{error}</p>\n ) : (\n <p style={placeholderStyle}>\n 질문 또는 코드 수정 제안을 실행하면 이 영역에 결과가 표시됩니다.\n </p>\n )}\n </div>\n </div>\n </aside>\n ) : null}\n </section>\n ) : null}\n </div>\n </>\n );\n}\n\nconst triggerButtonStyle: CSSProperties = {\n display: \"flex\",\n width: FLOATING_BUTTON_SIZE,\n height: FLOATING_BUTTON_SIZE,\n borderRadius: 9999,\n border: \"1px solid #f2d675\",\n background: \"#fff4b8\",\n color: \"#ffb03d\",\n alignItems: \"center\",\n justifyContent: \"center\",\n boxShadow: \"0 12px 28px rgba(15, 23, 42, 0.18)\",\n cursor: \"grab\",\n};\n\nconst railStyle: CSSProperties = {\n display: \"flex\",\n minHeight: 320,\n alignItems: \"center\",\n justifyContent: \"center\",\n borderLeft: \"1px solid #e5e7eb\",\n background: \"#f8fafc\",\n};\n\nconst railToggleStyle: CSSProperties = {\n display: \"flex\",\n width: 24,\n height: 24,\n border: \"0\",\n background: \"transparent\",\n color: \"#64748b\",\n cursor: \"pointer\",\n alignItems: \"center\",\n justifyContent: \"center\",\n};\n\nconst inputPanelStyle: CSSProperties = {\n padding: 16,\n minHeight: 0,\n overflowY: \"auto\",\n background: \"#ffffff\",\n};\n\nconst responsePanelStyle: CSSProperties = {\n minHeight: 0,\n overflow: \"hidden\",\n padding: 16,\n borderLeft: \"1px solid #e5e7eb\",\n background: \"#f8fafc\",\n};\n\nconst titleStyle: CSSProperties = {\n margin: 0,\n fontSize: 18,\n fontWeight: 700,\n lineHeight: 1.4,\n color: \"#111827\",\n};\n\nconst subtitleStyle: CSSProperties = {\n margin: \"4px 0 0\",\n fontSize: 14,\n lineHeight: 1.5,\n color: \"#6b7280\",\n};\n\nconst statusBoxStyle: CSSProperties = {\n marginTop: 12,\n border: \"1px solid #e5e7eb\",\n borderRadius: 16,\n background: \"#ffffff\",\n padding: \"12px 14px\",\n fontSize: 14,\n lineHeight: 1.6,\n color: \"#6b7280\",\n};\n\nconst statusLineStyle: CSSProperties = {\n margin: 0,\n};\n\nconst labelStyle: CSSProperties = {\n display: \"block\",\n marginTop: 16,\n fontSize: 14,\n lineHeight: 1.5,\n color: \"#6b7280\",\n};\n\nconst textareaStyle: CSSProperties = {\n width: \"100%\",\n resize: \"vertical\",\n borderRadius: 16,\n border: \"1px solid #e5e7eb\",\n background: \"#ffffff\",\n padding: \"12px 14px\",\n marginTop: 6,\n fontSize: 15,\n lineHeight: 1.7,\n color: \"#111827\",\n boxSizing: \"border-box\",\n minHeight: 132,\n};\n\nconst agentToggleRowStyle: CSSProperties = {\n display: \"flex\",\n gap: 8,\n marginTop: 6,\n};\n\nconst agentToggleButtonStyle: CSSProperties = {\n border: 0,\n borderRadius: 10,\n padding: \"8px 12px\",\n fontSize: 13,\n fontWeight: 600,\n lineHeight: 1.2,\n cursor: \"pointer\",\n};\n\nconst buttonRowStyle: CSSProperties = {\n display: \"flex\",\n alignItems: \"center\",\n gap: 8,\n marginTop: 16,\n};\n\nconst buttonStyle: CSSProperties = {\n border: 0,\n borderRadius: 12,\n padding: \"10px 14px\",\n fontSize: 14,\n fontWeight: 600,\n lineHeight: 1,\n cursor: \"pointer\",\n};\n\nconst responseCardStyle: CSSProperties = {\n marginTop: 12,\n minHeight: 0,\n flex: 1,\n overflowY: \"auto\",\n borderRadius: 18,\n border: \"1px solid #e5e7eb\",\n background: \"#ffffff\",\n padding: 16,\n};\n\nconst busyContainerStyle: CSSProperties = {\n display: \"flex\",\n height: \"100%\",\n minHeight: 192,\n alignItems: \"center\",\n justifyContent: \"center\",\n gap: 12,\n color: \"#6b7280\",\n fontSize: 14,\n};\n\nconst spinnerStyle: CSSProperties = {\n display: \"inline-block\",\n width: 18,\n height: 18,\n borderRadius: 9999,\n border: \"2px solid #d1d5db\",\n borderTopColor: \"#2563eb\",\n};\n\nconst articleStyle: CSSProperties = {\n display: \"flex\",\n flexDirection: \"column\",\n gap: 12,\n};\n\nconst errorBoxStyle: CSSProperties = {\n margin: 0,\n border: \"1px solid #fca5a5\",\n borderRadius: 12,\n background: \"#fef2f2\",\n padding: \"10px 12px\",\n fontSize: 14,\n lineHeight: 1.6,\n color: \"#dc2626\",\n whiteSpace: \"pre-wrap\",\n};\n\nconst warningBoxStyle: CSSProperties = {\n display: \"flex\",\n flexDirection: \"column\",\n gap: 6,\n border: \"1px solid #fca5a5\",\n borderRadius: 12,\n background: \"#fef2f2\",\n padding: \"10px 12px\",\n fontSize: 14,\n lineHeight: 1.6,\n color: \"#dc2626\",\n};\n\nconst messageStyle: CSSProperties = {\n margin: 0,\n whiteSpace: \"pre-wrap\",\n fontSize: 15,\n lineHeight: 1.7,\n color: \"#111827\",\n};\n\nconst patchPreviewStyle: CSSProperties = {\n margin: 0,\n maxHeight: 288,\n overflow: \"auto\",\n borderRadius: 12,\n border: \"1px solid #e5e7eb\",\n background: \"#f8fafc\",\n padding: 12,\n fontSize: 13,\n lineHeight: 1.6,\n color: \"#111827\",\n fontFamily:\n \"ui-monospace, SFMono-Regular, SFMono-Regular, Menlo, Consolas, monospace\",\n whiteSpace: \"pre-wrap\",\n wordBreak: \"break-word\",\n};\n\nconst placeholderStyle: CSSProperties = {\n margin: 0,\n fontSize: 14,\n lineHeight: 1.6,\n color: \"#6b7280\",\n};\n\nconst toastStyle: CSSProperties = {\n position: \"fixed\",\n top: 16,\n right: 16,\n zIndex: 2147483600,\n maxWidth: 360,\n borderRadius: 16,\n border: \"1px solid #bbf7d0\",\n background: \"#f0fdf4\",\n color: \"#15803d\",\n padding: \"12px 14px\",\n boxShadow: \"0 12px 28px rgba(15, 23, 42, 0.12)\",\n fontFamily:\n \"Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif\",\n fontSize: 14,\n lineHeight: 1.6,\n};\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yr-kits/dev-copilot",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "선택 텍스트 기반 Dev Copilot 오버레이와 로컬 브리지 실행 CLI",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.mjs",
|
|
@@ -9,11 +9,13 @@
|
|
|
9
9
|
"exports": {
|
|
10
10
|
".": {
|
|
11
11
|
"types": "./dist/index.d.mts",
|
|
12
|
-
"import": "./dist/index.mjs"
|
|
12
|
+
"import": "./dist/index.mjs",
|
|
13
|
+
"default": "./dist/index.mjs"
|
|
13
14
|
},
|
|
14
15
|
"./types": {
|
|
15
16
|
"types": "./dist/types/index.d.mts",
|
|
16
|
-
"import": "./dist/types/index.mjs"
|
|
17
|
+
"import": "./dist/types/index.mjs",
|
|
18
|
+
"default": "./dist/types/index.mjs"
|
|
17
19
|
}
|
|
18
20
|
},
|
|
19
21
|
"bin": {
|
|
@@ -57,8 +59,8 @@
|
|
|
57
59
|
"build": "tsdown",
|
|
58
60
|
"dev": "tsdown --watch",
|
|
59
61
|
"test": "echo \"No tests yet\"",
|
|
60
|
-
"release:patch": "
|
|
61
|
-
"release:minor": "
|
|
62
|
-
"release:major": "
|
|
62
|
+
"release:patch": "npm version patch --no-git-tag-version && pnpm publish --access public --no-git-checks",
|
|
63
|
+
"release:minor": "npm version minor --no-git-tag-version && pnpm publish --access public --no-git-checks",
|
|
64
|
+
"release:major": "npm version major --no-git-tag-version && pnpm publish --access public --no-git-checks"
|
|
63
65
|
}
|
|
64
66
|
}
|