# Installs the Claude Code bridge for JoboCop (Windows). # Usage: powershell -ExecutionPolicy Bypass -File install.ps1 [-ExtensionId ] # Without an ID it allows both JoboCop IDs: the Chrome Web Store one and the development one # from the manifest "key". Pass an ID only for a build of your own without that key. param([string]$ExtensionId = '') $ErrorActionPreference = 'Stop' $Name = 'app.jobocop.bridge' # Earlier versions installed the bridge under these names and folders; we remove them. $LegacyNames = @('com.bos.job_application_filler', 'com.secretremote.autofill') $LegacyDirs = @('BosJobApplicationFiller', 'SecretRemoteAutofill') # Keep in step with EXTENSION_IDS in src/ai.js. $StoreId = '' # Chrome Web Store, filled in after the first upload $DevId = 'laiejbgdbcmcannblobjbjjfhgdolnif' # from the "key" in manifest.json $ids = if ($ExtensionId) { @($ExtensionId) } else { @($StoreId, $DevId) | Where-Object { $_ } } foreach ($id in $ids) { if ($id -notmatch '^[a-p]{32}$') { throw "That doesn't look like an extension ID: $id. Copy the 32-letter ID from the extension's AI answers settings." } } $src = if ($MyInvocation.MyCommand.Path) { Split-Path -Parent $MyInvocation.MyCommand.Path } else { $null } # The bridge lives outside the extension folder, so moving the extension doesn't break it. $dir = Join-Path $env:LOCALAPPDATA 'JoboCop' New-Item -ItemType Directory -Path $dir -Force | Out-Null # Run from the extension's native-host folder, host.js is next to this script. Run as # `irm https://jobocop.app/bridge/install.ps1 | iex`, it is downloaded from the same place. if ($src -and (Test-Path (Join-Path $src 'host.js'))) { Copy-Item (Join-Path $src 'host.js') (Join-Path $dir 'host.js') -Force } else { Invoke-WebRequest -UseBasicParsing 'https://jobocop.app/bridge/host.js' -OutFile (Join-Path $dir 'host.js') } $node = (Get-Command node -ErrorAction SilentlyContinue).Source $claude = (where.exe claude 2>$null) | Where-Object { $_ -match '\.(exe|cmd)$' } | Select-Object -First 1 if (-not $node) { throw 'Node.js was not found. Install it from https://nodejs.org, open a new terminal and run this again.' } if (-not $claude) { throw "Claude Code ('claude') was not found. Install Claude Code, run 'claude' once to sign in, then run this again." } # Chrome starts the bridge without your terminal's PATH, so remember the absolute paths now. $bat = Join-Path $dir 'run-host.bat' [IO.File]::WriteAllText($bat, "@echo off`r`n`"$node`" `"$dir\host.js`" %*`r`n") $config = @{ claudePath = $claude; path = $env:PATH } | ConvertTo-Json [IO.File]::WriteAllText((Join-Path $dir 'config.json'), $config) $manifestPath = Join-Path $dir "$Name.json" $manifest = @{ name = $Name description = 'JoboCop bridge to Claude Code' path = $bat type = 'stdio' allowed_origins = @($ids | ForEach-Object { "chrome-extension://$_/" }) } | ConvertTo-Json [IO.File]::WriteAllText($manifestPath, $manifest) $keys = @( "HKCU:\Software\Google\Chrome\NativeMessagingHosts\$Name", "HKCU:\Software\Chromium\NativeMessagingHosts\$Name", "HKCU:\Software\Microsoft\Edge\NativeMessagingHosts\$Name", "HKCU:\Software\BraveSoftware\Brave-Browser\NativeMessagingHosts\$Name" ) foreach ($k in $keys) { New-Item -Path $k -Value $manifestPath -Force | Out-Null foreach ($old in $LegacyNames) { Remove-Item -Path $k.Replace($Name, $old) -Force -ErrorAction SilentlyContinue } } foreach ($old in $LegacyDirs) { Remove-Item -Path (Join-Path $env:LOCALAPPDATA $old) -Recurse -Force -ErrorAction SilentlyContinue } Write-Host '' Write-Host "Done. Claude Code: $claude" Write-Host 'Go back to the extension and click Test connection.' Write-Host "Bridge files: $dir (run the installer again after updating the extension)"