Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 25 additions & 6 deletions .antigravity-plugin/scripts/monk-launcher-telemetry.sh
Original file line number Diff line number Diff line change
Expand Up @@ -92,16 +92,35 @@ monk_emit_launcher_event() {
_osa="$(uname -m 2>/dev/null || printf unknown)"
_plat="$(printf '%s' "$_osn" | tr '[:upper:]' '[:lower:]')"

# Strip characters that would break the hand-built JSON.
_msan() { printf '%s' "$1" | tr -d '"\\' | tr -d '\n\r'; }
# Encode a shell string as JSON string content. LC_ALL=C makes awk's %c
# byte-preserving, so UTF-8 survives while every U+0000-U+001F byte that JSON
# forbids is escaped. POSIX od/awk keep this usable before the agent exists.
_mjson() {
printf '%s' "$1" |
LC_ALL=C od -An -v -t u1 2>/dev/null |
LC_ALL=C awk '{
for (i = 1; i <= NF; i++) {
b = $i + 0
if (b == 34) printf "\\\""
else if (b == 92) printf "\\\\"
else if (b == 8) printf "\\b"
else if (b == 9) printf "\\t"
else if (b == 10) printf "\\n"
else if (b == 12) printf "\\f"
else if (b == 13) printf "\\r"
else if (b < 32) printf "\\u%04x", b
else printf "%c", b
}
}'
}

_payload="$(
printf '{"api_key":"%s","event":"plugin_launcher_started","distinct_id":"%s","properties":{"launch_client":"%s","host_client":"%s","client":"%s","first_start":%s,"agent_installed":%s,"client_id_source":"%s","plugin_version":"%s","ide_version":"%s","platform":"%s","os_arch":"%s","source":"monk-plugin-launcher"}}' \
"$(_msan "$_ph_key")" "$(_msan "$_cid")" \
"$(_msan "$_mlc")" "$(_msan "$_mlc")" "$(_msan "$_mlc")" \
"$(_mjson "$_ph_key")" "$(_mjson "$_cid")" \
"$(_mjson "$_mlc")" "$(_mjson "$_mlc")" "$(_mjson "$_mlc")" \
"$_first_start" "$_agent_installed" "$_cid_src" \
"$(_msan "$_pv")" "$(_msan "$_mlc_ide")" \
"$(_msan "$_plat")" "$(_msan "$_osa")"
"$(_mjson "$_pv")" "$(_mjson "$_mlc_ide")" \
"$(_mjson "$_plat")" "$(_mjson "$_osa")"
)"

if command -v curl >/dev/null 2>&1; then
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/install-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ jobs:
shell: bash
run: ./tests/start-monk-agent-readiness-timeout.sh

- name: Check launcher telemetry JSON escaping
shell: bash
run: ./tests/monk-launcher-telemetry-json.sh

windows:
name: windows-latest
runs-on: windows-latest
Expand Down
31 changes: 25 additions & 6 deletions plugins/monk/scripts/monk-launcher-telemetry.sh
Original file line number Diff line number Diff line change
Expand Up @@ -92,16 +92,35 @@ monk_emit_launcher_event() {
_osa="$(uname -m 2>/dev/null || printf unknown)"
_plat="$(printf '%s' "$_osn" | tr '[:upper:]' '[:lower:]')"

# Strip characters that would break the hand-built JSON.
_msan() { printf '%s' "$1" | tr -d '"\\' | tr -d '\n\r'; }
# Encode a shell string as JSON string content. LC_ALL=C makes awk's %c
# byte-preserving, so UTF-8 survives while every U+0000-U+001F byte that JSON
# forbids is escaped. POSIX od/awk keep this usable before the agent exists.
_mjson() {
printf '%s' "$1" |
LC_ALL=C od -An -v -t u1 2>/dev/null |
LC_ALL=C awk '{
for (i = 1; i <= NF; i++) {
b = $i + 0
if (b == 34) printf "\\\""
else if (b == 92) printf "\\\\"
else if (b == 8) printf "\\b"
else if (b == 9) printf "\\t"
else if (b == 10) printf "\\n"
else if (b == 12) printf "\\f"
else if (b == 13) printf "\\r"
else if (b < 32) printf "\\u%04x", b
else printf "%c", b
}
}'
}

_payload="$(
printf '{"api_key":"%s","event":"plugin_launcher_started","distinct_id":"%s","properties":{"launch_client":"%s","host_client":"%s","client":"%s","first_start":%s,"agent_installed":%s,"client_id_source":"%s","plugin_version":"%s","ide_version":"%s","platform":"%s","os_arch":"%s","source":"monk-plugin-launcher"}}' \
"$(_msan "$_ph_key")" "$(_msan "$_cid")" \
"$(_msan "$_mlc")" "$(_msan "$_mlc")" "$(_msan "$_mlc")" \
"$(_mjson "$_ph_key")" "$(_mjson "$_cid")" \
"$(_mjson "$_mlc")" "$(_mjson "$_mlc")" "$(_mjson "$_mlc")" \
"$_first_start" "$_agent_installed" "$_cid_src" \
"$(_msan "$_pv")" "$(_msan "$_mlc_ide")" \
"$(_msan "$_plat")" "$(_msan "$_osa")"
"$(_mjson "$_pv")" "$(_mjson "$_mlc_ide")" \
"$(_mjson "$_plat")" "$(_mjson "$_osa")"
)"

