From 7c7d00858568ce744fea0261575a2052c798720d Mon Sep 17 00:00:00 2001 From: lxcong Date: Tue, 12 May 2026 03:23:50 +0800 Subject: [PATCH 1/4] feat(install): add --no-telemetry flag and explicit telemetry status --- scripts/install.sh | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/scripts/install.sh b/scripts/install.sh index 30009dc..bfbba7f 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -111,6 +111,9 @@ Options: --force-mcp Re-run MCP auth even if AgentKey is already configured --skip-skill Skip the skill install step (only run MCP auth) --skip-mcp Skip the MCP auth step (only install the skill) + --no-telemetry Disable anonymous usage telemetry (writes + ~/.config/agentkey/telemetry-disabled so the skill + stays opted-out across runs) -h, --help Show this help Behavior: @@ -256,6 +259,7 @@ main() { # `set -u` happy. FORCE_REMOTE=false FORCE_LOCAL=false + local NO_TELEMETRY=false while [ $# -gt 0 ]; do case "$1" in @@ -270,6 +274,7 @@ main() { --force-mcp) FORCE_MCP=true; shift ;; --skip-skill) SKIP_SKILL=true; shift ;; --skip-mcp) SKIP_MCP=true; shift ;; + --no-telemetry) NO_TELEMETRY=true; shift ;; -h|--help) PRINT_HELP=true; shift ;; *) ui_warn "Unknown argument: $1"; shift ;; esac @@ -332,6 +337,19 @@ main() { fi ui_ok "Mode: $MODE" + # Resolve telemetry intent: --no-telemetry overrides everything; existing + # ~/.config/agentkey/telemetry-disabled file means already-opted-out. + local TELEMETRY_OPT_OUT_FILE="$HOME/.config/agentkey/telemetry-disabled" + if $NO_TELEMETRY; then + mkdir -p "$(dirname "$TELEMETRY_OPT_OUT_FILE")" 2>/dev/null || true + touch "$TELEMETRY_OPT_OUT_FILE" 2>/dev/null || true + ui_ok "Telemetry: disabled (--no-telemetry)" + elif [ -f "$TELEMETRY_OPT_OUT_FILE" ]; then + ui_ok "Telemetry: disabled (~/.config/agentkey/telemetry-disabled exists)" + else + ui_info "Telemetry: anonymous usage stats enabled (re-run with --no-telemetry to opt out)" + fi + # Node check local NODE_OK=false NODE_VERSION NODE_MAJOR if command -v node >/dev/null 2>&1; then From 88eb5d20ad8eb8649ea81d58705198f755bb238e Mon Sep 17 00:00:00 2001 From: lxcong Date: Tue, 12 May 2026 03:24:41 +0800 Subject: [PATCH 2/4] feat(install): pass install context env to --auth-login --- scripts/install.sh | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/scripts/install.sh b/scripts/install.sh index bfbba7f..8eb0f31 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -240,6 +240,26 @@ install_node() { ui_ok "Node.js installed" } +# Compute a stable per-device fingerprint for install_completed dedup. +# spec §6.3: sha256(hostname+platform+username)[:16]. Falls back to a random +# value if neither sha256sum nor shasum is available (extremely rare). +compute_device_fingerprint() { + local platform="$1" + local hn user input hash + hn="$(hostname 2>/dev/null || echo "")" + user="${USER:-$(id -un 2>/dev/null || echo "")}" + input="$hn|$platform|$user" + if command -v sha256sum >/dev/null 2>&1; then + hash="$(printf '%s' "$input" | sha256sum | cut -c1-16)" + elif command -v shasum >/dev/null 2>&1; then + hash="$(printf '%s' "$input" | shasum -a 256 | cut -c1-16)" + else + # Last resort: use $RANDOM. Won't dedup across runs but won't crash. + hash="rnd$(printf '%04x%04x%04x' "$RANDOM" "$RANDOM" "$RANDOM")" + fi + printf '%s' "$hash" +} + # ────────────────────────────────────────────────────────────────────────── # main — wraps the entire procedural body so that under `curl | bash` # bash finishes reading the script before any fd-rebinding happens. @@ -489,6 +509,20 @@ main() { fi echo + # Telemetry context: server picks these up from env and capture + # install_completed. Always exported (even when opt-out), because + # AGENTKEY_TELEMETRY=0 is itself one of the signals the server reads. + local _flags="" + for _f in "$@"; do _flags="${_flags:+$_flags,}$_f"; done + export AGENTKEY_TELEMETRY="$($NO_TELEMETRY && echo 0 || echo 1)" + # If the persistent opt-out file exists, force 0 regardless of flag. + [ -f "$TELEMETRY_OPT_OUT_FILE" ] && export AGENTKEY_TELEMETRY=0 + export AGENTKEY_INSTALL_SOURCE="one_liner" + export AGENTKEY_DETECTED_AGENTS="$(detect_agents)" + export AGENTKEY_SELECTED_AGENTS="${TARGETS:-}" + export AGENTKEY_INSTALLER_FLAGS="$_flags" + export AGENTKEY_DEVICE_FINGERPRINT="$(compute_device_fingerprint "$PLATFORM")" + if ! npx -y "$MCP_PACKAGE" "${AUTH_ARGS[@]}"; then ui_error "MCP auth failed." ui_muted "Retry manually: npx -y $MCP_PACKAGE ${AUTH_ARGS[*]}" From b8877a5332a31ed10feb8bfdd1ce466963191e8d Mon Sep 17 00:00:00 2001 From: lxcong Date: Tue, 12 May 2026 03:26:18 +0800 Subject: [PATCH 3/4] feat(install): mirror --no-telemetry support to PowerShell installer --- scripts/install.ps1 | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/scripts/install.ps1 b/scripts/install.ps1 index 3469a70..60cf03e 100644 --- a/scripts/install.ps1 +++ b/scripts/install.ps1 @@ -7,6 +7,7 @@ irm https://agentkey.app/install.ps1 | iex & ([scriptblock]::Create((irm https://agentkey.app/install.ps1))) -Yes & ([scriptblock]::Create((irm https://agentkey.app/install.ps1))) -Only "claude-code,cursor" + & ([scriptblock]::Create((irm https://agentkey.app/install.ps1))) -NoTelemetry Behavior mirrors install.sh: checks Node >= 18 (installs via winget/scoop/choco), auto-detects which AI agents are installed and runs `npx skills add` for them, @@ -28,6 +29,7 @@ param( [switch]$ForceMcp, [switch]$SkipSkill, [switch]$SkipMcp, + [switch]$NoTelemetry, [switch]$Help ) @@ -159,6 +161,9 @@ Parameters: -ForceMcp Re-run MCP auth even if AgentKey is already configured -SkipSkill Skip the skill install step (only run MCP auth) -SkipMcp Skip the MCP auth step (only install the skill) + -NoTelemetry Disable anonymous usage telemetry (writes + ~/.config/agentkey/telemetry-disabled so the skill + stays opted-out across runs) -Help Show this help Behavior: @@ -213,6 +218,19 @@ else { } Write-Ok "Mode: $Mode" +# Resolve telemetry intent: -NoTelemetry overrides everything; existing +# ~/.config/agentkey/telemetry-disabled file means already-opted-out. +$TelemetryOptOutFile = Join-Path $env:USERPROFILE '.config\agentkey\telemetry-disabled' +if ($NoTelemetry) { + New-Item -ItemType Directory -Path (Split-Path $TelemetryOptOutFile) -Force | Out-Null + New-Item -ItemType File -Path $TelemetryOptOutFile -Force | Out-Null + Write-Ok 'Telemetry: disabled (-NoTelemetry)' +} elseif (Test-Path -LiteralPath $TelemetryOptOutFile) { + Write-Ok "Telemetry: disabled ($TelemetryOptOutFile exists)" +} else { + Write-Info 'Telemetry: anonymous usage stats enabled (re-run with -NoTelemetry to opt out)' +} + # Node check function Get-NodeMajor { try { @@ -357,6 +375,31 @@ if ($SkipMcp) { } Write-Host '' + # Telemetry context: server picks these up from env and captures + # install_completed. Always exported (even when opt-out), because + # AGENTKEY_TELEMETRY=0 is itself one of the signals the server reads. + $_hn = [System.Net.Dns]::GetHostName() + $_user = $env:USERNAME + $_input = "$_hn|windows|$_user" + $_bytes = [System.Text.Encoding]::UTF8.GetBytes($_input) + $_sha = [System.Security.Cryptography.SHA256]::Create() + $_hash = ($_sha.ComputeHash($_bytes) | ForEach-Object { $_.ToString('x2') }) -join '' + $DeviceFingerprint = $_hash.Substring(0, 16) + + $DetectedAgents = Get-DetectedAgents + if (-not (Get-Variable -Name targets -Scope Script -ErrorAction SilentlyContinue)) { $targets = @() } + + if ($NoTelemetry -or (Test-Path -LiteralPath $TelemetryOptOutFile)) { + $env:AGENTKEY_TELEMETRY = '0' + } else { + $env:AGENTKEY_TELEMETRY = '1' + } + $env:AGENTKEY_INSTALL_SOURCE = 'one_liner' + $env:AGENTKEY_DETECTED_AGENTS = ($DetectedAgents -join ',') + $env:AGENTKEY_SELECTED_AGENTS = ($targets -join ',') + $env:AGENTKEY_INSTALLER_FLAGS = ($PSBoundParameters.Keys | ForEach-Object { "-$_" }) -join ',' + $env:AGENTKEY_DEVICE_FINGERPRINT = $DeviceFingerprint + & npx -y $McpPackage @authArgs if ($LASTEXITCODE -ne 0) { Write-Err 'MCP auth failed.' From 420f126c8e65cea483d41340ea9949fd7d065738 Mon Sep 17 00:00:00 2001 From: lxcong Date: Tue, 12 May 2026 03:51:52 +0800 Subject: [PATCH 4/4] fix(install): suppress install context at source on opt-out, fix flag capture MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three review findings addressed: 1. Misleading opt-out semantics (both scripts) — when --no-telemetry / -NoTelemetry was active, only AGENTKEY_TELEMETRY=0 signaled the server, but AGENTKEY_DEVICE_FINGERPRINT (sha256 of hostname|platform|user) + AGENTKEY_DETECTED_AGENTS + AGENTKEY_SELECTED_AGENTS + the rest were still computed and exported to the child `npx @agentkey/mcp` process. The help text "Disable anonymous usage telemetry" and the UI banner "Telemetry: disabled" implied source-level suppression — the actual behavior leaked hostname-derived data regardless. Now opt-out is honored at the source: TELEMETRY=0 is the only env exported, the fingerprint is never computed, agent lists never passed. 2. install.ps1 -NoTelemetry help text and inline comment referenced ~/.config/agentkey/telemetry-disabled (Unix path notation) while actually writing to %USERPROFILE%\.config\agentkey\telemetry-disabled. Updated to Windows path notation for both. 3. install.sh: `for _f in "$@"` ran on an exhausted $@ — main()'s arg parse loop shifts every positional out via `shift`, so by the time the telemetry block executed, AGENTKEY_INSTALLER_FLAGS was always empty. Snapshot args at main() entry into _orig_args=("$@") and iterate that. install.ps1 was already correct because $PSBoundParameters captures the parameter set at script entry and is not modified by execution. Verified opt-out path now exports ONLY AGENTKEY_TELEMETRY=0; opt-in path exports all 5 context vars with AGENTKEY_INSTALLER_FLAGS correctly populated (e.g. "--yes,--skip-skill"). Co-Authored-By: Claude Opus 4.7 (1M context) --- scripts/install.ps1 | 47 ++++++++++++++++++++++++--------------------- scripts/install.sh | 37 ++++++++++++++++++++++------------- 2 files changed, 49 insertions(+), 35 deletions(-) diff --git a/scripts/install.ps1 b/scripts/install.ps1 index 60cf03e..ac8b616 100644 --- a/scripts/install.ps1 +++ b/scripts/install.ps1 @@ -162,8 +162,8 @@ Parameters: -SkipSkill Skip the skill install step (only run MCP auth) -SkipMcp Skip the MCP auth step (only install the skill) -NoTelemetry Disable anonymous usage telemetry (writes - ~/.config/agentkey/telemetry-disabled so the skill - stays opted-out across runs) + %USERPROFILE%\.config\agentkey\telemetry-disabled so + the skill stays opted-out across runs) -Help Show this help Behavior: @@ -219,7 +219,7 @@ else { Write-Ok "Mode: $Mode" # Resolve telemetry intent: -NoTelemetry overrides everything; existing -# ~/.config/agentkey/telemetry-disabled file means already-opted-out. +# %USERPROFILE%\.config\agentkey\telemetry-disabled file means already-opted-out. $TelemetryOptOutFile = Join-Path $env:USERPROFILE '.config\agentkey\telemetry-disabled' if ($NoTelemetry) { New-Item -ItemType Directory -Path (Split-Path $TelemetryOptOutFile) -Force | Out-Null @@ -375,30 +375,33 @@ if ($SkipMcp) { } Write-Host '' - # Telemetry context: server picks these up from env and captures - # install_completed. Always exported (even when opt-out), because - # AGENTKEY_TELEMETRY=0 is itself one of the signals the server reads. - $_hn = [System.Net.Dns]::GetHostName() - $_user = $env:USERNAME - $_input = "$_hn|windows|$_user" - $_bytes = [System.Text.Encoding]::UTF8.GetBytes($_input) - $_sha = [System.Security.Cryptography.SHA256]::Create() - $_hash = ($_sha.ComputeHash($_bytes) | ForEach-Object { $_.ToString('x2') }) -join '' - $DeviceFingerprint = $_hash.Substring(0, 16) - - $DetectedAgents = Get-DetectedAgents - if (-not (Get-Variable -Name targets -Scope Script -ErrorAction SilentlyContinue)) { $targets = @() } - + # Telemetry context for install_completed. Opt-out is honored at the + # SOURCE: when AGENTKEY_TELEMETRY=0, no other context env vars are + # exported — hostname-derived fingerprint, agent lists, and installer + # flags are never computed nor passed to the child `npx @agentkey/mcp` + # process. The server treats AGENTKEY_TELEMETRY=0 as a hard skip. if ($NoTelemetry -or (Test-Path -LiteralPath $TelemetryOptOutFile)) { $env:AGENTKEY_TELEMETRY = '0' } else { $env:AGENTKEY_TELEMETRY = '1' + + $_hn = [System.Net.Dns]::GetHostName() + $_user = $env:USERNAME + $_input = "$_hn|windows|$_user" + $_bytes = [System.Text.Encoding]::UTF8.GetBytes($_input) + $_sha = [System.Security.Cryptography.SHA256]::Create() + $_hash = ($_sha.ComputeHash($_bytes) | ForEach-Object { $_.ToString('x2') }) -join '' + $DeviceFingerprint = $_hash.Substring(0, 16) + + $DetectedAgents = Get-DetectedAgents + if (-not (Get-Variable -Name targets -Scope Script -ErrorAction SilentlyContinue)) { $targets = @() } + + $env:AGENTKEY_INSTALL_SOURCE = 'one_liner' + $env:AGENTKEY_DETECTED_AGENTS = ($DetectedAgents -join ',') + $env:AGENTKEY_SELECTED_AGENTS = ($targets -join ',') + $env:AGENTKEY_INSTALLER_FLAGS = ($PSBoundParameters.Keys | ForEach-Object { "-$_" }) -join ',' + $env:AGENTKEY_DEVICE_FINGERPRINT = $DeviceFingerprint } - $env:AGENTKEY_INSTALL_SOURCE = 'one_liner' - $env:AGENTKEY_DETECTED_AGENTS = ($DetectedAgents -join ',') - $env:AGENTKEY_SELECTED_AGENTS = ($targets -join ',') - $env:AGENTKEY_INSTALLER_FLAGS = ($PSBoundParameters.Keys | ForEach-Object { "-$_" }) -join ',' - $env:AGENTKEY_DEVICE_FINGERPRINT = $DeviceFingerprint & npx -y $McpPackage @authArgs if ($LASTEXITCODE -ne 0) { diff --git a/scripts/install.sh b/scripts/install.sh index 8eb0f31..4c1e8c3 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -281,6 +281,10 @@ main() { FORCE_LOCAL=false local NO_TELEMETRY=false + # Snapshot original args before the parse loop shifts them away — needed + # later for AGENTKEY_INSTALLER_FLAGS env passthrough. + local _orig_args=("$@") + while [ $# -gt 0 ]; do case "$1" in -y|--yes) MODE=noninteractive; shift ;; @@ -509,19 +513,26 @@ main() { fi echo - # Telemetry context: server picks these up from env and capture - # install_completed. Always exported (even when opt-out), because - # AGENTKEY_TELEMETRY=0 is itself one of the signals the server reads. - local _flags="" - for _f in "$@"; do _flags="${_flags:+$_flags,}$_f"; done - export AGENTKEY_TELEMETRY="$($NO_TELEMETRY && echo 0 || echo 1)" - # If the persistent opt-out file exists, force 0 regardless of flag. - [ -f "$TELEMETRY_OPT_OUT_FILE" ] && export AGENTKEY_TELEMETRY=0 - export AGENTKEY_INSTALL_SOURCE="one_liner" - export AGENTKEY_DETECTED_AGENTS="$(detect_agents)" - export AGENTKEY_SELECTED_AGENTS="${TARGETS:-}" - export AGENTKEY_INSTALLER_FLAGS="$_flags" - export AGENTKEY_DEVICE_FINGERPRINT="$(compute_device_fingerprint "$PLATFORM")" + # Telemetry context for `install_completed`. Opt-out is honored at + # the SOURCE: when AGENTKEY_TELEMETRY=0, no other context env vars + # are exported — hostname-derived fingerprint, agent lists, and + # installer flags are never computed nor passed to the child + # `npx @agentkey/mcp` process. The server treats AGENTKEY_TELEMETRY=0 + # as a hard skip. + if $NO_TELEMETRY || [ -f "$TELEMETRY_OPT_OUT_FILE" ]; then + export AGENTKEY_TELEMETRY=0 + else + export AGENTKEY_TELEMETRY=1 + local _flags="" + for _f in "${_orig_args[@]:-}"; do + _flags="${_flags:+$_flags,}$_f" + done + export AGENTKEY_INSTALL_SOURCE="one_liner" + export AGENTKEY_DETECTED_AGENTS="$(detect_agents)" + export AGENTKEY_SELECTED_AGENTS="${TARGETS:-}" + export AGENTKEY_INSTALLER_FLAGS="$_flags" + export AGENTKEY_DEVICE_FINGERPRINT="$(compute_device_fingerprint "$PLATFORM")" + fi if ! npx -y "$MCP_PACKAGE" "${AUTH_ARGS[@]}"; then ui_error "MCP auth failed."