makecc 0.2.10 → 0.2.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/client/assets/{index-B9j5oIcH.js → index-lpZin4sA.js} +1 -1
- package/dist/client/index.html +1 -1
- package/dist/server/index.js +13 -1
- package/dist/server/services/skillGeneratorService.js +21 -8
- package/dist/server/services/workflowAIService.js +21 -16
- package/package.json +1 -1
- package/server/index.ts +13 -1
- package/server/services/skillGeneratorService.ts +24 -8
- package/server/services/workflowAIService.ts +24 -16
|
@@ -98,7 +98,7 @@ ${r.outputType||"auto"}
|
|
|
98
98
|
${new Date().toISOString()}`;return n.onNodeProgress(e.id,100),n.onLog("success","Workflow completed successfully",e.id),a}async function IR(e,t,n){const r={results:new Map,cancelled:!1},a=RR(e,t);n.onLog("info",`Starting workflow execution with ${a.length} nodes`);for(const l of a){n.onNodeStart(l.id);try{const u=zR(l.id,t,r.results);let f;switch(l.type){case"input":f=await OR(l,n);break;case"subagent":f=await DR(l,u,n);break;case"skill":f=await LR(l,u,n);break;case"command":f=await BR(l,u,n);break;case"hook":f=await HR(l,u,n);break;case"output":f=await UR(l,u,n);break;default:f=u}r.results.set(l.id,f),n.onNodeComplete(l.id,f)}catch(u){const f=u instanceof Error?u.message:"Unknown error";throw n.onNodeError(l.id,f),n.onLog("error",`Node ${l.data.label} failed: ${f}`,l.id),u}}return r.results}function D_(){const{nodes:e,edges:t,workflowName:n,updateNodeStatus:r}=ci(),{isRunning:a,startExecution:l,stopExecution:u,setCurrentNode:f,markNodeCompleted:d,markNodeFailed:h,setWorkflowResults:g,addLog:m,clearLogs:x}=Na(),[b,w]=X.useState([]),k=X.useCallback(async()=>{if(e.length===0){m("warning","No nodes to execute");return}x(),l(),w([]),m("info",`워크플로우 "${n}" 실행 시작...`);try{const z=await IR(e,t,{onNodeStart:L=>{r(L,"running",0),f(L)},onNodeProgress:(L,A)=>{r(L,"running",A)},onNodeComplete:(L,A)=>{r(L,"completed",100),d(L,A)},onNodeError:(L,A)=>{r(L,"error",0),h(L,A)},onLog:(L,A,B)=>{m(L,A,B)}}),E=[],C=[];for(const[L,A]of z){const B=e.find(Z=>Z.id===L),G=typeof A=="string"?A:JSON.stringify(A,null,2);if(E.push({nodeId:L,label:B?.data.label||L,success:!0,result:G}),B?.type==="output"){const Z=`${n.replace(/\s+/g,"_")}_${B.data.label.replace(/\s+/g,"_")}.md`;C.push({name:Z,content:G})}}w(C),g(E),u(),m("success","워크플로우 실행 완료!"),C.length>0&&(m("info",`${C.length}개 파일 생성됨`),C.forEach(L=>{m("info",` - ${L.name}`)}))}catch(z){const E=z instanceof Error?z.message:"Unknown error";u(),m("error",`실행 오류: ${E}`)}},[e,t,n,m,x,l,u,r,f,d,h,g]),N=X.useCallback((z,E)=>{const C=new Blob([E],{type:"text/markdown;charset=utf-8"}),L=URL.createObjectURL(C),A=document.createElement("a");A.href=L,A.download=z,document.body.appendChild(A),A.click(),document.body.removeChild(A),URL.revokeObjectURL(L),m("info",`파일 다운로드: ${z}`)},[m]),_=X.useCallback(()=>{b.forEach(z=>{N(z.name,z.content)})},[b,N]);return{isRunning:a,execute:k,generatedFiles:b,downloadFile:N,downloadAllFiles:_}}function qR(e,t,n,r="local"){const a=r==="global"?"~/.claude":".claude",l=$R(t,a),u=PR(t,a),f=VR(e,t,n,a),d=FR(t);return{skills:l,commands:f,agents:u,mcpSettings:d}}function $R(e,t){return e.filter(r=>r.type==="skill").filter(r=>r.data.skillType==="custom").map(r=>{const a=r.data,l=Ta(a.skillId||a.label),u=`${t}/skills/${l}/SKILL.md`,f=YR(a);return{name:l,path:u,content:f}})}function PR(e,t){return e.filter(r=>r.type==="subagent").map(r=>{const a=r.data,l=Ta(a.label),u=`${t}/agents/${l}.md`,f=XR(a);return{name:l,path:u,content:f}})}function VR(e,t,n,r){if(t.length===0)return[];const a=Ta(e),l=`${r}/commands/${a}.md`,u=GR(e,t,n);return[{name:a,path:l,content:u}]}function FR(e){return e.filter(n=>n.type==="hook").length===0,null}function YR(e){if(e.skillContent)return e.skillContent;const t=Ta(e.skillId||e.label),n=e.description||e.label,r=["---",`name: ${t}`,`description: ${n}`,"---","",`# ${e.label}`,""];return e.mdContent?r.push(e.mdContent):r.push(`Execute the ${e.label} skill.`),r.join(`
|
|
99
99
|
`)}function XR(e){const t=Ta(e.label),n=e.description||e.label,r=e.tools.length>0?e.tools.join(", "):"Read, Write, Edit",a=e.model||"sonnet",l=["---",`name: ${t}`,`description: ${n}`,`tools: ${r}`,`model: ${a}`,"---",""];return e.systemPrompt?l.push(e.systemPrompt):e.mdContent?l.push(e.mdContent):(l.push(`# ${e.label}`),l.push(""),l.push(`This agent performs the ${e.role} role.`)),l.join(`
|
|
100
100
|
`)}function GR(e,t,n){const r=t.filter(g=>g.type==="input"),a=t.filter(g=>g.type==="output"),l=r.map(g=>`<${Ta(g.data.label)}>`).join(" "),f=["---",`description: ${`Workflow: ${e}`}`];l&&f.push(`argument-hint: ${l}`),f.push("---"),f.push(""),f.push(`# ${e}`),f.push(""),r.length>0&&(f.push("## Inputs"),f.push(""),r.forEach(g=>{const m=g.data;f.push(`- **${m.label}**: ${m.description||m.placeholder||"User input"}`)}),f.push("")),f.push("## Workflow Steps"),f.push("");const d=ZR(t,n);let h=1;return d.forEach(g=>{if(g.type!=="input"){if(f.push(`${h}. **${g.data.label}**`),g.data.description&&f.push(` ${g.data.description}`),g.type==="subagent"){const m=g.data;m.systemPrompt&&f.push(` - Prompt: ${m.systemPrompt.substring(0,100)}${m.systemPrompt.length>100?"...":""}`),m.tools.length>0&&f.push(` - Tools: ${m.tools.join(", ")}`)}if(g.type==="skill"){const m=g.data;m.skillId&&f.push(` - Invoke skill: ${m.skillId}`)}if(g.type==="command"){const m=g.data;f.push(` - Command: /${m.commandName}`)}if(g.type==="hook"){const m=g.data;f.push(` - Hook: ${m.hookEvent} (${m.hookMatcher||"*"})`)}f.push(""),h++}}),a.length>0&&(f.push("## Outputs"),f.push(""),a.forEach(g=>{const m=g.data;f.push(`- **${m.label}**: ${m.outputType} format`)})),f.join(`
|
|
101
|
-
`)}function Ta(e){return e.toLowerCase().replace(/\s+/g,"-").replace(/[^a-z0-9-]/g,"").replace(/-+/g,"-").replace(/^-|-$/g,"")}function ZR(e,t){const n=new Map(e.map(f=>[f.id,f])),r=new Map,a=new Map;e.forEach(f=>{r.set(f.id,0),a.set(f.id,[])}),t.forEach(f=>{a.get(f.source)?.push(f.target),r.set(f.target,(r.get(f.target)||0)+1)});const l=[];r.forEach((f,d)=>{f===0&&l.push(d)});const u=[];for(;l.length>0;){const f=l.shift(),d=n.get(f);d&&u.push(d),a.get(f)?.forEach(h=>{const g=(r.get(h)||0)-1;r.set(h,g),g===0&&l.push(h)})}return u}async function L_(){try{const e=await fetch("/api/load/claude-config");if(!e.ok)throw new Error(`Failed to load config: ${e.statusText}`);const t=await e.json();return QR(t)}catch(e){return console.error("Failed to load Claude config:",e),[]}}function QR(e){const t=[];let n=0;const r=280,a=150;for(const l of e.skills)t.push({id:l.id,type:"skill",position:{x:100+n,y:a},data:{label:l.label,description:l.description,status:"idle",skillType:"custom",skillId:l.skillId,skillPath:l.skillPath}}),n+=r;for(const l of e.subagents)t.push({id:l.id,type:"subagent",position:{x:100+n,y:a},data:{label:l.label,description:l.description,role:"custom",tools:l.tools,model:l.model,skills:l.skills,systemPrompt:l.systemPrompt,status:"idle",usedInputs:[]}}),n+=r;for(const l of e.commands)t.push({id:l.id,type:"command",position:{x:100+n,y:a},data:{label:l.label,description:l.description,commandName:l.commandName,commandContent:l.commandContent,status:"idle",usedInputs:[]}}),n+=r;for(const l of e.hooks)t.push({id:l.id,type:"hook",position:{x:100+n,y:a},data:{label:l.label,description:l.description,hookEvent:l.hookEvent,hookMatcher:l.hookMatcher,hookCommand:l.hookCommand,status:"idle",usedInputs:[]}}),n+=r;return t}function B_(e){var t,n,r="";if(typeof e=="string"||typeof e=="number")r+=e;else if(typeof e=="object")if(Array.isArray(e)){var a=e.length;for(t=0;t<a;t++)e[t]&&(n=B_(e[t]))&&(r&&(r+=" "),r+=n)}else for(n in e)e[n]&&(r&&(r+=" "),r+=n);return r}function Gt(){for(var e,t,n=0,r="",a=arguments.length;n<a;n++)(e=arguments[n])&&(t=B_(e))&&(r&&(r+=" "),r+=t);return r}const H_="http://localhost:3001/api";async function KR(e,t){const n=await fetch(`${H_}/save/workflow`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({config:e,options:t})});if(!n.ok){const r=await n.json().catch(()=>({}));throw new Error(r.message||"Failed to save workflow")}return n.json()}async function JR(){const e=await fetch(`${H_}/project-path`);if(!e.ok)throw new Error("Failed to get project path");return(await e.json()).path}async function WR(e,t){const n=[];return t.includeSkills&&e.skills.forEach(r=>{n.push({path:r.path,content:r.content,type:"skill"})}),t.includeCommands&&e.commands.forEach(r=>{n.push({path:r.path,content:r.content,type:"command"})}),t.includeAgents&&e.agents.forEach(r=>{n.push({path:r.path,content:r.content,type:"agent"})}),t.includeMcpSettings&&e.mcpSettings&&n.push({path:t.location==="global"?"~/.claude/settings.local.json":".claude/settings.local.json",content:JSON.stringify(e.mcpSettings,null,2),type:"mcp"}),{files:n}}function ez({isOpen:e,onClose:t,config:n}){const[r,a]=X.useState("local"),[l,u]=X.useState(!0),[f,d]=X.useState(!0),[h,g]=X.useState(!0),[m,x]=X.useState(!0),[b,w]=X.useState(""),[k,N]=X.useState([]),[_,z]=X.useState(null),[E,C]=X.useState(!1),[L,A]=X.useState(null),[B,G]=X.useState(null);X.useEffect(()=>{e&&JR().then(w).catch(()=>w("."))},[e]),X.useEffect(()=>{if(!e)return;WR(n,{location:r,includeSkills:l,includeCommands:f,includeAgents:h,includeMcpSettings:m}).then(({files:te})=>{N(te),te.length>0&&!_&&z(te[0])})},[e,n,r,l,f,h,m]);const Z=async()=>{C(!0),G(null),A(null);const P={location:r,includeSkills:l,includeCommands:f,includeAgents:h,includeMcpSettings:m};try{const te=await KR(n,P);A(te),te.success&&setTimeout(()=>t(),2e3)}catch(te){G(te instanceof Error?te.message:"Failed to save")}finally{C(!1)}},H=P=>{switch(P){case"skill":return v.jsx(ha,{className:"w-4 h-4 text-node-skill"});case"agent":return v.jsx(R5,{className:"w-4 h-4 text-node-generate"});case"command":return v.jsx(ds,{className:"w-4 h-4 text-accent"});case"mcp":return v.jsx(KS,{className:"w-4 h-4 text-node-mcp"});default:return v.jsx(ha,{className:"w-4 h-4 text-gray-400"})}},$=n.skills.length>0||n.commands.length>0||n.agents.length>0||n.mcpSettings;return e?v.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm",children:v.jsxs("div",{className:"bg-surface border border-border rounded-xl shadow-2xl w-[900px] max-h-[80vh] flex flex-col",children:[v.jsxs("div",{className:"flex items-center justify-between px-6 py-4 border-b border-border",children:[v.jsxs("div",{className:"flex items-center gap-3",children:[v.jsx(da,{className:"w-5 h-5 text-accent"}),v.jsx("h2",{className:"text-lg font-semibold text-white",children:"Export to Claude Code"})]}),v.jsx("button",{onClick:t,className:"p-2 hover:bg-surface-hover rounded-lg transition-colors",children:v.jsx(rg,{className:"w-5 h-5 text-gray-400"})})]}),v.jsxs("div",{className:"flex flex-1 overflow-hidden",children:[v.jsxs("div",{className:"w-64 border-r border-border p-4 space-y-6 overflow-y-auto",children:[v.jsxs("div",{children:[v.jsx("label",{className:"block text-sm font-medium text-gray-300 mb-3",children:"Save Location"}),v.jsxs("div",{className:"space-y-2",children:[v.jsxs("button",{onClick:()=>a("local"),className:Gt("w-full flex items-center gap-3 px-3 py-2 rounded-lg border transition-colors text-left",r==="local"?"border-accent bg-accent/10 text-white":"border-border hover:border-gray-500 text-gray-400"),children:[v.jsx(O1,{className:"w-4 h-4"}),v.jsxs("div",{children:[v.jsx("div",{className:"text-sm font-medium",children:"Local"}),v.jsx("div",{className:"text-xs text-gray-500",children:".claude/"})]})]}),v.jsxs("button",{onClick:()=>a("global"),className:Gt("w-full flex items-center gap-3 px-3 py-2 rounded-lg border transition-colors text-left",r==="global"?"border-accent bg-accent/10 text-white":"border-border hover:border-gray-500 text-gray-400"),children:[v.jsx(O1,{className:"w-4 h-4"}),v.jsxs("div",{children:[v.jsx("div",{className:"text-sm font-medium",children:"Global"}),v.jsx("div",{className:"text-xs text-gray-500",children:"~/.claude/"})]})]})]})]}),v.jsxs("div",{children:[v.jsx("label",{className:"block text-sm font-medium text-gray-300 mb-3",children:"Include Files"}),v.jsxs("div",{className:"space-y-2",children:[v.jsxs("label",{className:"flex items-center gap-3 cursor-pointer",children:[v.jsx("input",{type:"checkbox",checked:l,onChange:P=>u(P.target.checked),disabled:n.skills.length===0,className:"w-4 h-4 rounded border-border bg-surface text-accent focus:ring-accent"}),v.jsxs("span",{className:Gt("text-sm",n.skills.length===0?"text-gray-600":"text-gray-300"),children:["Skills (",n.skills.length,")"]})]}),v.jsxs("label",{className:"flex items-center gap-3 cursor-pointer",children:[v.jsx("input",{type:"checkbox",checked:f,onChange:P=>d(P.target.checked),disabled:n.commands.length===0,className:"w-4 h-4 rounded border-border bg-surface text-accent focus:ring-accent"}),v.jsxs("span",{className:Gt("text-sm",n.commands.length===0?"text-gray-600":"text-gray-300"),children:["Commands (",n.commands.length,")"]})]}),v.jsxs("label",{className:"flex items-center gap-3 cursor-pointer",children:[v.jsx("input",{type:"checkbox",checked:h,onChange:P=>g(P.target.checked),disabled:n.agents.length===0,className:"w-4 h-4 rounded border-border bg-surface text-accent focus:ring-accent"}),v.jsxs("span",{className:Gt("text-sm",n.agents.length===0?"text-gray-600":"text-gray-300"),children:["Agents (",n.agents.length,")"]})]}),v.jsxs("label",{className:"flex items-center gap-3 cursor-pointer",children:[v.jsx("input",{type:"checkbox",checked:m,onChange:P=>x(P.target.checked),disabled:!n.mcpSettings,className:"w-4 h-4 rounded border-border bg-surface text-accent focus:ring-accent"}),v.jsx("span",{className:Gt("text-sm",n.mcpSettings?"text-gray-300":"text-gray-600"),children:"MCP Settings"})]})]})]}),v.jsxs("div",{className:"pt-4 border-t border-border",children:[v.jsx("div",{className:"text-xs text-gray-500 mb-1",children:"Project Path"}),v.jsx("div",{className:"text-xs text-gray-400 font-mono truncate",title:b,children:b||"Loading..."})]})]}),v.jsxs("div",{className:"flex-1 flex flex-col overflow-hidden",children:[v.jsxs("div",{className:"flex items-center gap-2 px-4 py-3 border-b border-border overflow-x-auto",children:[k.map((P,te)=>v.jsxs("button",{onClick:()=>z(P),className:Gt("flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm whitespace-nowrap transition-colors",_===P?"bg-surface-hover text-white":"text-gray-400 hover:text-white hover:bg-surface-hover/50"),children:[H(P.type),v.jsx("span",{children:P.path.split("/").pop()})]},te)),k.length===0&&v.jsx("span",{className:"text-sm text-gray-500",children:"No files to export"})]}),v.jsx("div",{className:"flex-1 overflow-auto p-4",children:_?v.jsxs("div",{className:"space-y-2",children:[v.jsx("div",{className:"text-xs text-gray-500 font-mono",children:_.path}),v.jsx("pre",{className:"text-sm text-gray-300 font-mono bg-canvas p-4 rounded-lg overflow-auto max-h-[400px]",children:_.content})]}):v.jsx("div",{className:"flex items-center justify-center h-full text-gray-500",children:"Select a file to preview"})})]})]}),v.jsxs("div",{className:"flex items-center justify-between px-6 py-4 border-t border-border",children:[v.jsxs("div",{className:"flex items-center gap-2",children:[L&&L.success&&v.jsxs("div",{className:"flex items-center gap-2 text-status-completed text-sm",children:[v.jsx(D5,{className:"w-4 h-4"}),v.jsx("span",{children:"Saved successfully!"})]}),L&&!L.success&&v.jsxs("div",{className:"flex items-center gap-2 text-status-error text-sm",children:[v.jsx(dm,{className:"w-4 h-4"}),v.jsx("span",{children:"Some files failed to save"})]}),B&&v.jsxs("div",{className:"flex items-center gap-2 text-status-error text-sm",children:[v.jsx(dm,{className:"w-4 h-4"}),v.jsx("span",{children:B})]})]}),v.jsxs("div",{className:"flex items-center gap-3",children:[v.jsx("button",{onClick:t,className:"px-4 py-2 text-sm font-medium text-gray-400 hover:text-white transition-colors",children:"Cancel"}),v.jsxs("button",{onClick:Z,disabled:E||k.length===0||!$,className:Gt("flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-colors",E||k.length===0||!$?"bg-gray-600 text-gray-400 cursor-not-allowed":"bg-accent hover:bg-accent-hover text-white"),children:[v.jsx(da,{className:"w-4 h-4"}),E?"Saving...":"Export"]})]})]})]})}):null}const fg=Rf()(JS(e=>({apiMode:"proxy",apiKey:"",proxyUrl:"https://api.anthropic.com",setApiMode:t=>e({apiMode:t}),setApiKey:t=>e({apiKey:t}),setProxyUrl:t=>e({proxyUrl:t})}),{name:"makecc-settings"}));function tz({isOpen:e,onClose:t}){const{apiMode:n,apiKey:r,proxyUrl:a,setApiMode:l,setApiKey:u,setProxyUrl:f}=fg(),[d,h]=X.useState(n),[g,m]=X.useState(r),[x,b]=X.useState(a),[w,k]=X.useState(!1);if(X.useEffect(()=>{e&&(h(n),m(r),b(a))},[e,n,r,a]),!e)return null;const N=async()=>{if(l(d),u(g),f(x),d==="direct"&&g)try{(await fetch("/api/settings/api-key",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:g})})).ok||console.error("Failed to save API key to .env")}catch(_){console.error("Failed to save API key:",_)}t()};return v.jsx("div",{className:"fixed inset-0 bg-black/50 flex items-center justify-center z-50",children:v.jsxs("div",{className:"bg-surface border border-border rounded-xl w-full max-w-md mx-4 shadow-2xl",children:[v.jsxs("div",{className:"flex items-center justify-between px-6 py-4 border-b border-border",children:[v.jsx("h2",{className:"text-lg font-semibold text-white",children:"설정"}),v.jsx("button",{onClick:t,className:"p-1 hover:bg-surface-hover rounded-lg transition-colors",children:v.jsx(rg,{className:"w-5 h-5 text-gray-400"})})]}),v.jsxs("div",{className:"p-6 space-y-6",children:[v.jsxs("div",{className:"space-y-3",children:[v.jsx("label",{className:"text-sm font-medium text-gray-300",children:"API 모드"}),v.jsxs("div",{className:"space-y-2",children:[v.jsxs("label",{className:`
|
|
101
|
+
`)}function Ta(e){return e.toLowerCase().replace(/\s+/g,"-").replace(/[^a-z0-9-]/g,"").replace(/-+/g,"-").replace(/^-|-$/g,"")}function ZR(e,t){const n=new Map(e.map(f=>[f.id,f])),r=new Map,a=new Map;e.forEach(f=>{r.set(f.id,0),a.set(f.id,[])}),t.forEach(f=>{a.get(f.source)?.push(f.target),r.set(f.target,(r.get(f.target)||0)+1)});const l=[];r.forEach((f,d)=>{f===0&&l.push(d)});const u=[];for(;l.length>0;){const f=l.shift(),d=n.get(f);d&&u.push(d),a.get(f)?.forEach(h=>{const g=(r.get(h)||0)-1;r.set(h,g),g===0&&l.push(h)})}return u}async function L_(){try{const e=await fetch("/api/load/claude-config");if(!e.ok)throw new Error(`Failed to load config: ${e.statusText}`);const t=await e.json();return QR(t)}catch(e){return console.error("Failed to load Claude config:",e),[]}}function QR(e){const t=[];let n=0;const r=280,a=150;for(const l of e.skills)t.push({id:l.id,type:"skill",position:{x:100+n,y:a},data:{label:l.label,description:l.description,status:"idle",skillType:"custom",skillId:l.skillId,skillPath:l.skillPath}}),n+=r;for(const l of e.subagents)t.push({id:l.id,type:"subagent",position:{x:100+n,y:a},data:{label:l.label,description:l.description,role:"custom",tools:l.tools,model:l.model,skills:l.skills,systemPrompt:l.systemPrompt,status:"idle",usedInputs:[]}}),n+=r;for(const l of e.commands)t.push({id:l.id,type:"command",position:{x:100+n,y:a},data:{label:l.label,description:l.description,commandName:l.commandName,commandContent:l.commandContent,status:"idle",usedInputs:[]}}),n+=r;for(const l of e.hooks)t.push({id:l.id,type:"hook",position:{x:100+n,y:a},data:{label:l.label,description:l.description,hookEvent:l.hookEvent,hookMatcher:l.hookMatcher,hookCommand:l.hookCommand,status:"idle",usedInputs:[]}}),n+=r;return t}function B_(e){var t,n,r="";if(typeof e=="string"||typeof e=="number")r+=e;else if(typeof e=="object")if(Array.isArray(e)){var a=e.length;for(t=0;t<a;t++)e[t]&&(n=B_(e[t]))&&(r&&(r+=" "),r+=n)}else for(n in e)e[n]&&(r&&(r+=" "),r+=n);return r}function Gt(){for(var e,t,n=0,r="",a=arguments.length;n<a;n++)(e=arguments[n])&&(t=B_(e))&&(r&&(r+=" "),r+=t);return r}const H_="http://localhost:3001/api";async function KR(e,t){const n=await fetch(`${H_}/save/workflow`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({config:e,options:t})});if(!n.ok){const r=await n.json().catch(()=>({}));throw new Error(r.message||"Failed to save workflow")}return n.json()}async function JR(){const e=await fetch(`${H_}/project-path`);if(!e.ok)throw new Error("Failed to get project path");return(await e.json()).path}async function WR(e,t){const n=[];return t.includeSkills&&e.skills.forEach(r=>{n.push({path:r.path,content:r.content,type:"skill"})}),t.includeCommands&&e.commands.forEach(r=>{n.push({path:r.path,content:r.content,type:"command"})}),t.includeAgents&&e.agents.forEach(r=>{n.push({path:r.path,content:r.content,type:"agent"})}),t.includeMcpSettings&&e.mcpSettings&&n.push({path:t.location==="global"?"~/.claude/settings.local.json":".claude/settings.local.json",content:JSON.stringify(e.mcpSettings,null,2),type:"mcp"}),{files:n}}function ez({isOpen:e,onClose:t,config:n}){const[r,a]=X.useState("local"),[l,u]=X.useState(!0),[f,d]=X.useState(!0),[h,g]=X.useState(!0),[m,x]=X.useState(!0),[b,w]=X.useState(""),[k,N]=X.useState([]),[_,z]=X.useState(null),[E,C]=X.useState(!1),[L,A]=X.useState(null),[B,G]=X.useState(null);X.useEffect(()=>{e&&JR().then(w).catch(()=>w("."))},[e]),X.useEffect(()=>{if(!e)return;WR(n,{location:r,includeSkills:l,includeCommands:f,includeAgents:h,includeMcpSettings:m}).then(({files:te})=>{N(te),te.length>0&&!_&&z(te[0])})},[e,n,r,l,f,h,m]);const Z=async()=>{C(!0),G(null),A(null);const P={location:r,includeSkills:l,includeCommands:f,includeAgents:h,includeMcpSettings:m};try{const te=await KR(n,P);A(te),te.success&&setTimeout(()=>t(),2e3)}catch(te){G(te instanceof Error?te.message:"Failed to save")}finally{C(!1)}},H=P=>{switch(P){case"skill":return v.jsx(ha,{className:"w-4 h-4 text-node-skill"});case"agent":return v.jsx(R5,{className:"w-4 h-4 text-node-generate"});case"command":return v.jsx(ds,{className:"w-4 h-4 text-accent"});case"mcp":return v.jsx(KS,{className:"w-4 h-4 text-node-mcp"});default:return v.jsx(ha,{className:"w-4 h-4 text-gray-400"})}},$=n.skills.length>0||n.commands.length>0||n.agents.length>0||n.mcpSettings;return e?v.jsx("div",{className:"fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm",children:v.jsxs("div",{className:"bg-surface border border-border rounded-xl shadow-2xl w-[900px] max-h-[80vh] flex flex-col",children:[v.jsxs("div",{className:"flex items-center justify-between px-6 py-4 border-b border-border",children:[v.jsxs("div",{className:"flex items-center gap-3",children:[v.jsx(da,{className:"w-5 h-5 text-accent"}),v.jsx("h2",{className:"text-lg font-semibold text-white",children:"Export to Claude Code"})]}),v.jsx("button",{onClick:t,className:"p-2 hover:bg-surface-hover rounded-lg transition-colors",children:v.jsx(rg,{className:"w-5 h-5 text-gray-400"})})]}),v.jsxs("div",{className:"flex flex-1 overflow-hidden",children:[v.jsxs("div",{className:"w-64 border-r border-border p-4 space-y-6 overflow-y-auto",children:[v.jsxs("div",{children:[v.jsx("label",{className:"block text-sm font-medium text-gray-300 mb-3",children:"Save Location"}),v.jsxs("div",{className:"space-y-2",children:[v.jsxs("button",{onClick:()=>a("local"),className:Gt("w-full flex items-center gap-3 px-3 py-2 rounded-lg border transition-colors text-left",r==="local"?"border-accent bg-accent/10 text-white":"border-border hover:border-gray-500 text-gray-400"),children:[v.jsx(O1,{className:"w-4 h-4"}),v.jsxs("div",{children:[v.jsx("div",{className:"text-sm font-medium",children:"Local"}),v.jsx("div",{className:"text-xs text-gray-500",children:".claude/"})]})]}),v.jsxs("button",{onClick:()=>a("global"),className:Gt("w-full flex items-center gap-3 px-3 py-2 rounded-lg border transition-colors text-left",r==="global"?"border-accent bg-accent/10 text-white":"border-border hover:border-gray-500 text-gray-400"),children:[v.jsx(O1,{className:"w-4 h-4"}),v.jsxs("div",{children:[v.jsx("div",{className:"text-sm font-medium",children:"Global"}),v.jsx("div",{className:"text-xs text-gray-500",children:"~/.claude/"})]})]})]})]}),v.jsxs("div",{children:[v.jsx("label",{className:"block text-sm font-medium text-gray-300 mb-3",children:"Include Files"}),v.jsxs("div",{className:"space-y-2",children:[v.jsxs("label",{className:"flex items-center gap-3 cursor-pointer",children:[v.jsx("input",{type:"checkbox",checked:l,onChange:P=>u(P.target.checked),disabled:n.skills.length===0,className:"w-4 h-4 rounded border-border bg-surface text-accent focus:ring-accent"}),v.jsxs("span",{className:Gt("text-sm",n.skills.length===0?"text-gray-600":"text-gray-300"),children:["Skills (",n.skills.length,")"]})]}),v.jsxs("label",{className:"flex items-center gap-3 cursor-pointer",children:[v.jsx("input",{type:"checkbox",checked:f,onChange:P=>d(P.target.checked),disabled:n.commands.length===0,className:"w-4 h-4 rounded border-border bg-surface text-accent focus:ring-accent"}),v.jsxs("span",{className:Gt("text-sm",n.commands.length===0?"text-gray-600":"text-gray-300"),children:["Commands (",n.commands.length,")"]})]}),v.jsxs("label",{className:"flex items-center gap-3 cursor-pointer",children:[v.jsx("input",{type:"checkbox",checked:h,onChange:P=>g(P.target.checked),disabled:n.agents.length===0,className:"w-4 h-4 rounded border-border bg-surface text-accent focus:ring-accent"}),v.jsxs("span",{className:Gt("text-sm",n.agents.length===0?"text-gray-600":"text-gray-300"),children:["Agents (",n.agents.length,")"]})]}),v.jsxs("label",{className:"flex items-center gap-3 cursor-pointer",children:[v.jsx("input",{type:"checkbox",checked:m,onChange:P=>x(P.target.checked),disabled:!n.mcpSettings,className:"w-4 h-4 rounded border-border bg-surface text-accent focus:ring-accent"}),v.jsx("span",{className:Gt("text-sm",n.mcpSettings?"text-gray-300":"text-gray-600"),children:"MCP Settings"})]})]})]}),v.jsxs("div",{className:"pt-4 border-t border-border",children:[v.jsx("div",{className:"text-xs text-gray-500 mb-1",children:"Project Path"}),v.jsx("div",{className:"text-xs text-gray-400 font-mono truncate",title:b,children:b||"Loading..."})]})]}),v.jsxs("div",{className:"flex-1 flex flex-col overflow-hidden",children:[v.jsxs("div",{className:"flex items-center gap-2 px-4 py-3 border-b border-border overflow-x-auto",children:[k.map((P,te)=>v.jsxs("button",{onClick:()=>z(P),className:Gt("flex items-center gap-2 px-3 py-1.5 rounded-lg text-sm whitespace-nowrap transition-colors",_===P?"bg-surface-hover text-white":"text-gray-400 hover:text-white hover:bg-surface-hover/50"),children:[H(P.type),v.jsx("span",{children:P.path.split("/").pop()})]},te)),k.length===0&&v.jsx("span",{className:"text-sm text-gray-500",children:"No files to export"})]}),v.jsx("div",{className:"flex-1 overflow-auto p-4",children:_?v.jsxs("div",{className:"space-y-2",children:[v.jsx("div",{className:"text-xs text-gray-500 font-mono",children:_.path}),v.jsx("pre",{className:"text-sm text-gray-300 font-mono bg-canvas p-4 rounded-lg overflow-auto max-h-[400px]",children:_.content})]}):v.jsx("div",{className:"flex items-center justify-center h-full text-gray-500",children:"Select a file to preview"})})]})]}),v.jsxs("div",{className:"flex items-center justify-between px-6 py-4 border-t border-border",children:[v.jsxs("div",{className:"flex items-center gap-2",children:[L&&L.success&&v.jsxs("div",{className:"flex items-center gap-2 text-status-completed text-sm",children:[v.jsx(D5,{className:"w-4 h-4"}),v.jsx("span",{children:"Saved successfully!"})]}),L&&!L.success&&v.jsxs("div",{className:"flex items-center gap-2 text-status-error text-sm",children:[v.jsx(dm,{className:"w-4 h-4"}),v.jsx("span",{children:"Some files failed to save"})]}),B&&v.jsxs("div",{className:"flex items-center gap-2 text-status-error text-sm",children:[v.jsx(dm,{className:"w-4 h-4"}),v.jsx("span",{children:B})]})]}),v.jsxs("div",{className:"flex items-center gap-3",children:[v.jsx("button",{onClick:t,className:"px-4 py-2 text-sm font-medium text-gray-400 hover:text-white transition-colors",children:"Cancel"}),v.jsxs("button",{onClick:Z,disabled:E||k.length===0||!$,className:Gt("flex items-center gap-2 px-4 py-2 rounded-lg text-sm font-medium transition-colors",E||k.length===0||!$?"bg-gray-600 text-gray-400 cursor-not-allowed":"bg-accent hover:bg-accent-hover text-white"),children:[v.jsx(da,{className:"w-4 h-4"}),E?"Saving...":"Export"]})]})]})]})}):null}const fg=Rf()(JS(e=>({apiMode:"proxy",apiKey:"",proxyUrl:"https://makecc-proxy.vercel.app",setApiMode:t=>e({apiMode:t}),setApiKey:t=>e({apiKey:t}),setProxyUrl:t=>e({proxyUrl:t})}),{name:"makecc-settings"}));function tz({isOpen:e,onClose:t}){const{apiMode:n,apiKey:r,proxyUrl:a,setApiMode:l,setApiKey:u,setProxyUrl:f}=fg(),[d,h]=X.useState(n),[g,m]=X.useState(r),[x,b]=X.useState(a),[w,k]=X.useState(!1);if(X.useEffect(()=>{e&&(h(n),m(r),b(a))},[e,n,r,a]),!e)return null;const N=async()=>{if(l(d),u(g),f(x),d==="direct"&&g)try{(await fetch("/api/settings/api-key",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({apiKey:g})})).ok||console.error("Failed to save API key to .env")}catch(_){console.error("Failed to save API key:",_)}t()};return v.jsx("div",{className:"fixed inset-0 bg-black/50 flex items-center justify-center z-50",children:v.jsxs("div",{className:"bg-surface border border-border rounded-xl w-full max-w-md mx-4 shadow-2xl",children:[v.jsxs("div",{className:"flex items-center justify-between px-6 py-4 border-b border-border",children:[v.jsx("h2",{className:"text-lg font-semibold text-white",children:"설정"}),v.jsx("button",{onClick:t,className:"p-1 hover:bg-surface-hover rounded-lg transition-colors",children:v.jsx(rg,{className:"w-5 h-5 text-gray-400"})})]}),v.jsxs("div",{className:"p-6 space-y-6",children:[v.jsxs("div",{className:"space-y-3",children:[v.jsx("label",{className:"text-sm font-medium text-gray-300",children:"API 모드"}),v.jsxs("div",{className:"space-y-2",children:[v.jsxs("label",{className:`
|
|
102
102
|
flex items-center gap-3 p-3 rounded-lg border cursor-pointer transition-colors
|
|
103
103
|
${d==="proxy"?"border-accent bg-accent/10":"border-border hover:border-gray-600"}
|
|
104
104
|
`,children:[v.jsx("input",{type:"radio",name:"apiMode",value:"proxy",checked:d==="proxy",onChange:()=>h("proxy"),className:"sr-only"}),v.jsx(_j,{className:`w-5 h-5 ${d==="proxy"?"text-accent":"text-gray-400"}`}),v.jsxs("div",{className:"flex-1",children:[v.jsx("div",{className:`font-medium ${d==="proxy"?"text-white":"text-gray-300"}`,children:"프록시 서버"}),v.jsx("div",{className:"text-sm text-gray-500",children:"프록시 서버의 API 키를 사용합니다"})]}),d==="proxy"&&v.jsx("div",{className:"w-2 h-2 bg-accent rounded-full"})]}),v.jsxs("label",{className:`
|
package/dist/client/index.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>vite-temp</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-lpZin4sA.js"></script>
|
|
9
9
|
<link rel="stylesheet" crossorigin href="/assets/index-v9IFpWkA.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
package/dist/server/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import 'dotenv
|
|
1
|
+
import dotenv from 'dotenv';
|
|
2
2
|
import express from 'express';
|
|
3
3
|
import { createServer } from 'http';
|
|
4
4
|
import { Server } from 'socket.io';
|
|
@@ -6,6 +6,18 @@ import cors from 'cors';
|
|
|
6
6
|
import { join, dirname } from 'path';
|
|
7
7
|
import { fileURLToPath } from 'url';
|
|
8
8
|
import { existsSync, promises as fs } from 'fs';
|
|
9
|
+
// 프로젝트 경로의 .env 파일을 명시적으로 로드
|
|
10
|
+
// npx makecc 실행 시 MAKECC_PROJECT_PATH가 사용자 프로젝트 경로를 가리킴
|
|
11
|
+
const projectPath = process.env.MAKECC_PROJECT_PATH || process.cwd();
|
|
12
|
+
const envPath = join(projectPath, '.env');
|
|
13
|
+
if (existsSync(envPath)) {
|
|
14
|
+
dotenv.config({ path: envPath });
|
|
15
|
+
console.log(`Loaded .env from: ${envPath}`);
|
|
16
|
+
}
|
|
17
|
+
else {
|
|
18
|
+
// 폴백: 현재 디렉토리에서 로드 시도
|
|
19
|
+
dotenv.config();
|
|
20
|
+
}
|
|
9
21
|
import { ClaudeService } from './services/claudeService';
|
|
10
22
|
import { fileService } from './services/fileService';
|
|
11
23
|
import { workflowAIService } from './services/workflowAIService';
|
|
@@ -66,20 +66,33 @@ export class SkillGeneratorService {
|
|
|
66
66
|
this.projectRoot = projectRoot || process.env.MAKECC_PROJECT_PATH || process.cwd();
|
|
67
67
|
}
|
|
68
68
|
getClient(settings) {
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if (settings?.
|
|
69
|
+
const DEFAULT_ANTHROPIC_URL = 'https://api.anthropic.com';
|
|
70
|
+
// 1. 프록시 우선: proxyUrl이 설정되어 있고, 기본 Anthropic URL이 아닌 경우
|
|
71
|
+
// 프록시 서버가 API 키를 관리하므로 클라이언트는 키 불필요
|
|
72
|
+
if (settings?.proxyUrl && settings.proxyUrl !== DEFAULT_ANTHROPIC_URL) {
|
|
73
|
+
console.log(`Using proxy server: ${settings.proxyUrl}`);
|
|
73
74
|
return new Anthropic({
|
|
74
75
|
baseURL: settings.proxyUrl,
|
|
75
|
-
apiKey:
|
|
76
|
+
apiKey: 'proxy-mode', // 프록시 서버가 실제 키를 관리
|
|
76
77
|
});
|
|
77
78
|
}
|
|
79
|
+
// 2. 환경변수에 API 키가 있으면 직접 호출
|
|
78
80
|
const envApiKey = process.env.ANTHROPIC_API_KEY;
|
|
79
|
-
if (
|
|
80
|
-
|
|
81
|
+
if (envApiKey) {
|
|
82
|
+
console.log('Using API key from environment variable');
|
|
83
|
+
return new Anthropic({ apiKey: envApiKey });
|
|
84
|
+
}
|
|
85
|
+
// 3. UI에서 direct 모드로 API 키를 직접 입력한 경우
|
|
86
|
+
if (settings?.apiMode === 'direct' && settings.apiKey) {
|
|
87
|
+
console.log('Using API key from UI settings');
|
|
88
|
+
return new Anthropic({ apiKey: settings.apiKey });
|
|
81
89
|
}
|
|
82
|
-
|
|
90
|
+
// 4. 아무것도 없으면 에러
|
|
91
|
+
throw new Error('API 키가 설정되지 않았습니다.\n' +
|
|
92
|
+
'해결 방법:\n' +
|
|
93
|
+
'1. 프록시 서버 URL을 설정하거나\n' +
|
|
94
|
+
'2. 프로젝트 .env 파일에 ANTHROPIC_API_KEY를 추가하거나\n' +
|
|
95
|
+
'3. 설정에서 Direct 모드로 API 키를 직접 입력하세요.');
|
|
83
96
|
}
|
|
84
97
|
async generate(prompt, settings, onProgress) {
|
|
85
98
|
const progress = onProgress || (() => { });
|
|
@@ -124,28 +124,33 @@ ${AVAILABLE_TOOLS.join(', ')}
|
|
|
124
124
|
`;
|
|
125
125
|
export class WorkflowAIService {
|
|
126
126
|
getClient(settings) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
});
|
|
132
|
-
}
|
|
133
|
-
// 2. Proxy mode with custom base URL
|
|
134
|
-
if (settings?.apiMode === 'proxy' && settings.proxyUrl) {
|
|
127
|
+
const DEFAULT_ANTHROPIC_URL = 'https://api.anthropic.com';
|
|
128
|
+
// 1. 프록시 우선: proxyUrl이 설정되어 있고, 기본 Anthropic URL이 아닌 경우
|
|
129
|
+
// 프록시 서버가 API 키를 관리하므로 클라이언트는 키 불필요
|
|
130
|
+
if (settings?.proxyUrl && settings.proxyUrl !== DEFAULT_ANTHROPIC_URL) {
|
|
131
|
+
console.log(`Using proxy server: ${settings.proxyUrl}`);
|
|
135
132
|
return new Anthropic({
|
|
136
133
|
baseURL: settings.proxyUrl,
|
|
137
|
-
//
|
|
138
|
-
apiKey: process.env.ANTHROPIC_API_KEY || 'proxy-mode',
|
|
134
|
+
apiKey: 'proxy-mode', // 프록시 서버가 실제 키를 관리
|
|
139
135
|
});
|
|
140
136
|
}
|
|
141
|
-
//
|
|
137
|
+
// 2. 환경변수에 API 키가 있으면 직접 호출
|
|
142
138
|
const envApiKey = process.env.ANTHROPIC_API_KEY;
|
|
143
|
-
if (
|
|
144
|
-
|
|
139
|
+
if (envApiKey) {
|
|
140
|
+
console.log('Using API key from environment variable');
|
|
141
|
+
return new Anthropic({ apiKey: envApiKey });
|
|
145
142
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
143
|
+
// 3. UI에서 direct 모드로 API 키를 직접 입력한 경우
|
|
144
|
+
if (settings?.apiMode === 'direct' && settings.apiKey) {
|
|
145
|
+
console.log('Using API key from UI settings');
|
|
146
|
+
return new Anthropic({ apiKey: settings.apiKey });
|
|
147
|
+
}
|
|
148
|
+
// 4. 아무것도 없으면 에러
|
|
149
|
+
throw new Error('API 키가 설정되지 않았습니다.\n' +
|
|
150
|
+
'해결 방법:\n' +
|
|
151
|
+
'1. 프록시 서버 URL을 설정하거나\n' +
|
|
152
|
+
'2. 프로젝트 .env 파일에 ANTHROPIC_API_KEY를 추가하거나\n' +
|
|
153
|
+
'3. 설정에서 Direct 모드로 API 키를 직접 입력하세요.');
|
|
149
154
|
}
|
|
150
155
|
async generate(prompt, settings) {
|
|
151
156
|
const client = this.getClient(settings);
|
package/package.json
CHANGED
package/server/index.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import 'dotenv
|
|
1
|
+
import dotenv from 'dotenv';
|
|
2
2
|
import express from 'express';
|
|
3
3
|
import { createServer } from 'http';
|
|
4
4
|
import { Server } from 'socket.io';
|
|
@@ -6,6 +6,18 @@ import cors from 'cors';
|
|
|
6
6
|
import { join, dirname } from 'path';
|
|
7
7
|
import { fileURLToPath } from 'url';
|
|
8
8
|
import { existsSync, promises as fs } from 'fs';
|
|
9
|
+
|
|
10
|
+
// 프로젝트 경로의 .env 파일을 명시적으로 로드
|
|
11
|
+
// npx makecc 실행 시 MAKECC_PROJECT_PATH가 사용자 프로젝트 경로를 가리킴
|
|
12
|
+
const projectPath = process.env.MAKECC_PROJECT_PATH || process.cwd();
|
|
13
|
+
const envPath = join(projectPath, '.env');
|
|
14
|
+
if (existsSync(envPath)) {
|
|
15
|
+
dotenv.config({ path: envPath });
|
|
16
|
+
console.log(`Loaded .env from: ${envPath}`);
|
|
17
|
+
} else {
|
|
18
|
+
// 폴백: 현재 디렉토리에서 로드 시도
|
|
19
|
+
dotenv.config();
|
|
20
|
+
}
|
|
9
21
|
import { ClaudeService } from './services/claudeService';
|
|
10
22
|
import { fileService } from './services/fileService';
|
|
11
23
|
import { workflowAIService } from './services/workflowAIService';
|
|
@@ -107,23 +107,39 @@ export class SkillGeneratorService {
|
|
|
107
107
|
}
|
|
108
108
|
|
|
109
109
|
private getClient(settings?: ApiSettings): Anthropic {
|
|
110
|
-
|
|
111
|
-
return new Anthropic({ apiKey: settings.apiKey });
|
|
112
|
-
}
|
|
110
|
+
const DEFAULT_ANTHROPIC_URL = 'https://api.anthropic.com';
|
|
113
111
|
|
|
114
|
-
|
|
112
|
+
// 1. 프록시 우선: proxyUrl이 설정되어 있고, 기본 Anthropic URL이 아닌 경우
|
|
113
|
+
// 프록시 서버가 API 키를 관리하므로 클라이언트는 키 불필요
|
|
114
|
+
if (settings?.proxyUrl && settings.proxyUrl !== DEFAULT_ANTHROPIC_URL) {
|
|
115
|
+
console.log(`Using proxy server: ${settings.proxyUrl}`);
|
|
115
116
|
return new Anthropic({
|
|
116
117
|
baseURL: settings.proxyUrl,
|
|
117
|
-
apiKey:
|
|
118
|
+
apiKey: 'proxy-mode', // 프록시 서버가 실제 키를 관리
|
|
118
119
|
});
|
|
119
120
|
}
|
|
120
121
|
|
|
122
|
+
// 2. 환경변수에 API 키가 있으면 직접 호출
|
|
121
123
|
const envApiKey = process.env.ANTHROPIC_API_KEY;
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
+
if (envApiKey) {
|
|
125
|
+
console.log('Using API key from environment variable');
|
|
126
|
+
return new Anthropic({ apiKey: envApiKey });
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// 3. UI에서 direct 모드로 API 키를 직접 입력한 경우
|
|
130
|
+
if (settings?.apiMode === 'direct' && settings.apiKey) {
|
|
131
|
+
console.log('Using API key from UI settings');
|
|
132
|
+
return new Anthropic({ apiKey: settings.apiKey });
|
|
124
133
|
}
|
|
125
134
|
|
|
126
|
-
|
|
135
|
+
// 4. 아무것도 없으면 에러
|
|
136
|
+
throw new Error(
|
|
137
|
+
'API 키가 설정되지 않았습니다.\n' +
|
|
138
|
+
'해결 방법:\n' +
|
|
139
|
+
'1. 프록시 서버 URL을 설정하거나\n' +
|
|
140
|
+
'2. 프로젝트 .env 파일에 ANTHROPIC_API_KEY를 추가하거나\n' +
|
|
141
|
+
'3. 설정에서 Direct 모드로 API 키를 직접 입력하세요.'
|
|
142
|
+
);
|
|
127
143
|
}
|
|
128
144
|
|
|
129
145
|
async generate(
|
|
@@ -165,31 +165,39 @@ ${AVAILABLE_TOOLS.join(', ')}
|
|
|
165
165
|
|
|
166
166
|
export class WorkflowAIService {
|
|
167
167
|
private getClient(settings?: ApiSettings): Anthropic {
|
|
168
|
-
|
|
169
|
-
if (settings?.apiMode === 'direct' && settings.apiKey) {
|
|
170
|
-
return new Anthropic({
|
|
171
|
-
apiKey: settings.apiKey,
|
|
172
|
-
});
|
|
173
|
-
}
|
|
168
|
+
const DEFAULT_ANTHROPIC_URL = 'https://api.anthropic.com';
|
|
174
169
|
|
|
175
|
-
//
|
|
176
|
-
|
|
170
|
+
// 1. 프록시 우선: proxyUrl이 설정되어 있고, 기본 Anthropic URL이 아닌 경우
|
|
171
|
+
// 프록시 서버가 API 키를 관리하므로 클라이언트는 키 불필요
|
|
172
|
+
if (settings?.proxyUrl && settings.proxyUrl !== DEFAULT_ANTHROPIC_URL) {
|
|
173
|
+
console.log(`Using proxy server: ${settings.proxyUrl}`);
|
|
177
174
|
return new Anthropic({
|
|
178
175
|
baseURL: settings.proxyUrl,
|
|
179
|
-
//
|
|
180
|
-
apiKey: process.env.ANTHROPIC_API_KEY || 'proxy-mode',
|
|
176
|
+
apiKey: 'proxy-mode', // 프록시 서버가 실제 키를 관리
|
|
181
177
|
});
|
|
182
178
|
}
|
|
183
179
|
|
|
184
|
-
//
|
|
180
|
+
// 2. 환경변수에 API 키가 있으면 직접 호출
|
|
185
181
|
const envApiKey = process.env.ANTHROPIC_API_KEY;
|
|
186
|
-
if (
|
|
187
|
-
|
|
182
|
+
if (envApiKey) {
|
|
183
|
+
console.log('Using API key from environment variable');
|
|
184
|
+
return new Anthropic({ apiKey: envApiKey });
|
|
188
185
|
}
|
|
189
186
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
187
|
+
// 3. UI에서 direct 모드로 API 키를 직접 입력한 경우
|
|
188
|
+
if (settings?.apiMode === 'direct' && settings.apiKey) {
|
|
189
|
+
console.log('Using API key from UI settings');
|
|
190
|
+
return new Anthropic({ apiKey: settings.apiKey });
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// 4. 아무것도 없으면 에러
|
|
194
|
+
throw new Error(
|
|
195
|
+
'API 키가 설정되지 않았습니다.\n' +
|
|
196
|
+
'해결 방법:\n' +
|
|
197
|
+
'1. 프록시 서버 URL을 설정하거나\n' +
|
|
198
|
+
'2. 프로젝트 .env 파일에 ANTHROPIC_API_KEY를 추가하거나\n' +
|
|
199
|
+
'3. 설정에서 Direct 모드로 API 키를 직접 입력하세요.'
|
|
200
|
+
);
|
|
193
201
|
}
|
|
194
202
|
|
|
195
203
|
async generate(prompt: string, settings?: ApiSettings): Promise<AIWorkflowResult> {
|