dmesg --follow
[ 69285720.000 ] posts.x: @noelkimx It doesn't have the password -- I logged in myself, then handed the browser session over. It's dodgy practice to ask for a customer…  |   [ 69280380.000 ] posts.x: @not_sacke Yeah, it's handled things so nicely! I had to take photos of the physical device at the request of the customer support rep, so it pinged…  |   [ 69279960.000 ] posts.x: One of my agents is working a device support issue with a live customer service agent on my behalf. I've got it sending me notifications when it…  |   [ 69280080.000 ] posts.x: Here's the GitHub repo for chrome-agent, so your agent can use the web with, and for, you:  |   [ 69268140.000 ] posts.x: This is particularly useful for agents that you want to update with new information or additional tasks during their run.  |   [ 69264480.000 ] posts.x: /me rattles the Interwebs wires between here and Claude ...  |   [ 69264420.000 ] posts.x: They don't yet know it's down ...  |   [ 69264360.000 ] posts.x: Oh, no -- Claude is down!  |   [ 69131220.000 ] posts.x: Full talk on the OWASP Top 10 for LLM, AI-BOM, MCP exploits, and red teaming a vulnerable student chatbot live:  |   [ 69131220.000 ] posts.x: Ask a chatbot for its system prompt five times and it might refuse every time, then just hand it over on the sixth, not because you changed your…  |   [ 69121740.000 ] posts.x: Full talk on redacting sensitive documents through a self-hosted MCP server while cutting token costs:  |   [ 69121740.000 ] posts.x: Redacting a document's phone numbers and social security numbers before Claude Code ever sees the file, while also cutting token spend in the same…  |   [ 69110640.000 ] posts.x: Full talk on the attention curve, memory pointers, and keeping agent context lean instead of just large:  |   [ 69110640.000 ] posts.x: A bigger context window doesn't fix an agent that's drowning in its own tool output. @ElizabethFue12, a Developer Advocate at AWS, makes that case in…  |  
corey@gallon.me:~/til$

Launching a Web App from Stream Deck

FIGURE 1 ⋅ Launching a Web App from Stream Deck

This is actually much more of a hassle than I’d have expected …

The trick, in my case, is that I have a virtual desktop called Media and, on it, an always open Microsoft Edge browser window also called Media in which I keep the Brain.fm web player open. I want to be able to press a button on the Stream Deck and play or pause Brain.fm from anywhere.

I ended up writing a PowerShell script to accomplish this, using Claude to help. Claude did a relatively terrible job prompting him regularly. The way I ultimately got there quickly was to borrow from Manuel Odendahl’s brilliant suggestions (video and PDF handout) of having the model create a Domain Specific Language (DSL) for the tasks at hand, and then implement specific parts of the DSL.

The resulting utility is available on GitHub here. This was, at first, a hassle but then really effing cool to see how much better the model does with the DSL! h/t Manuel!

<#
.SYNOPSIS
Automates playing Brain.fm in Microsoft Edge on a specific virtual desktop.

.DESCRIPTION
This script navigates to a named virtual desktop, focuses on Microsoft Edge,
switches to the Brain.fm tab, and starts playback. It uses the VirtualDesktop
module and Windows API calls for desktop and window management.

.NOTES
Requires the VirtualDesktop module to be installed.
Feature_Image: ./images/feature_images/20240824_-_Launching_a_Web_App_from_Stream_Deck_3.png

#>

# Import the VirtualDesktop module
# Must be installed as admin by running "Install-Module VirtualDesktop -Scope CurrentUser"
Import-Module VirtualDesktop

Add-Type -AssemblyName System.Windows.Forms

# Add Windows API function declarations for window management
Add-Type @"
using System;
using System.Runtime.InteropServices;

public class User32 {
    [DllImport("user32.dll")]
    public static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

    [DllImport("user32.dll")]
    [return: MarshalAs(UnmanagedType.Bool)]
    public static extern bool SetForegroundWindow(IntPtr hWnd);
}
"@

<#
.SYNOPSIS
Switches to a named virtual desktop.

.PARAMETER name
The name of the virtual desktop to switch to.

