aicommit2 2.5.3 → 2.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -53,7 +53,10 @@ brew install aicommit2
53
53
  # Or install via npm
54
54
  npm install -g aicommit2
55
55
 
56
- # Set up at least one AI provider
56
+ # Set up AI providers (interactive wizard)
57
+ aicommit2 setup
58
+
59
+ # Or configure manually
57
60
  aicommit2 config set OPENAI.key=<your-key>
58
61
 
59
62
  # Use in your Git repository
@@ -113,8 +116,16 @@ npm install -g aicommit2
113
116
 
114
117
  > ⚠️ For npm installation, the minimum supported version of Node.js is v18. Check your Node.js version with `node --version`.
115
118
 
116
- 2. Set up API keys (**at least ONE key must be set**):
119
+ 2. Configure your AI provider(s) (**at least ONE provider must be configured**):
120
+
121
+ **Option A: Interactive setup wizard (recommended)**
122
+ ```bash
123
+ aicommit2 setup
124
+ ```
125
+
126
+ > 👉 The setup wizard guides you through provider selection, API key entry, and model configuration in one step.
117
127
 
128
+ **Option B: Manual configuration**
118
129
  ```bash
119
130
  aicommit2 config set OPENAI.key=<your key>
120
131
  aicommit2 config set ANTHROPIC.key=<your key>
@@ -427,6 +438,7 @@ In addition to the main commit message generation, aicommit2 provides several ut
427
438
 
428
439
  | Command | Description |
429
440
  |---------|-------------|
441
+ | `aicommit2 setup` | Interactive setup wizard for configuring AI providers |
430
442
  | `aicommit2 config` | Manage configuration (get, set, list, del) |
431
443
  | `aicommit2 doctor` | Check health status of AI providers |
432
444
  | `aicommit2 stats` | View usage statistics and performance metrics |
@@ -435,6 +447,9 @@ In addition to the main commit message generation, aicommit2 provides several ut
435
447
  | `aicommit2 github-login` | Login to GitHub for GitHub Models access |
436
448
 
437
449
  ```bash
450
+ # Interactive setup wizard
451
+ aicommit2 setup
452
+
438
453
  # Configuration management
439
454
  aicommit2 config set OPENAI.key=<your-key>
440
455
  aicommit2 config get OPENAI
@@ -640,7 +655,7 @@ aicommit2 doctor
640
655
  Example output:
641
656
 
642
657
  ```
643
- 🩺 AICommit2 Health Check
658
+ 🩺 aicommit2 Health Check
644
659
 
645
660
  Providers:
646
661
  ✅ OPENAI API key configured
@@ -877,6 +892,7 @@ For detailed information about all available settings, see the [General Settings
877
892
  | `useStats` | Enable usage statistics tracking | true |
878
893
  | `statsDays` | Days to retain statistics data (auto-cleanup) | 30 |
879
894
  | `systemPromptPath` | Path to custom system prompt file | - |
895
+ | `stream` | **Experimental.** Enable streaming for real-time commit message generation | false |
880
896
 
881
897
  ```bash
882
898
  # Example: Set settings for a specific model
@@ -889,20 +905,20 @@ aicommit2 config set ANTHROPIC.includeBody=true
889
905
 
890
906
  ### Available Settings by Model
891
907
 
892
- | | timeout | temperature | maxTokens | topP |
893
- | :-----------------------: | :-----: | :---------: | :-------: | :--: |
894
- | **OpenAI** | ✓ | ✓ | ✓ | ✓ |
895
- | **Anthropic Claude** | ✓ | ✓ | ✓ | ✓ |
896
- | **Gemini** | | ✓ | ✓ | ✓ |
897
- | **Mistral AI** | ✓ | ✓ | ✓ | ✓ |
898
- | **Codestral** | ✓ | ✓ | ✓ | ✓ |
899
- | **Cohere** | ✓ | ✓ | ✓ | ✓ |
900
- | **Groq** | ✓ | ✓ | ✓ | ✓ |
901
- | **Perplexity** | ✓ | ✓ | ✓ | ✓ |
902
- | **DeepSeek** | ✓ | ✓ | ✓ | ✓ |
903
- | **Github Models** | ✓ | ✓ | ✓ | ✓ |
904
- | **Ollama** | ✓ | ✓ | | ✓ |
905
- | **OpenAI API-Compatible** | ✓ | ✓ | ✓ | ✓ |
908
+ | | timeout | temperature | maxTokens | topP | stream |
909
+ | :-----------------------: | :-----: | :---------: | :-------: | :--: | :----: |
910
+ | **OpenAI** | ✓ | ✓ | ✓ | ✓ | ✓ |
911
+ | **Anthropic Claude** | ✓ | ✓ | ✓ | ✓ | ✓ |
912
+ | **Gemini** | | ✓ | ✓ | ✓ | ✓ |
913
+ | **Mistral AI** | ✓ | ✓ | ✓ | ✓ | |
914
+ | **Codestral** | ✓ | ✓ | ✓ | ✓ | |
915
+ | **Cohere** | ✓ | ✓ | ✓ | ✓ | |
916
+ | **Groq** | ✓ | ✓ | ✓ | ✓ | ✓ |
917
+ | **Perplexity** | ✓ | ✓ | ✓ | ✓ | |
918
+ | **DeepSeek** | ✓ | ✓ | ✓ | ✓ | ✓ |
919
+ | **Github Models** | ✓ | ✓ | ✓ | ✓ | |
920
+ | **Ollama** | ✓ | ✓ | | ✓ | ✓ |
921
+ | **OpenAI API-Compatible** | ✓ | ✓ | ✓ | ✓ | ✓ |
906
922
 
