@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.
@@ -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 toClaudeErrorMessage = (error) => {
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
- if (/401|authentication|invalid authentication credentials|please run \/login/i.test(message)) return "Claude Code 로그인이 필요합니다. 터미널에서 `claude /login`을 실행해 주세요.";
89
- if (/ENOENT/.test(message)) return "Claude CLI를 찾을 없습니다. Claude Code CLI 설치 상태를 확인해 주세요.";
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 message = error instanceof Error ? error.message : String(error);
193
- const unavailable = /ENOENT/.test(message);
194
- const authError = /401|authentication|please run \/login|invalid authentication credentials/i.test(message);
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 로그인이 필요합니다." : message,
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: ["message"],
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 = /logged in/i.test(output);
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, `${patchPreview.trimEnd()}\n`, "utf-8");
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}`).trim();
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").trim();
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, `${patchPreview.trimEnd()}\n`, "utf-8");
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 ?? "에이전트가 수정안을 만들었지만 실제 파일 내용과 매칭하지 못했습니다.",
@@ -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":"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"}
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
- return parseResponse(await fetch(`${API_BASE_URL}${path}`, {
47
- method: "POST",
48
- headers: { "Content-Type": "application/json" },
49
- body: JSON.stringify(body)
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
- return parseResponse(await fetch(`${API_BASE_URL}/status${query}`));
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", {
@@ -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.0",
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": "pnpm version patch --no-git-tag-version && pnpm publish --access public --no-git-checks",
61
- "release:minor": "pnpm version minor --no-git-tag-version && pnpm publish --access public --no-git-checks",
62
- "release:major": "pnpm version major --no-git-tag-version && pnpm publish --access public --no-git-checks"
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
  }