.EXAMPLE
Switch-ToNamedDesktop -name "Media"
#>
function Switch-ToNamedDesktop {
    param([string]$name)
    $desktops = Get-DesktopList
    $targetDesktop = $desktops | Where-Object { $_.Name -eq $name }
    if ($targetDesktop) {
        try {
            Switch-Desktop -Desktop $targetDesktop.Number
            Write-Host "Switched to virtual desktop: $name"
        } catch {
            Write-Host "Error switching to desktop: $_"
        }
    } else {
        Write-Host "Virtual desktop '$name' not found."
        Write-Host "Available desktops: $($desktops | ForEach-Object { $_.Name } | Join-String -Separator ', ')"
    }
}

<#
.SYNOPSIS
Sets focus on an application window.

.PARAMETER name
The name of the application to focus on.

.EXAMPLE
Set-ApplicationFocus -name "Edge"
#>
function Set-ApplicationFocus {
    param([string]$name)
    $processes = Get-Process | Where-Object { $_.MainWindowTitle -ne "" }
    Write-Host "Searching for application with name containing '$name'"
    Write-Host "Running processes with non-empty window titles:"
    $processes | ForEach-Object { Write-Host "  - $($_.ProcessName): $($_.MainWindowTitle)" }

    $app = $processes | Where-Object { $_.ProcessName -like "*$name*" -or $_.MainWindowTitle -like "*$name*" } | Select-Object -First 1
    if ($app) {
        [User32]::ShowWindow($app.MainWindowHandle, 9) # 9 = SW_RESTORE
        [User32]::SetForegroundWindow($app.MainWindowHandle)
        Write-Host "Set focus on application: $($app.ProcessName) (Window Title: $($app.MainWindowTitle))"
        return $true
    } else {
        Write-Host "Application with name containing '$name' not found."
        return $false
    }
}

<#
.SYNOPSIS
Switches to a specific tab in Microsoft Edge.

.PARAMETER tabTitle
The title of the tab to switch to.

.EXAMPLE
Switch-EdgeTab -tabTitle "brain.fm"
#>
function Switch-EdgeTab {
    param(
        [string]$tabTitle
    )
    $edge = Get-Process | Where-Object { $_.ProcessName -eq "msedge" -and $_.MainWindowTitle -ne "" } | Select-Object -First 1
    if ($edge) {
        # Activate the Edge window
        [User32]::ShowWindow($edge.MainWindowHandle, 9) # 9 = SW_RESTORE
        [User32]::SetForegroundWindow($edge.MainWindowHandle)
        Write-Host "Activated Edge window: $($edge.MainWindowTitle)"

        # Use correct keyboard shortcut to search tabs
        $wshell = New-Object -ComObject wscript.shell
        $wshell.SendKeys("^+a")  # Ctrl+Shift+A to open tab search
        Start-Sleep -Milliseconds 500  # Wait for search to open
        $wshell.SendKeys($tabTitle)
        Start-Sleep -Milliseconds 500  # Wait for search results
        $wshell.SendKeys("{ENTER}")

        Write-Host "Attempted to switch to tab '$tabTitle' in Edge"
        return $true
    } else {
        Write-Host "No Edge window with a non-empty title found."
        return $false
    }
}

# Main execution
try {
    # Switch to the "Media" virtual desktop
    Switch-ToNamedDesktop -name "Media"

    # Set focus on the Edge browser window
    $focusSuccess = Set-ApplicationFocus -name "Media"

    # Only proceed if focus was successful
    if ($focusSuccess) {
        # Switch to the tab with brain.fm
        $switchSuccess = Switch-EdgeTab -tabTitle "brain.fm"

        if ($switchSuccess) {
            Write-Host "Navigation completed successfully."
            # Wait for 0.5 seconds
            Start-Sleep -Milliseconds 500
            # Send a space key press to start playing Brain.fm
            [System.Windows.Forms.SendKeys]::SendWait(' ')

        } else {
            Write-Host "Navigation partially completed. Failed to switch to the desired tab."
        }
    } else {
        Write-Host "Navigation failed. Could not focus on Microsoft Edge."
    }
} catch {
    Write-Host "An error occurred: $_"
}
corey@gallon.me:~$ tail -f /writing Attach to the stream. An email when I have something worth sending. Replies encouraged!
corey@gallon.me:~$ ls -lt /til ↑2024-08-24 Comments are Failures in Coding
▸2024-08-24 Launching a Web App from Stream Deck ⋅ you are here
↓2024-08-24 Programs Do Things