EtherAssist.psm1
|
# Requires -Version 5.1 #region Internal Helper Functions # Resolves a stable path under the current user's profile. Params: file name. Returns: full path. function Get-EAUserConfigPath { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$FileName ) $home = [Environment]::GetFolderPath('UserProfile') if (-not $home) { throw "Unable to resolve the user profile directory." } return (Join-Path $home $FileName) } # Validates an absolute HTTP/HTTPS URL. Params: url. Returns: $true (or throws). function Assert-EAHttpUrl { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Url ) try { $u = [Uri]$Url } catch { throw "The URL '$Url' is not valid. Please provide a valid HTTP/HTTPS URL." } if (-not $u.IsAbsoluteUri -or $u.Scheme -notin @("http", "https")) { throw "The URL '$Url' is not valid. Please provide a valid HTTP/HTTPS URL." } return $true } # Joins base URL + endpoint and ensures the /api prefix is applied exactly once. Params: base, endpoint. Returns: full URI string. function Join-EAApiUri { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$BaseUrl, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Endpoint ) $base = $BaseUrl.TrimEnd('/') $path = if ($Endpoint.StartsWith('/')) { $Endpoint } else { "/$Endpoint" } if ($path -notmatch '^/api(/|$)') { $path = "/api$path" } if ($base -match '/api$' -and $path -match '^/api(/|$)') { $path = $path.Substring(4) } return "$base$path" } # Extracts HTTP status/body/retry metadata from Invoke-RestMethod errors. Params: error record. Returns: object with StatusCode, BodyText, RetryAfter. function Get-EAHttpErrorInfo { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNull()] [object]$ErrorRecord ) $info = [ordered]@{ StatusCode = $null BodyText = $null RetryAfter = $null Message = $null } try { $info.Message = $ErrorRecord.Exception.Message } catch {} $resp = $null try { if ($ErrorRecord.Exception -and ($ErrorRecord.Exception.PSObject.Properties.Name -contains "Response")) { $resp = $ErrorRecord.Exception.Response } } catch {} if ($resp) { try { if ($resp.PSObject.Properties.Name -contains "StatusCode") { $info.StatusCode = [int]$resp.StatusCode } } catch {} # Retry-After header (HttpWebResponse / HttpResponseMessage). try { if ($resp.PSObject.Properties.Name -contains "Headers" -and $resp.Headers) { $headers = $resp.Headers $ra = $null try { $ra = $headers["Retry-After"] } catch {} if (-not $ra -and ($headers.PSObject.Properties.Name -contains "RetryAfter") -and $headers.RetryAfter) { if ($headers.RetryAfter.Delta) { $ra = [int]$headers.RetryAfter.Delta.TotalSeconds } elseif ($headers.RetryAfter.Date) { $ra = $headers.RetryAfter.Date.ToString("o") } } if ($ra) { $info.RetryAfter = $ra } } } catch {} # Response body (HttpWebResponse stream / HttpResponseMessage content). try { if (($resp.PSObject.Properties.Name -contains "Content") -and $resp.Content) { $info.BodyText = $resp.Content.ReadAsStringAsync().GetAwaiter().GetResult() } elseif ($resp.PSObject.Methods.Name -contains "GetResponseStream") { $sr = [System.IO.StreamReader]::new($resp.GetResponseStream()) try { $info.BodyText = $sr.ReadToEnd() } finally { $sr.Dispose() } } } catch {} } if (-not $info.BodyText) { try { if ($ErrorRecord.ErrorDetails -and $ErrorRecord.ErrorDetails.Message) { $info.BodyText = $ErrorRecord.ErrorDetails.Message } } catch {} } # Parse standard API error payloads for retryAfter/message. if ($info.BodyText) { try { $parsed = $info.BodyText | ConvertFrom-Json -ErrorAction Stop if (-not $info.RetryAfter -and $parsed.retryAfter) { $info.RetryAfter = [string]$parsed.retryAfter } if ($parsed.message) { $info.Message = [string]$parsed.message } if (-not $info.StatusCode -and $parsed.status) { $info.StatusCode = [int]$parsed.status } } catch {} } return [PSCustomObject]$info } # Extracts a human-friendly error string from an API response object. Params: response. Returns: string. function Get-EAErrorText { [CmdletBinding()] param ( [Parameter()] [object]$ResponseObject ) if (-not $ResponseObject) { return "No response received from the API." } if ($ResponseObject.errorMessage) { return [string]$ResponseObject.errorMessage } if ($ResponseObject.message) { return [string]$ResponseObject.message } if ($ResponseObject.reason) { if ($ResponseObject.retryAfter) { return "$($ResponseObject.reason) (retryAfter: $($ResponseObject.retryAfter))" } return [string]$ResponseObject.reason } try { return ($ResponseObject | ConvertTo-Json -Depth 6) } catch { return "Unknown error" } } # Formats an EtherAssist API response into plain text / object / JSON. Params: response + input context. Returns: formatted output. function Format-EAResponse { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNull()] [PSCustomObject]$ResponseObject, [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$InputText, [ValidateNotNullOrEmpty()] [string]$InputType = "Question", [switch]$OutputAsJson, [switch]$OutputAsObject, [switch]$MuteQuestion, [switch]$MuteDateTime, [switch]$MuteAnswer ) if (-not $ResponseObject.answer) { throw "Response object does not contain an answer" } try { if ($OutputAsJson) { $resultObject = [PSCustomObject]@{ $InputType = $InputText Answer = $ResponseObject.answer TimeOfQuery = Get-Date -Format 'o' Metadata = @{ Status = $ResponseObject.status StatusType = $ResponseObject.statusType Model = $ResponseObject.model } } return $resultObject | ConvertTo-Json -Depth 10 } elseif ($OutputAsObject) { $resultObject = [PSCustomObject]@{ $InputType = $InputText Answer = $ResponseObject.answer TimeOfQuery = Get-Date Metadata = @{ Status = $ResponseObject.status StatusType = $ResponseObject.statusType Model = $ResponseObject.model } } return $resultObject } else { $responseMessage = @() if (-not $MuteQuestion) { $responseMessage += "$InputType`: $InputText" } if (-not $MuteDateTime) { $responseMessage += "Date/Time: $(Get-Date)" } $answerText = $ResponseObject.answer if (-not $MuteAnswer) { $responseMessage += "Answer: $answerText" } else { $responseMessage += $answerText } return $responseMessage -join "`n" } } catch { throw "Error formatting response: $_" } } # Saves the EtherAssist API key and base URL for subsequent requests. Params: ApiKey, ApiUrl. Returns: none. function Set-EAApiConfig { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$ApiKey, [Parameter(Mandatory = $false)] [ValidateNotNullOrEmpty()] [ValidateScript({ Assert-EAHttpUrl $_ })] [string]$ApiUrl = "https://api.etherassist.ai" ) try { $KeyFilePath = Get-EAUserConfigPath -FileName "EtherAssistApiKey.xml" $secureApiKey = ConvertTo-SecureString $ApiKey -AsPlainText -Force $settings = @{ ApiKey = $secureApiKey ApiUrl = $ApiUrl } $settings | Export-Clixml -Path $KeyFilePath -Force Write-Verbose "API configuration saved successfully. Using URL: $ApiUrl" } catch { Write-Error "Error storing API Key: $_" } } # Retrieves the saved API key/url (or env var overrides). Params: none. Returns: settings object. function Get-EAApiConfig { [CmdletBinding()] param() $KeyFilePath = Get-EAUserConfigPath -FileName "EtherAssistApiKey.xml" if (Test-Path $KeyFilePath) { try { $settings = Import-Clixml -Path $KeyFilePath $bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($settings.ApiKey) try { $settings.ApiKey = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr) } finally { [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr) } return $settings } catch { Write-Error "Error retrieving API Key: $_" } } else { $apiKey = $env:ETHERASSIST_API_KEY if ($apiKey) { return [PSCustomObject]@{ ApiKey = $apiKey ApiUrl = if ($env:ETHERASSIST_API_URL) { $env:ETHERASSIST_API_URL } else { "https://api.etherassist.ai" } } } Write-Error "API Key and URL are not set. Use Set-EAApiConfig to configure (or set ETHERASSIST_API_KEY / ETHERASSIST_API_URL)." } } # Executes a POST request to the EtherAssist API with retry/backoff. Params: endpoint, body, options. Returns: parsed API response. function Invoke-EtherAssistApi { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Endpoint, [Parameter(Mandatory = $true)] [ValidateNotNull()] [object]$Body, [Parameter()] [ValidateRange(1, 300)] [int]$TimeoutSec = 30, [Parameter()] [ValidateRange(1, 10)] [int]$MaxRetries = 3, [Parameter()] [switch]$EnableLogging ) $settings = Get-EAApiConfig if (-not $settings) { Write-Error "API settings are not set. Use Set-EAApiConfig to set the key and URL." return } $adv = Get-EAApiAdvancedConfig if (-not $adv) { $adv = [PSCustomObject]@{ DefaultTimeout = 30; MaxRetries = 3; EnableLogging = $false; ProxyUrl = $null } } if (-not $PSBoundParameters.ContainsKey('TimeoutSec')) { $TimeoutSec = if ($null -ne $adv.DefaultTimeout) { [int]$adv.DefaultTimeout } else { 30 } } if (-not $PSBoundParameters.ContainsKey('MaxRetries')) { $MaxRetries = if ($null -ne $adv.MaxRetries) { [int]$adv.MaxRetries } else { 3 } } $logEnabled = $EnableLogging.IsPresent -or ([bool]($adv.EnableLogging)) $proxyUrl = if ($adv.ProxyUrl) { [string]$adv.ProxyUrl } else { $null } $uri = Join-EAApiUri -BaseUrl ([string]$settings.ApiUrl) -Endpoint $Endpoint $headers = @{ Authorization = "Bearer $($settings.ApiKey)" 'Content-Type' = 'application/json' Accept = 'application/json' } if ($logEnabled) { $safeHeaders = [ordered]@{} foreach ($k in $headers.Keys) { $safeHeaders[$k] = $headers[$k] } if ($safeHeaders.Authorization) { $safeHeaders.Authorization = "Bearer ****" } Write-Verbose "Request URI: $uri" Write-Verbose "Headers: $($safeHeaders | ConvertTo-Json)" Write-Verbose "Body: $($Body | ConvertTo-Json)" } $retryCount = 0 $success = $false while (-not $success -and $retryCount -lt $MaxRetries) { try { $oldProgressPreference = $ProgressPreference $ProgressPreference = 'SilentlyContinue' try { if ($PSVersionTable.PSEdition -eq 'Desktop') { # Ensure modern TLS for Windows PowerShell 5.1. [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 } $irmParams = @{ Uri = $uri Method = 'Post' Headers = $headers Body = ($Body | ConvertTo-Json -Depth 10) TimeoutSec = $TimeoutSec ErrorAction = 'Stop' } if ($proxyUrl) { $irmParams.Proxy = $proxyUrl } $response = Invoke-RestMethod @irmParams $success = $true return $response } finally { $ProgressPreference = $oldProgressPreference } } catch { $retryCount++ $errInfo = Get-EAHttpErrorInfo -ErrorRecord $_ $statusCode = $errInfo.StatusCode $retryable = ($null -eq $statusCode) -or ($statusCode -in 408, 429, 500, 502, 503, 504) if (-not $retryable -or $retryCount -ge $MaxRetries) { if ($errInfo.BodyText) { try { $obj = $errInfo.BodyText | ConvertFrom-Json -ErrorAction Stop if (-not $obj.success) { $obj | Add-Member NoteProperty success $false -Force } if (-not $obj.status -and $statusCode) { $obj | Add-Member NoteProperty status $statusCode -Force } return $obj } catch { return [PSCustomObject]@{ success = $false; status = (if ($null -ne $statusCode) { [int]$statusCode } else { 500 }); message = $errInfo.BodyText } } } return [PSCustomObject]@{ success = $false; status = (if ($null -ne $statusCode) { [int]$statusCode } else { 500 }); message = (if ($errInfo.Message) { $errInfo.Message } else { "Request failed" }) } } $sleepSec = $null if ($statusCode -eq 429 -and $errInfo.RetryAfter) { if ($errInfo.RetryAfter -is [int]) { $sleepSec = [int]$errInfo.RetryAfter } else { try { $retryAt = [datetime]::Parse([string]$errInfo.RetryAfter) $sleepSec = [math]::Ceiling(($retryAt.ToUniversalTime() - (Get-Date).ToUniversalTime()).TotalSeconds) } catch {} } } if (-not $sleepSec) { $sleepSec = [math]::Min(30, (2 * [math]::Pow(2, [math]::Min($retryCount, 4))) + (Get-Random -Minimum 0 -Maximum 3)) } if ($logEnabled) { Write-Verbose "Retrying in $sleepSec seconds (attempt $($retryCount + 1) of $MaxRetries)..." } Start-Sleep -Seconds ([math]::Max(1, $sleepSec)) } } } # Sends a one-off question to the EtherAssist API. Params: Question + output switches. Returns: formatted output. function Send-EARequest { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Question, [switch]$UseAdvancedModel, [switch]$MuteQuestion, [switch]$MuteDateTime, [switch]$MuteAnswer, [switch]$OutputAsJson, [switch]$OutputAsObject ) $body = @{ question = $Question useAdvancedModel = $UseAdvancedModel.IsPresent } $responseObject = Invoke-EtherAssistApi -Endpoint "/question" -Body $body if ($responseObject.success -eq $true) { Format-EAResponse -ResponseObject $responseObject -InputText $Question -InputType "Question" ` -OutputAsJson:$OutputAsJson -OutputAsObject:$OutputAsObject ` -MuteQuestion:$MuteQuestion -MuteDateTime:$MuteDateTime -MuteAnswer:$MuteAnswer } else { Write-Error "API request was not successful: $(Get-EAErrorText -ResponseObject $responseObject)" } } # Sends a basic completion request (no topics/embeddings) via EtherAssist. Params: Question + optional SystemPrompt/Model. Returns: formatted output. function Send-EACompletion { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Question, [Parameter()] [ValidateNotNullOrEmpty()] [string]$SystemPrompt, [Parameter()] [ValidateNotNullOrEmpty()] [string]$Model, [switch]$UseAdvancedModel, [switch]$MuteQuestion, [switch]$MuteDateTime, [switch]$MuteAnswer, [switch]$OutputAsJson, [switch]$OutputAsObject ) $body = @{ question = $Question useAdvancedModel = $UseAdvancedModel.IsPresent } if ($SystemPrompt) { $body.systemPrompt = $SystemPrompt } if ($Model) { $body.model = $Model } $responseObject = Invoke-EtherAssistApi -Endpoint "/completions" -Body $body if ($responseObject.success -eq $true) { Format-EAResponse -ResponseObject $responseObject -InputText $Question -InputType "Question" ` -OutputAsJson:$OutputAsJson -OutputAsObject:$OutputAsObject ` -MuteQuestion:$MuteQuestion -MuteDateTime:$MuteDateTime -MuteAnswer:$MuteAnswer } else { Write-Error "API request was not successful: $(Get-EAErrorText -ResponseObject $responseObject)" } } # Prompts interactively for a question and sends it to EtherAssist. Params: switches. Returns: formatted output. function Invoke-EAQuery { [CmdletBinding()] param ( [switch]$UseAdvancedModel, [switch]$MuteQuestion, [switch]$MuteDateTime, [switch]$MuteAnswer ) try { $question = Read-Host "Please enter the question for EtherAssist" Send-EARequest -Question $question -UseAdvancedModel:$UseAdvancedModel -MuteQuestion:$MuteQuestion -MuteDateTime:$MuteDateTime -MuteAnswer:$MuteAnswer } catch { Write-Error "An error occurred while invoking EtherAssist query: $_" } } # Sends a reflective (cognitive) question to EtherAssist. Params: Question + output switches. Returns: formatted output. function Send-EAReflectiveRequest { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Question, [switch]$MuteQuestion, [switch]$MuteDateTime, [switch]$MuteAnswer, [switch]$OutputAsJson, [switch]$OutputAsObject ) $body = @{ question = $Question } $responseObject = Invoke-EtherAssistApi -Endpoint "/reflective" -Body $body if ($responseObject.success -eq $true) { Format-EAResponse -ResponseObject $responseObject -InputText $Question -InputType "Question" ` -OutputAsJson:$OutputAsJson -OutputAsObject:$OutputAsObject ` -MuteQuestion:$MuteQuestion -MuteDateTime:$MuteDateTime -MuteAnswer:$MuteAnswer } else { Write-Error "API request was not successful: $(Get-EAErrorText -ResponseObject $responseObject)" } } # Performs a web search via EtherAssist and returns a summarized answer. Params: Query + output switches. Returns: formatted output. function Send-EAWebSearch { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Query, [switch]$OutputAsJson, [switch]$OutputAsObject, [switch]$MuteQuestion, [switch]$MuteDateTime, [switch]$MuteAnswer ) $body = @{ question = $Query } $responseObject = Invoke-EtherAssistApi -Endpoint "/web-search" -Body $body if ($responseObject.success -eq $true) { Format-EAResponse -ResponseObject $responseObject -InputText $Query -InputType "SearchQuery" ` -OutputAsJson:$OutputAsJson -OutputAsObject:$OutputAsObject ` -MuteQuestion:$MuteQuestion -MuteDateTime:$MuteDateTime -MuteAnswer:$MuteAnswer } else { Write-Error "API request was not successful: $(Get-EAErrorText -ResponseObject $responseObject)" } } # Converts Batch script text to PowerShell via EtherAssist. Params: BatchCode + output switches. Returns: formatted output. function Convert-EABatchToPs { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$BatchCode, [switch]$OutputAsJson, [switch]$OutputAsObject ) $body = @{ question = $BatchCode } $responseObject = Invoke-EtherAssistApi -Endpoint "/gen/convert-batch-to-ps" -Body $body if ($responseObject.success -eq $true) { Format-EAResponse -ResponseObject $responseObject -InputText $BatchCode -InputType "BatchScript" ` -OutputAsJson:$OutputAsJson -OutputAsObject:$OutputAsObject } else { Write-Error "API request was not successful: $(Get-EAErrorText -ResponseObject $responseObject)" } } # Generates a knowledge article for a topic via EtherAssist. Params: Topic + output switches. Returns: formatted output. function New-EAKnowledgeArticle { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Topic, [switch]$OutputAsJson, [switch]$OutputAsObject, [switch]$MuteQuestion, [switch]$MuteDateTime, [switch]$MuteAnswer ) $body = @{ question = $Topic } $responseObject = Invoke-EtherAssistApi -Endpoint "/gen/create-knowledge-article" -Body $body if ($responseObject.success -eq $true) { Format-EAResponse -ResponseObject $responseObject -InputText $Topic -InputType "Topic" ` -OutputAsJson:$OutputAsJson -OutputAsObject:$OutputAsObject ` -MuteQuestion:$MuteQuestion -MuteDateTime:$MuteDateTime -MuteAnswer:$MuteAnswer } else { Write-Error "API request was not successful: $(Get-EAErrorText -ResponseObject $responseObject)" } } # Persists advanced request defaults (timeout/retry/logging/proxy). Params: settings. Returns: none. function Set-EAApiAdvancedConfig { [CmdletBinding()] param ( [Parameter()] [ValidateRange(1, 300)] [int]$DefaultTimeout = 30, [Parameter()] [ValidateRange(1, 10)] [int]$MaxRetries = 3, [Parameter()] [switch]$EnableLogging, [Parameter()] [ValidateScript({ if ([string]::IsNullOrEmpty($_)) { return $true } return (Assert-EAHttpUrl $_) })] [string]$ProxyUrl ) try { $ConfigFilePath = Get-EAUserConfigPath -FileName "EtherAssistConfig.xml" $config = @{ DefaultTimeout = $DefaultTimeout MaxRetries = $MaxRetries EnableLogging = $EnableLogging.IsPresent ProxyUrl = $ProxyUrl LastModified = Get-Date } # Encrypt sensitive data if ($ProxyUrl) { $secureProxyUrl = ConvertTo-SecureString $ProxyUrl -AsPlainText -Force $config.ProxyUrl = $secureProxyUrl } $config | Export-Clixml -Path $ConfigFilePath -Force Write-Verbose "Advanced configuration saved successfully" } catch { Write-Error "Error saving advanced configuration: $_" } } # Loads advanced request defaults (or returns built-in defaults). Params: none. Returns: config object. function Get-EAApiAdvancedConfig { [CmdletBinding()] param() $ConfigFilePath = Get-EAUserConfigPath -FileName "EtherAssistConfig.xml" if (Test-Path $ConfigFilePath) { try { $config = Import-Clixml -Path $ConfigFilePath # Decrypt sensitive data if ($config.ProxyUrl -is [System.Security.SecureString]) { $bstr = [System.Runtime.InteropServices.Marshal]::SecureStringToBSTR($config.ProxyUrl) try { $config.ProxyUrl = [System.Runtime.InteropServices.Marshal]::PtrToStringBSTR($bstr) } finally { [System.Runtime.InteropServices.Marshal]::ZeroFreeBSTR($bstr) } } return [PSCustomObject]$config } catch { Write-Error "Error retrieving advanced configuration: $_" } } else { Write-Verbose "No advanced configuration found. Using defaults." return [PSCustomObject]@{ DefaultTimeout = 30 MaxRetries = 3 EnableLogging = $false ProxyUrl = $null LastModified = $null } } } # Generates a short title from input text via EtherAssist. Params: Text + output switches. Returns: formatted output. function Get-EATitle { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Text, [switch]$OutputAsJson, [switch]$OutputAsObject ) $body = @{ question = $Text } $responseObject = Invoke-EtherAssistApi -Endpoint "/utils/title" -Body $body if ($responseObject.success -eq $true) { Format-EAResponse -ResponseObject $responseObject -InputText $Text -InputType "Text" ` -OutputAsJson:$OutputAsJson -OutputAsObject:$OutputAsObject } else { Write-Error "API request was not successful: $(Get-EAErrorText -ResponseObject $responseObject)" } } # Summarizes input text via EtherAssist. Params: Text + output switches. Returns: formatted output. function Get-EATextSummary { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$Text, [switch]$OutputAsJson, [switch]$OutputAsObject ) $body = @{ question = $Text } $responseObject = Invoke-EtherAssistApi -Endpoint "/utils/Summarize" -Body $body if ($responseObject.success -eq $true) { Format-EAResponse -ResponseObject $responseObject -InputText $Text -InputType "Text" ` -OutputAsJson:$OutputAsJson -OutputAsObject:$OutputAsObject } else { Write-Error "API request was not successful: $(Get-EAErrorText -ResponseObject $responseObject)" } } # Summarizes Azure billing data into a structured report. Params: bill text. Returns: formatted output. function Get-EAAzureBillSummary { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$BillText, [switch]$OutputAsJson, [switch]$OutputAsObject ) $body = @{ question = $BillText } $responseObject = Invoke-EtherAssistApi -Endpoint "/utils/SummarizeBill" -Body $body if ($responseObject.success -eq $true) { Format-EAResponse -ResponseObject $responseObject -InputText $BillText -InputType "Bill" ` -OutputAsJson:$OutputAsJson -OutputAsObject:$OutputAsObject } else { Write-Error "API request was not successful: $(Get-EAErrorText -ResponseObject $responseObject)" } } # Analyzes log content via EtherAssist. Params: LogContent + output switches. Returns: formatted output. function Get-EALogAnalysis { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$LogContent, [switch]$OutputAsJson, [switch]$OutputAsObject ) $body = @{ question = $LogContent } $responseObject = Invoke-EtherAssistApi -Endpoint "/gen/log-analyze" -Body $body if ($responseObject.success -eq $true) { Format-EAResponse -ResponseObject $responseObject -InputText $LogContent -InputType "LogContent" ` -OutputAsJson:$OutputAsJson -OutputAsObject:$OutputAsObject } else { Write-Error "API request was not successful: $(Get-EAErrorText -ResponseObject $responseObject)" } } # Explains an error code/string and suggests resolutions via EtherAssist. Params: ErrorCode + output switches. Returns: formatted output. function Get-EAErrorCodeDescription { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$ErrorCode, [switch]$OutputAsJson, [switch]$OutputAsObject ) $body = @{ question = $ErrorCode } $responseObject = Invoke-EtherAssistApi -Endpoint "/gen/errorcode" -Body $body if ($responseObject.success -eq $true) { Format-EAResponse -ResponseObject $responseObject -InputText $ErrorCode -InputType "ErrorCode" ` -OutputAsJson:$OutputAsJson -OutputAsObject:$OutputAsObject } else { Write-Error "API request was not successful: $(Get-EAErrorText -ResponseObject $responseObject)" } } # Converts VBScript text to PowerShell via EtherAssist. Params: VbsCode + output switches. Returns: formatted output. function Convert-EAVbsToPs { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$VbsCode, [switch]$OutputAsJson, [switch]$OutputAsObject ) $body = @{ question = $VbsCode } $responseObject = Invoke-EtherAssistApi -Endpoint "/gen/convert-vbs-to-ps" -Body $body if ($responseObject.success -eq $true) { Format-EAResponse -ResponseObject $responseObject -InputText $VbsCode -InputType "VbsScript" ` -OutputAsJson:$OutputAsJson -OutputAsObject:$OutputAsObject } else { Write-Error "API request was not successful: $(Get-EAErrorText -ResponseObject $responseObject)" } } # Provides an application description via EtherAssist. Params: AppName + output switches. Returns: formatted output. function Get-EAAppDescription { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$AppName, [switch]$OutputAsJson, [switch]$OutputAsObject ) $body = @{ question = $AppName } $responseObject = Invoke-EtherAssistApi -Endpoint "/apps/app-description" -Body $body if ($responseObject.success -eq $true) { Format-EAResponse -ResponseObject $responseObject -InputText $AppName -InputType "AppName" ` -OutputAsJson:$OutputAsJson -OutputAsObject:$OutputAsObject } else { Write-Error "API request was not successful: $(Get-EAErrorText -ResponseObject $responseObject)" } } # Provides silent installer arguments for an app via EtherAssist. Params: AppName + output switches. Returns: formatted output. function Get-EAAppInstallerArgs { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$AppName, [switch]$OutputAsJson, [switch]$OutputAsObject ) $body = @{ question = $AppName } $responseObject = Invoke-EtherAssistApi -Endpoint "/apps/installer-args" -Body $body if ($responseObject.success -eq $true) { Format-EAResponse -ResponseObject $responseObject -InputText $AppName -InputType "AppName" ` -OutputAsJson:$OutputAsJson -OutputAsObject:$OutputAsObject } else { Write-Error "API request was not successful: $(Get-EAErrorText -ResponseObject $responseObject)" } } # Analyzes MSIX AppxManifest.xml content (optionally App Attach) via EtherAssist. Params: ManifestContent + switches. Returns: formatted output. function Get-EAMsixAnalysis { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$ManifestContent, [switch]$AppAttachAnalysis, [switch]$OutputAsJson, [switch]$OutputAsObject ) $body = @{ question = $ManifestContent } $endpoint = if ($AppAttachAnalysis) { "/apps/msix-app-attach-analysis" } else { "/apps/msix-analysis" } $responseObject = Invoke-EtherAssistApi -Endpoint $endpoint -Body $body if ($responseObject.success -eq $true) { Format-EAResponse -ResponseObject $responseObject -InputText $ManifestContent -InputType "MsixManifest" ` -OutputAsJson:$OutputAsJson -OutputAsObject:$OutputAsObject } else { Write-Error "API request was not successful: $(Get-EAErrorText -ResponseObject $responseObject)" } } # Analyzes Entra sign-in/audit log data for threats via EtherAssist. Params: LogData + output switches. Returns: formatted output. function Get-EAEntraThreatAnalysis { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$LogData, [switch]$OutputAsJson, [switch]$OutputAsObject ) $body = @{ question = $LogData } $responseObject = Invoke-EtherAssistApi -Endpoint "/entra-threat-detection" -Body $body if ($responseObject.success -eq $true) { Format-EAResponse -ResponseObject $responseObject -InputText $LogData -InputType "EntraLogs" ` -OutputAsJson:$OutputAsJson -OutputAsObject:$OutputAsObject } else { Write-Error "API request was not successful: $(Get-EAErrorText -ResponseObject $responseObject)" } } # Analyzes PCAP content for suspicious activity via EtherAssist. Params: PcapContent + output switches. Returns: formatted output. function Get-EAPcapAnalysis { [CmdletBinding()] param ( [Parameter(Mandatory = $true)] [ValidateNotNullOrEmpty()] [string]$PcapContent, [switch]$OutputAsJson, [switch]$OutputAsObject ) $body = @{ question = $PcapContent } $responseObject = Invoke-EtherAssistApi -Endpoint "/utils/pcap-analyze" -Body $body if ($responseObject.success -eq $true) { Format-EAResponse -ResponseObject $responseObject -InputText $PcapContent -InputType "PcapData" ` -OutputAsJson:$OutputAsJson -OutputAsObject:$OutputAsObject } else { Write-Error "API request was not successful: $(Get-EAErrorText -ResponseObject $responseObject)" } } Export-ModuleMember -Function Set-EAApiConfig, Get-EAApiConfig, Send-EARequest, Send-EACompletion, Send-EAReflectiveRequest, Invoke-EAQuery, Send-EAWebSearch, Convert-EABatchToPs, New-EAKnowledgeArticle, Set-EAApiAdvancedConfig, Get-EAApiAdvancedConfig, Format-EAResponse, Get-EATitle, Get-EATextSummary, Get-EAAzureBillSummary, Get-EALogAnalysis, Get-EAErrorCodeDescription, Convert-EAVbsToPs, Get-EAAppDescription, Get-EAAppInstallerArgs, Get-EAMsixAnalysis, Get-EAEntraThreatAnalysis, Get-EAPcapAnalysis |