907
923
  ## Logging
908
924
 
@@ -0,0 +1,11 @@
1
+ import{of as N,Observable as k,Subject as _,catchError as B}from"rxjs";import"fs";import"path";import{xxh64 as w}from"@pacote/xxhash";import d from"winston";import{A as T,l as $,D as F,g as J,h as D,d as U,E as q,e as z,s as H,f as M}from"./cli-9533dfd6.mjs";const y=new Map,p=(c,t,e)=>{const r=w(0).update(t).digest("hex").substring(0,8),s=`${c}_${r}_${e}`;if(y.has(s))return y.get(s);const n=new Date,o=X(n,c,t,e),i=`${T}/${o}`,l=d.createLogger({level:"info",format:d.format.combine(d.format.timestamp({format:"YYYY-MM-DDTHH:mm:ss.SSSZ"}),d.format.printf(({timestamp:f,level:u,message:a,...m})=>m&&Object.keys(m).length>0?`[${f}] ${u}: ${a} ${JSON.stringify(m,null,2)}`:`[${f}] ${u}: ${a}`)),transports:[new d.transports.File({filename:i})]});return l.info(`=== ${c.toUpperCase()} AI SERVICE LOG ===`),l.info(`Diff Hash: ${r}`),l.info(`Request Type: ${e.toUpperCase()}`),l.info(`Start Time: ${n.toISOString()}`),l.info("=".repeat(50)),l.info(""),y.set(s,l),l},K=c=>{const t={...c},e=["authorization","x-api-key","x-goog-api-key","api-key","x-amzn-bedrock-application-key"];for(const r of e){const s=r.toLowerCase(),n=Object.keys(t).find(o=>o.toLowerCase()===s);n&&t[n]&&typeof t[n]=="string"&&(t[n].startsWith("Bearer ")?t[n]="Bearer [MASKED]":t[n]="[MASKED]")}return t},G=(c,t,e,r,s,n,o=!0)=>{if(!o)return;const i=p(e,c,t);i.info(`Making request to ${e} API with model: ${r}`),i.info(`Request URL: ${s}`),i.info("Request headers:",K(n))},Y=(c,t,e,r,s=!0)=>{if(!s)return;p(e,c,t).info("Request payload:",r)},W=(c,t,e,r,s,n=!0)=>{if(!n)return;const o=p(e,c,t);o.info("System prompt:",{prompt:r}),o.info("User prompt:",{prompt:s})},V=(c,t,e,r,s=!0)=>{if(!s)return;p(e,c,t).info("Response received:",r)},Q=(c,t,e,r,s=!0)=>{if(!s)return;p(e,c,t).error("API request failed:",r)},C=(c,t,e,r,s,n=!0)=>{if(!n)return;const o=p(e,c,t);r?o.info(`Request completed successfully in ${r}ms`):o.info("Request completed successfully"),s&&o.info("Final processed response:",{response:s}),o.info(""),o.info("=".repeat(50)),o.info(`End Time: ${new Date().toISOString()}`),o.info("=== REQUEST COMPLETED ===")},Z=(c,t,e,r,s,n,o,i=!0)=>{if(!i)return;const l=p(e,c,t);o?l.error(`Request failed after ${n}ms:`,{error:o}):(l.info(`Request completed in ${n}ms`),l.info("Response:",{response:s})),C(c,t,e,n,s,i)},X=(c,t,e,r)=>{const{year:s,month:n,day:o,hours:i,minutes:l,seconds:f}=ee(c),a=w(0).update(e).digest("hex").substring(0,8),m=t.toLowerCase().replace(/[^a-z0-9]/g,"").substring(0,20);return r==="review"?`${s}-${n}-${o}_${i}-${l}-${f}_${a}_${m}_review.log`:`${s}-${n}-${o}_${i}-${l}-${f}_${a}_${m}_commit.log`},ee=c=>{const t=c.getFullYear().toString(),e=(c.getMonth()+1).toString().padStart(2,"0"),r=c.getDate().toString().padStart(2,"0"),s=c.getHours().toString().padStart(2,"0"),n=c.getMinutes().toString().padStart(2,"0"),o=c.getSeconds().toString().padStart(2,"0");return{year:t,month:e,day:r,hours:s,minutes:n,seconds:o}},v=()=>{for(const[c,t]of y.entries())try{t.close()}catch(e){console.error(`Failed to close logger ${c}:`,e)}y.clear()};process.on("exit",v),process.on("SIGINT",()=>{v(),process.exit(0)}),process.on("SIGTERM",()=>{v(),process.exit(0)});class te{constructor(){this.buffer="",this.arrayStartFound=!1,this.scanPosition=0,this.feed=t=>{this.buffer+=t;const e=[];if(!this.arrayStartFound){const r=this.buffer.indexOf("[");if(r===-1)return e;this.arrayStartFound=!0,this.scanPosition=r+1}for(;;){const r=this.buffer.indexOf("{",this.scanPosition);if(r===-1)break;const s=this.extractBalancedBraces(r);if(!s)break;this.scanPosition=r+s.length;const n=this.tryParseCommitMessage(s);n&&e.push(n)}return e},this.flush=()=>this.feed(""),this.getBuffer=()=>this.buffer,this.getUnparsedBuffer=()=>this.buffer.slice(this.scanPosition),this.extractBalancedBraces=t=>{let e=0,r=!1,s=!1;for(let n=t;n<this.buffer.length;n++){const o=this.buffer[n];if(s){s=!1;continue}if(o==="\\"&&r){s=!0;continue}if(o==='"'){r=!r;continue}if(!r&&(o==="{"&&e++,o==="}"&&e--,e===0))return this.buffer.slice(t,n+1)}return null},this.tryParseCommitMessage=t=>{try{const e=JSON.parse(t);return typeof e.subject!="string"?null:{subject:e.subject,body:typeof e.body=="string"?e.body:void 0,footer:typeof e.footer=="string"?e.footer:void 0}}catch{return null}}}}class re{constructor(t){this.handleError$=e=>{const r=this.getDetailedErrorMessage(e),s=e.status?`HTTP ${e.status}: ${r}`:r;if(this.params.config.logging){const n=this.params.stagedDiff.diff,o=this.serviceName.replace(/\[|\]/g,"").trim();Z(n,"commit",o,"Error occurred","",void 0,s)}return $.error(`${this.errorPrefix} ${s}`),e.stack&&$.error(` ${e.stack}`),e.content&&$.error(` Problematic content: ${e.content}`),e.originalError&&$.error(` Original error: ${e.originalError}`),N({name:`${this.errorPrefix} ${s}`,value:s,isError:!0,disabled:!0})},this.buildCommitPrompt=()=>{const{systemPrompt:e,systemPromptPath:r,codeReviewPromptPath:s,locale:n,generate:o,type:i,maxLength:l}=this.params.config,f={...F,locale:n,maxLength:l,type:i,generate:o,systemPrompt:e,systemPromptPath:r,codeReviewPromptPath:s,vcs_branch:this.params.branchName||""};return J(f)},this.formatRawCommitMessage=(e,r)=>{const s=this.extractMessageAsType(e,r),n=s.subject,o=`${s.subject}${s.body?`
2
+
3
+ ${s.body}`:""}${s.footer?`
4
+
5
+ ${s.footer}`:""}`;return{title:n,value:o}},this.formatAsChoice=e=>({name:`${this.serviceName} ${e.title}`,short:e.title,value:this.params.config.includeBody?e.value:e.title,description:this.params.config.includeBody?e.value:"",isError:!1}),this.extractStreamPreview=e=>{const r=e.match(/"subject"\s*:\s*"((?:[^"\\]|\\.)*)(?:"|$)/),s=e.match(/"body"\s*:\s*"((?:[^"\\]|\\.)*)(?:"|$)/),n=r?r[1].replace(/\\"/g,'"').replace(/\\n/g,`
6
+ `):"",o=s?s[1].replace(/\\"/g,'"').replace(/\\n/g,`
7
+ `):"";return{subject:n,body:o}},this.createStreamingCommitMessages$=(e,r,s)=>{const n=`stream-${this.serviceName}-${Date.now()}`;return new k(o=>{const i=new te,l=new _;let f=0,u=!1,a=0;const m=100,R="streaming",E={name:"",value:"",streamKey:n,disabled:!0,isError:!1},A=(g=!1)=>{const h=Date.now();if(!g&&h-a<m)return;a=h;const S=i.getUnparsedBuffer();if(!S.trim())return;const{subject:b,body:x}=this.extractStreamPreview(S),I=b?`${this.serviceName} ${b}`:`${this.serviceName} Generating...`,O=x||b||"",L={name:I,short:b||"Generating...",value:`__streaming__${n}`,description:O,disabled:R,isError:!1,streamKey:n};o.next(L),u=!0},P=g=>{for(const h of g){if(f>=s)break;o.next(this.formatAsChoice(this.formatRawCommitMessage(h,r))),f++}},j=l.subscribe({next:g=>{if(f>=s)return;const h=i.feed(g);P(h),f<s&&A()},error:g=>{u&&o.next(E),o.error(g)},complete:()=>{P(i.flush()),u&&o.next(E);const g=i.getBuffer();if(f===0&&g.trim())try{const h=this.parseMessage(g,r,s);for(const S of h)o.next(this.formatAsChoice(S))}catch(h){o.error(h);return}o.complete()}});return e(l),()=>{j.unsubscribe()}}).pipe(B(this.handleError$))},this.serviceName="AI",this.errorPrefix="ERROR",this.colors={primary:""},this.params=t,this.logSessionId=t.logSessionId}getProviderName(){const t=String.fromCharCode(27),e=new RegExp(`${t}\\[[0-9;]*m`,"g");return this.serviceName.replace(e,"").replace(/\[|\]/g,"").trim()}getDetailedErrorMessage(t){const e=t.message||"",r=this.getProviderName(),s=this.params.config.model?.[0],n=this.params.config.timeout,o=this.getServiceSpecificErrorMessage(t);if(o)return o;const i=t.code||(t.status?D(t.status):U(e));return i!==q.UNKNOWN?z(i,{provider:r,model:s,timeout:n}):e||"Unknown error occurred"}getServiceSpecificErrorMessage(t){return null}cleanJsonCodeBlock(t){const e=/```(?:json|JSON)?\s*([\s\S]*?)\s*```/,r=t.match(e);return r?r[1].trim():t}extractJsonFromResponse(t){let e=t.indexOf("[");if(e!==-1){const r=this.extractBalancedJson(t,e,"[","]");if(r)return r}if(e=t.indexOf("{"),e!==-1){const r=this.extractBalancedJson(t,e,"{","}");if(r)return r}return null}extractBalancedJson(t,e,r,s){let n=0,o=!1,i=!1;for(let l=e;l<t.length;l++){const f=t[l];if(i){i=!1;continue}if(f==="\\"&&o){i=!0;continue}if(f==='"'){o=!o;continue}if(!o&&(f===r&&n++,f===s&&n--,n===0))return t.slice(e,l+1)}return null}parseMessage(t,e,r){const s=this.cleanJsonCodeBlock(t),n=this.extractJsonFromResponse(s);if(!n){const a=new Error("AI response did not contain a valid JSON object or array.");throw a.name="InvalidJsonResponse",a.content=t,a}const o=H(n);if(!o.ok){const a=new Error("Failed to parse AI response as JSON");throw a.name="JsonParseError",a.content=n,a.originalError=o.error,a}const i=o.data,l=Array.isArray(i)?i:[i];if(!l.length||!l.every(a=>typeof a.subject=="string")){const a=new Error("AI response contained malformed commit message data.");throw a.name="MalformedCommitMessage",a.content=t,a}const u=l.map(a=>this.extractMessageAsType(a,e)).map(a=>({title:`${a.subject}`,value:`${a.subject}${a.body?`
8
+
9
+ ${a.body}`:""}${a.footer?`
10
+
11
+ ${a.footer}`:""}`})).slice(0,r);if(this.isLoggingEnabled()){const a=u.map(m=>m.title).join(", ");$.info(`${this.serviceName} Parsed ${u.length} commit messages: ${a}`)}return u}extractMessageAsType(t,e){switch(e){case"conventional":const r=/(\w+)(?:\(.*?\))?:\s*(.*)/,s=t.subject.match(r),n=s?s[0]:t.subject;return{...t,subject:this.normalizeCommitMessage(n)};case"gitmoji":const o=/:\w*:\s*(.*)/,i=t.subject.match(o),l=this.params.config.disableLowerCase??!1;return{...t,subject:i&&!l?i[0].toLowerCase():t.subject};default:return t}}normalizeCommitMessage(t){const e=/^(\w+)(\(.*?\))?:\s(.*)$/,r=t.match(e);if(r){const[,s,n,o]=r,i=this.params.config.disableLowerCase??!1,l=s.toLowerCase(),f=i?o:o.charAt(0).toLowerCase()+o.slice(1);t=`${l}${n||""}: ${f}`}return t}sanitizeResponse(t){if(typeof t=="string")try{return[{title:`${M(t)}...`,value:t}]}catch{return[]}return t.map(e=>{try{return{title:`${M(e)}...`,value:e}}catch{return{title:"",value:""}}})}isLoggingEnabled(){return this.params.config.logging&&!!this.logSessionId}}export{re as A,W as a,Y as b,V as c,C as d,Q as e,G as l};
@@ -0,0 +1 @@
1
+ import j from"@anthropic-ai/sdk";import S from"chalk";import{concatMap as $,from as b,map as D,catchError as R}from"rxjs";import{fromPromise as U}from"rxjs/internal/observable/innerFrom";import{A as B,l as F,a as O,b as L,c as N,d as T,e as z}from"./ai.service-6f818099.mjs";import{D as W,g as H,b as G,k as K}from"./cli-9533dfd6.mjs";import"fs";import"path";import"@pacote/xxhash";import"winston";import"cleye";import"module";import"crypto";import"os";import"node:buffer";import"node:path";import"node:child_process";import"node:process";import"child_process";import"node:url";import"node:os";import"assert";import"events";import"node:fs";import"buffer";import"stream";import"util";import"node:util";import"inquirer";import"fs/promises";import"readline";import"figlet";import"gradient-string";import"ora";import"inquirer-reactive-list-prompt";import"winston-daily-rotate-file";import"axios";import"url";import"node:fs/promises";import"chokidar";import"rxjs/operators";const J=["claude-4","claude-haiku-4","claude-sonnet-4","claude-opus-4"],X=x=>{const e=x.toLowerCase();return J.some(t=>e===t||e.startsWith(`${t}-`)||e.startsWith(`${t}.`))},Q=10*60*1e3;class V extends B{constructor(e){super(e),this.params=e,this.generateStreamingCommitMessage$=()=>{const{generate:t,type:o}=this.params.config;return this.createStreamingCommitMessages$(n=>{this.streamChunks(n).catch(c=>n.error(c))},o,t)},this.streamChunks=async t=>{const o=this.params.stagedDiff.diff,{systemPrompt:n,systemPromptPath:c,codeReviewPromptPath:s,logging:i,temperature:P,locale:l,generate:d,type:y,maxLength:v,maxTokens:k,topP:p,model:m}=this.params.config,u={...W,locale:l,maxLength:v,type:y,generate:d,systemPrompt:n,systemPromptPath:c,codeReviewPromptPath:s,vcs_branch:this.params.branchName||""},h=H(u),M=K(o,"commit"),C=`${this.params.config.url||"https://api.anthropic.com"}/v1/messages`,I={"Content-Type":"application/json","x-api-key":this.params.config.key,"anthropic-version":"2023-06-01"};F(o,"commit","Anthropic",m,C,I,i),O(o,"commit","Anthropic",h,M,i);const g=X(m),f={max_tokens:k,temperature:P,system:h,messages:[{role:"user",content:M}],model:m,stream:!0,...g?{}:{top_p:p}};L(o,"commit","Anthropic",f,i);const a=Date.now();let A="";try{const r=this.anthropic.messages.stream(f);r.on("text",_=>{A+=_,t.next(_)});const w=await r.finalMessage(),Y=Date.now()-a;N(o,"commit","Anthropic",w,i),T(o,"commit","Anthropic",Y,A,i),t.complete()}catch(r){z(o,"commit","Anthropic",r,i),t.error(r)}},this.colors={primary:"#AE5630",secondary:"#fff"},this.serviceName=S.bgHex(this.colors.primary).hex(this.colors.secondary).bold("[Anthropic]"),this.errorPrefix=S.red.bold("[Anthropic]"),this.anthropic=new j({apiKey:this.params.config.key,...this.params.config.timeout>Q&&{timeout:this.params.config.timeout}})}getServiceSpecificErrorMessage(e){const t=e.message||"";return t.includes("API key")||t.includes("api_key")?"Invalid API key. Check your Anthropic API key in configuration":t.includes("quota")||t.includes("usage")?"API quota exceeded. Check your Anthropic usage limits":t.includes("model_not_found")||t.includes("Model not found")||/model.*does not exist/i.test(t)?"Model not found or not accessible. Check if the Claude model name is correct":t.includes("403")||t.includes("Forbidden")?"Access denied. Your API key may not have permission for this Claude model":t.includes("404")||t.includes("Not Found")?"Model or endpoint not found. Check your Claude model configuration":t.includes("500")||t.includes("Internal Server Error")?"Anthropic server error. Try again later":null}generateCommitMessage$(){return this.params.config.stream||!1?this.generateStreamingCommitMessage$():U(this.generateMessage("commit")).pipe($(t=>b(t)),D(this.formatAsChoice),R(this.handleError$))}generateCodeReview$(){return U(this.generateMessage("review")).pipe($(e=>b(e)),D(e=>({name:`${this.serviceName} ${e.title}`,short:e.title,value:e.value,description:e.value,isError:!1})),R(this.handleError$))}async generateMessage(e){const t=this.params.stagedDiff.diff,{systemPrompt:o,systemPromptPath:n,codeReviewPromptPath:c,logging:s,temperature:i,locale:P,generate:l,type:d,maxLength:y,maxTokens:v,topP:k,model:p}=this.params.config,m={...W,locale:P,maxLength:y,type:d,generate:l,systemPrompt:o,systemPromptPath:n,codeReviewPromptPath:c,vcs_branch:this.params.branchName||""},u=e==="review"?G(m):H(m),h=K(t,e),E=`${this.params.config.url||"https://api.anthropic.com"}/v1/messages`,C={"Content-Type":"application/json","x-api-key":this.params.config.key,"anthropic-version":"2023-06-01"};F(t,e,"Anthropic",p,E,C,s),O(t,e,"Anthropic",u,h,s);const I=X(p),g={max_tokens:v,temperature:i,system:u,messages:[{role:"user",content:h}],model:p,...I?{}:{top_p:k}};L(t,e,"Anthropic",g,s);const f=Date.now();try{const a=await this.anthropic.messages.create(g),A=Date.now()-f;N(t,e,"Anthropic",a,s);const r=a.content.map(({text:w})=>w).join("");return T(t,e,"Anthropic",A,r,s),e==="review"?this.sanitizeResponse(r):this.parseMessage(r,d,l)}catch(a){throw z(t,e,"Anthropic",a,s),a}}}export{V as AnthropicService};
@@ -0,0 +1 @@
1
+ import F from"https";import h from"chalk";import{concatMap as $,from as T,map as x,catchError as L}from"rxjs";import{fromPromise as W}from"rxjs/internal/observable/innerFrom";import{A as q,l as Y,a as z,b as H,d as V,e as _,c as K}from"./ai.service-6f818099.mjs";import{D as J,b as j,g as Q,p as O,s as X,k as Z}from"./cli-9533dfd6.mjs";import"fs";import"path";import"@pacote/xxhash";import"winston";import"cleye";import"module";import"crypto";import"os";import"node:buffer";import"node:path";import"node:child_process";import"node:process";import"child_process";import"node:url";import"node:os";import"assert";import"events";import"node:fs";import"buffer";import"stream";import"util";import"node:util";import"inquirer";import"fs/promises";import"readline";import"figlet";import"gradient-string";import"ora";import"inquirer-reactive-list-prompt";import"winston-daily-rotate-file";import"axios";import"url";import"node:fs/promises";import"chokidar";import"rxjs/operators";const g="Bedrock",S={MISSING_DEPENDENCY:"MissingDependencyError",MISSING_REGION:"MissingRegionError",MISSING_MODEL_ID:"MissingModelIdError",MISSING_API_KEY:"MissingApiKeyError",MISSING_APPLICATION_KEY:"MissingApplicationKeyError",INVALID_RESPONSE:"InvalidResponseError",EMPTY_RESPONSE:"EmptyResponseError"},i=u=>typeof u=="string"&&u.length>0;let v=null,M=null;const G=u=>{const e=new Error('Amazon Bedrock support requires "@aws-sdk/client-bedrock-runtime" and "@aws-sdk/credential-providers". Install them with `pnpm add @aws-sdk/client-bedrock-runtime @aws-sdk/credential-providers`.');return e.name=S.MISSING_DEPENDENCY,e.originalError=u,e},ee=async()=>{if(v)return v;try{return v=await import("@aws-sdk/client-bedrock-runtime"),v}catch(u){throw v=null,G(u)}},U=async()=>{if(M)return M;try{return M=await import("@aws-sdk/credential-providers"),M}catch(u){throw M=null,G(u)}};class oe extends q{constructor(e){super(e),this.params=e,this.credentialCache=void 0,this.credentialCacheTimestamp=0,this.CREDENTIAL_CACHE_TTL=5*60*1e3,this.bedrockConfig=this.params.config,this.colors={primary:"#232F3E",secondary:"#FF9900"},this.serviceName=h.bgHex(this.colors.primary).hex(this.colors.secondary).bold(`[${g}]`),this.errorPrefix=h.red.bold(`[${g}]`),this.validateConfiguration()}validateConfiguration(){const e=this.bedrockConfig;if(!i(e.model)){const t=new Error("Model ID or inference profile ARN is required.");throw t.name=S.MISSING_MODEL_ID,t}if(!this.getRegion()){const t=new Error("AWS region is required. Configure BEDROCK.region or set AWS_REGION/AWS_DEFAULT_REGION.");throw t.name=S.MISSING_REGION,t}const r=i(e.key),o=this.canUseAwsSdk();if(!r&&!o){const t=new Error("Authentication required: Configure AWS credentials (profile, access keys, IAM role) or API key (BEDROCK.key).");throw t.name=S.MISSING_API_KEY,t}if(r&&!o&&!this.getRegion()&&!i(e.applicationBaseUrl)){const t=new Error("Bearer token authentication requires region or applicationBaseUrl to construct endpoint.");throw t.name=S.MISSING_REGION,t}}canUseAwsSdk(){const e=this.bedrockConfig,r=i(e.profile)||i(process.env.AWS_PROFILE),o=i(e.accessKeyId)&&i(e.secretAccessKey)||i(process.env.AWS_ACCESS_KEY_ID)&&i(process.env.AWS_SECRET_ACCESS_KEY);return r||o}determineAuthMethod(){const e=i(this.bedrockConfig.key);if(this.canUseAwsSdk())return"aws-sdk";if(e)return"bearer-token";throw new Error("No authentication method configured")}getServiceSpecificErrorMessage(e){const r=e?.name||e.code,o=e.message||"";switch(r){case"UnrecognizedClientException":case"InvalidSignatureException":return"Authentication with AWS failed. Check your IAM credentials or Bedrock API key settings.";case"AccessDeniedException":return"Access denied. Ensure the IAM principal or application key has permission to invoke the Bedrock resource.";case"ValidationException":return"Invalid request for the selected Bedrock model. Verify the model ID and payload.";case"ResourceNotFoundException":return"The specified Bedrock model, endpoint, or inference profile could not be found.";case"ThrottlingException":return"Request throttled by Bedrock. Reduce request rate or check service quotas."}return o.includes("Region")?"AWS region is required for Bedrock. Configure BEDROCK.region or set AWS_REGION/AWS_DEFAULT_REGION.":null}generateCommitMessage$(){return W(this.generateMessage("commit")).pipe($(e=>T(e)),x(this.formatAsChoice),L(this.handleError$))}generateCodeReview$(){return W(this.generateMessage("review")).pipe($(e=>T(e)),x(e=>({name:`${this.serviceName} ${e.title}`,short:e.title,value:e.value,description:e.value,isError:!1})),L(this.handleError$))}async generateMessage(e){const r=this.params.stagedDiff.diff,o=this.bedrockConfig,t=o.model,{logging:c,inferenceParameters:n}=o,s={...J,locale:o.locale,maxLength:o.maxLength,type:o.type,generate:o.generate,systemPrompt:o.systemPrompt,systemPromptPath:o.systemPromptPath,codeReviewPromptPath:o.codeReviewPromptPath},a=e==="review"?j(s):Q(s),l=Z(r,e),y={region:this.getRegion(),profile:o.profile,modelId:t},w=`https://bedrock-runtime.${this.getRegion()||"unknown"}.amazonaws.com/model/${encodeURIComponent(t)}/converse`;Y(r,e,g,t,w,y,c),z(r,e,g,a,l,c);const P={modelId:t,systemPrompt:a,userPrompt:l,...n&&Object.keys(n).length>0&&{inferenceConfig:n}};H(r,e,g,P,c);const f=Date.now();try{const m=this.determineAuthMethod();O()&&(console.log(h.cyan(`[Bedrock] Authentication method: ${m}`)),console.log(h.cyan(`[Bedrock] Model ID: ${t}`)),console.log(h.cyan(`[Bedrock] Region: ${this.getRegion()}`)),console.log(h.cyan(`[Bedrock] Has AWS credentials: ${this.canUseAwsSdk()}`)),console.log(h.cyan(`[Bedrock] Has API key: ${i(o.key)}`)));const E=m==="bearer-token"?await this.invokeWithBearerToken({model:t,systemPrompt:a,userPrompt:l,logging:c,requestType:e,diff:r,inferenceConfig:n}):await this.invokeWithAwsSdk({model:t,systemPrompt:a,userPrompt:l,logging:c,requestType:e,diff:r,inferenceConfig:n}),I=Date.now()-f;return V(r,e,g,I,E,c),e==="review"?this.sanitizeResponse(E):this.parseMessage(E,o.type,o.generate)}catch(m){throw _(r,e,g,m,c),m instanceof Error&&(m.status=m?.status||m?.$metadata?.httpStatusCode),m}}async invokeWithAwsSdk(e){const r=this.getRegion(),{model:o,systemPrompt:t,userPrompt:c,inferenceConfig:n,logging:s,requestType:a,diff:l}=e,{BedrockRuntimeClient:y,ConverseCommand:w}=await ee(),P=this.bedrockConfig,f={region:r,requestHandler:{requestTimeout:P.timeout||12e4}},m=await this.resolveCredentials();m&&(f.credentials=m);const E=new y(f),I=new w({modelId:o,messages:[{role:"user",content:[{text:c}]}],...t?{system:[{text:t}]}:{},...n&&Object.keys(n).length>0&&{inferenceConfig:n}});O()&&console.log(h.cyan(`[Bedrock] Sending ConverseCommand with modelId: ${o}`));let C;try{C=await E.send(I),K(l,a,g,C,s)}catch(d){throw O()&&(console.error(h.red(`[Bedrock] AWS SDK Error: ${d.name}`)),console.error(h.red(`[Bedrock] Error message: ${d.message}`)),d.$metadata&&(console.error(h.red(`[Bedrock] Request ID: ${d.$metadata.requestId}`)),console.error(h.red(`[Bedrock] HTTP Status: ${d.$metadata.httpStatusCode}`))),d.$fault&&console.error(h.red(`[Bedrock] Fault: ${d.$fault}`))),d}const A=C.output?.message?.content?.[0]?.text||"";if(!A){const d=new Error("No text content found in Bedrock response.");throw d.name=S.EMPTY_RESPONSE,d.content=C,d}return A}invokeWithBearerToken(e){const{model:r,systemPrompt:o,userPrompt:t,inferenceConfig:c,logging:n,requestType:s,diff:a}=e,l=this.bedrockConfig,y=this.getRegion(),w=encodeURIComponent(r),P=l.applicationBaseUrl||`https://bedrock-runtime.${y}.amazonaws.com/model/${w}/converse`,f=new URL(P),m={modelId:r,messages:[{role:"user",content:[{text:t}]}],...c&&Object.keys(c).length>0&&{inferenceConfig:c}};o&&(m.system=[{text:o}]);const E=JSON.stringify(m),I={"Content-Type":"application/json","Content-Length":Buffer.byteLength(E).toString()};return i(l.key)&&(I.Authorization=`Bearer ${l.key}`),i(l.applicationInferenceProfileArn)&&(I["x-amzn-bedrock-inference-profile-arn"]=l.applicationInferenceProfileArn),i(l.applicationEndpointId)&&(I["x-amzn-bedrock-endpoint-id"]=l.applicationEndpointId),new Promise((C,A)=>{const d=F.request({method:"POST",protocol:f.protocol,hostname:f.hostname,port:f.port,path:f.pathname+f.search,headers:I,timeout:l.timeout},k=>{const R=[];k.on("data",N=>R.push(N)),k.on("end",()=>{const N=Buffer.concat(R).toString("utf8");if(k.statusCode&&k.statusCode>=400){const p=new Error(`Bedrock application endpoint responded with status ${k.statusCode}.`);return p.status=k.statusCode,p.content=N,_(a,s,g,p,n),A(p)}const D=X(N);if(!D.ok){const p=new Error("Failed to parse Bedrock application response as JSON. The Bedrock Converse API should always return valid JSON.");return p.name=S.INVALID_RESPONSE,p.content=N,_(a,s,g,p,n),A(p)}const B=D.data;K(a,s,g,B,n);const b=B.output?.message?.content?.[0]?.text||"";if(!b){const p=new Error("No text content found in Bedrock response.");return p.name=S.EMPTY_RESPONSE,p.content=B,_(a,s,g,p,n),A(p)}C(b)})});d.on("error",k=>{const R=k;_(a,s,g,R,n),A(R)}),d.write(E),d.end()})}getRegion(){return this.bedrockConfig.region||process.env.AWS_REGION||process.env.AWS_DEFAULT_REGION||""}async resolveCredentials(){const e=Date.now();if(this.credentialCache&&e-this.credentialCacheTimestamp<this.CREDENTIAL_CACHE_TTL)return this.credentialCache;const r=this.bedrockConfig,o=r.profile,t=r.accessKeyId,c=r.secretAccessKey,n=r.sessionToken;let s;if(i(o)){const{fromIni:a}=await U();s=a({profile:o})}else if(i(t)&&i(c))s=async()=>({accessKeyId:t,secretAccessKey:c,sessionToken:n||process.env.AWS_SESSION_TOKEN||void 0});else if(process.env.AWS_PROFILE){const{fromIni:a}=await U();s=a({profile:process.env.AWS_PROFILE})}return s&&(this.credentialCache=s,this.credentialCacheTimestamp=e),s}}export{oe as BedrockService};