if command -v curl >/dev/null 2>&1; then
Expand Down
31 changes: 25 additions & 6 deletions scripts/monk-launcher-telemetry.sh
Original file line number Diff line number Diff line change
Expand Up @@ -92,16 +92,35 @@ monk_emit_launcher_event() {
_osa="$(uname -m 2>/dev/null || printf unknown)"
_plat="$(printf '%s' "$_osn" | tr '[:upper:]' '[:lower:]')"

# Strip characters that would break the hand-built JSON.
_msan() { printf '%s' "$1" | tr -d '"\\' | tr -d '\n\r'; }
# Encode a shell string as JSON string content. LC_ALL=C makes awk's %c
# byte-preserving, so UTF-8 survives while every U+0000-U+001F byte that JSON
# forbids is escaped. POSIX od/awk keep this usable before the agent exists.
_mjson() {
printf '%s' "$1" |
LC_ALL=C od -An -v -t u1 2>/dev/null |
LC_ALL=C awk '{
for (i = 1; i <= NF; i++) {
b = $i + 0
if (b == 34) printf "\\\""
else if (b == 92) printf "\\\\"
else if (b == 8) printf "\\b"
else if (b == 9) printf "\\t"
else if (b == 10) printf "\\n"
else if (b == 12) printf "\\f"
else if (b == 13) printf "\\r"
else if (b < 32) printf "\\u%04x", b
else printf "%c", b
}
}'
}

_payload="$(
printf '{"api_key":"%s","event":"plugin_launcher_started","distinct_id":"%s","properties":{"launch_client":"%s","host_client":"%s","client":"%s","first_start":%s,"agent_installed":%s,"client_id_source":"%s","plugin_version":"%s","ide_version":"%s","platform":"%s","os_arch":"%s","source":"monk-plugin-launcher"}}' \
"$(_msan "$_ph_key")" "$(_msan "$_cid")" \
"$(_msan "$_mlc")" "$(_msan "$_mlc")" "$(_msan "$_mlc")" \
"$(_mjson "$_ph_key")" "$(_mjson "$_cid")" \
"$(_mjson "$_mlc")" "$(_mjson "$_mlc")" "$(_mjson "$_mlc")" \
"$_first_start" "$_agent_installed" "$_cid_src" \
"$(_msan "$_pv")" "$(_msan "$_mlc_ide")" \
"$(_msan "$_plat")" "$(_msan "$_osa")"
"$(_mjson "$_pv")" "$(_mjson "$_mlc_ide")" \
"$(_mjson "$_plat")" "$(_mjson "$_osa")"
)"

if command -v curl >/dev/null 2>&1; then
Expand Down
79 changes: 79 additions & 0 deletions tests/monk-launcher-telemetry-json.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#!/usr/bin/env sh
# Regression coverage for #179: launcher telemetry must emit valid JSON without
# losing quotes, slashes, controls, or UTF-8 bytes from dynamic properties.
set -eu

repo_root="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)"
work_dir="$(mktemp -d)"
trap 'rm -rf "$work_dir"' EXIT HUP INT TERM
capture_path="$work_dir/payload.json"

curl() {
_test_payload=""
while [ "$#" -gt 0 ]; do
if [ "$1" = "-d" ]; then
shift
_test_payload="$1"
fi
shift
done
printf '%s' "$_test_payload" >"$capture_path"
}

# shellcheck source=../scripts/monk-launcher-telemetry.sh
. "$repo_root/scripts/monk-launcher-telemetry.sh"

client="$(printf 'codex"\\\tline\nctrl\001<caf\303\251>&end')"
ide_version="$(printf 'v1\r\f\b\002done')"
plugin_version="$(printf 'plugin"\\\t\n\003<caf\303\251>')"
posthog_key="$(printf 'key"\\\t\004end')"

HOME="$work_dir/home"
MONK_AGENT_HOME="$work_dir/monk"
MONK_AGENT_INSTALL_DIR="$work_dir/bin"
MONK_POSTHOG_HOST="https://capture.invalid"
MONK_POSTHOG_KEY="$posthog_key"
MONK_PLUGIN_VERSION="$plugin_version"
export HOME MONK_AGENT_HOME MONK_AGENT_INSTALL_DIR
export MONK_POSTHOG_HOST MONK_POSTHOG_KEY MONK_PLUGIN_VERSION
export client ide_version plugin_version posthog_key capture_path

monk_emit_launcher_event "$client" "$ide_version"
wait
test -s "$capture_path"

if [ -z "${PYTHON:-}" ]; then
if command -v python3 >/dev/null 2>&1; then
PYTHON=python3
elif command -v python >/dev/null 2>&1; then
PYTHON=python
else
echo "python is required to validate the captured JSON" >&2
exit 1
fi
fi

"$PYTHON" - <<'PY'
import json
import os

with open(os.environ["capture_path"], encoding="utf-8") as stream:
payload = json.load(stream)

properties = payload["properties"]
assert payload["api_key"] == os.environ["posthog_key"]
assert properties["launch_client"] == os.environ["client"]
assert properties["host_client"] == os.environ["client"]
assert properties["client"] == os.environ["client"]
assert properties["plugin_version"] == os.environ["plugin_version"]
assert properties["ide_version"] == os.environ["ide_version"]
assert properties["first_start"] is True
assert properties["agent_installed"] is False
PY

cmp "$repo_root/scripts/monk-launcher-telemetry.sh" \
"$repo_root/plugins/monk/scripts/monk-launcher-telemetry.sh"
cmp "$repo_root/scripts/monk-launcher-telemetry.sh" \
"$repo_root/.antigravity-plugin/scripts/monk-launcher-telemetry.sh"

printf 'launcher_telemetry_json_status=pass\n'