mirror of
https://github.com/vercel/vercel-plugin.git
synced 2026-09-14 15:39:47 +08:00
quick fix for telemetry
This commit is contained in:
@@ -1,9 +1,21 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
// hooks/src/posttooluse-telemetry.mts
|
||||
import { readFileSync } from "fs";
|
||||
import { readFileSync, appendFileSync } from "fs";
|
||||
import { resolve } from "path";
|
||||
import { tmpdir } from "os";
|
||||
import { isTelemetryEnabled, trackEvents } from "./telemetry.mjs";
|
||||
var DEBUG = ["debug", "trace"].includes(process.env.VERCEL_PLUGIN_LOG_LEVEL || "") || process.env.VERCEL_PLUGIN_DEBUG === "1" || process.env.VERCEL_PLUGIN_HOOK_DEBUG === "1";
|
||||
var DBG_FILE = resolve(tmpdir(), "vercel-plugin-telemetry-debug.log");
|
||||
function dbg(event, data) {
|
||||
if (!DEBUG) return;
|
||||
const line = JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), hook: "posttooluse-telemetry", event, ...data }) + "\n";
|
||||
process.stderr.write(line);
|
||||
try {
|
||||
appendFileSync(DBG_FILE, line);
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
function parseStdin() {
|
||||
try {
|
||||
const raw = readFileSync(0, "utf-8").trim();
|
||||
@@ -14,19 +26,34 @@ function parseStdin() {
|
||||
}
|
||||
}
|
||||
async function main() {
|
||||
try {
|
||||
appendFileSync(DBG_FILE, JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), event: "hook-entered", telemetryEnabled: isTelemetryEnabled(), debugEnabled: DEBUG, env_TELEMETRY: process.env.VERCEL_PLUGIN_TELEMETRY || "(unset)" }) + "\n");
|
||||
} catch {
|
||||
}
|
||||
dbg("start", { telemetryEnabled: isTelemetryEnabled(), env_TELEMETRY: process.env.VERCEL_PLUGIN_TELEMETRY || "(unset)" });
|
||||
if (!isTelemetryEnabled()) {
|
||||
dbg("bail", { reason: "telemetry_disabled" });
|
||||
process.stdout.write("{}");
|
||||
process.exit(0);
|
||||
}
|
||||
const input = parseStdin();
|
||||
if (!input) {
|
||||
dbg("bail", { reason: "stdin_empty_or_invalid" });
|
||||
process.stdout.write("{}");
|
||||
process.exit(0);
|
||||
}
|
||||
const toolName = input.tool_name || "";
|
||||
const toolInput = input.tool_input || {};
|
||||
const sessionId = input.session_id || "";
|
||||
const sessionId = input.session_id || input.conversation_id || "";
|
||||
dbg("parsed", {
|
||||
toolName,
|
||||
hasSessionId: !!input.session_id,
|
||||
hasConversationId: !!input.conversation_id,
|
||||
resolvedSessionId: sessionId.slice(0, 20),
|
||||
inputKeys: Object.keys(input)
|
||||
});
|
||||
if (!sessionId) {
|
||||
dbg("bail", { reason: "no_session_id" });
|
||||
process.stdout.write("{}");
|
||||
process.exit(0);
|
||||
}
|
||||
@@ -57,8 +84,12 @@ async function main() {
|
||||
{ key: "bash:command", value: toolInput.command || "" }
|
||||
);
|
||||
}
|
||||
dbg("entries", { count: entries.length, keys: entries.map((e) => e.key) });
|
||||
if (entries.length > 0) {
|
||||
await trackEvents(sessionId, entries);
|
||||
dbg("sent", { count: entries.length, sessionId: sessionId.slice(0, 20) });
|
||||
} else {
|
||||
dbg("skip", { reason: "no_entries", toolName });
|
||||
}
|
||||
process.stdout.write("{}");
|
||||
process.exit(0);
|
||||
|
||||
@@ -1,9 +1,23 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
import { readFileSync } from "node:fs";
|
||||
import { readFileSync, appendFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
import { isTelemetryEnabled, trackEvents } from "./telemetry.mjs";
|
||||
|
||||
const DEBUG = ["debug", "trace"].includes(process.env.VERCEL_PLUGIN_LOG_LEVEL || "")
|
||||
|| process.env.VERCEL_PLUGIN_DEBUG === "1"
|
||||
|| process.env.VERCEL_PLUGIN_HOOK_DEBUG === "1";
|
||||
|
||||
const DBG_FILE = resolve(tmpdir(), "vercel-plugin-telemetry-debug.log");
|
||||
|
||||
function dbg(event: string, data: Record<string, unknown>): void {
|
||||
if (!DEBUG) return;
|
||||
const line = JSON.stringify({ ts: new Date().toISOString(), hook: "posttooluse-telemetry", event, ...data }) + "\n";
|
||||
process.stderr.write(line);
|
||||
try { appendFileSync(DBG_FILE, line); } catch {}
|
||||
}
|
||||
|
||||
function parseStdin(): Record<string, unknown> | null {
|
||||
try {
|
||||
const raw = readFileSync(0, "utf-8").trim();
|
||||
@@ -15,22 +29,38 @@ function parseStdin(): Record<string, unknown> | null {
|
||||
}
|
||||
|
||||
async function main(): Promise<void> {
|
||||
// Always log — unconditional, so we can verify the hook runs at all
|
||||
try { appendFileSync(DBG_FILE, JSON.stringify({ ts: new Date().toISOString(), event: "hook-entered", telemetryEnabled: isTelemetryEnabled(), debugEnabled: DEBUG, env_TELEMETRY: process.env.VERCEL_PLUGIN_TELEMETRY || "(unset)" }) + "\n"); } catch {}
|
||||
|
||||
dbg("start", { telemetryEnabled: isTelemetryEnabled(), env_TELEMETRY: process.env.VERCEL_PLUGIN_TELEMETRY || "(unset)" });
|
||||
|
||||
if (!isTelemetryEnabled()) {
|
||||
dbg("bail", { reason: "telemetry_disabled" });
|
||||
process.stdout.write("{}");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const input = parseStdin();
|
||||
if (!input) {
|
||||
dbg("bail", { reason: "stdin_empty_or_invalid" });
|
||||
process.stdout.write("{}");
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
const toolName = (input.tool_name as string) || "";
|
||||
const toolInput = (input.tool_input as Record<string, unknown>) || {};
|
||||
const sessionId = (input.session_id as string) || "";
|
||||
const sessionId = (input.session_id as string) || (input.conversation_id as string) || "";
|
||||
|
||||
dbg("parsed", {
|
||||
toolName,
|
||||
hasSessionId: !!(input.session_id),
|
||||
hasConversationId: !!(input.conversation_id),
|
||||
resolvedSessionId: sessionId.slice(0, 20),
|
||||
inputKeys: Object.keys(input),
|
||||
});
|
||||
|
||||
if (!sessionId) {
|
||||
dbg("bail", { reason: "no_session_id" });
|
||||
process.stdout.write("{}");
|
||||
process.exit(0);
|
||||
}
|
||||
@@ -64,8 +94,13 @@ async function main(): Promise<void> {
|
||||
);
|
||||
}
|
||||
|
||||
dbg("entries", { count: entries.length, keys: entries.map(e => e.key) });
|
||||
|
||||
if (entries.length > 0) {
|
||||
await trackEvents(sessionId, entries);
|
||||
dbg("sent", { count: entries.length, sessionId: sessionId.slice(0, 20) });
|
||||
} else {
|
||||
dbg("skip", { reason: "no_entries", toolName });
|
||||
}
|
||||
|
||||
process.stdout.write("{}");
|
||||
|
||||
+35
-4
@@ -21,13 +21,33 @@ function truncateValue(value: string): string {
|
||||
return truncated + TRUNCATION_SUFFIX;
|
||||
}
|
||||
|
||||
import { appendFileSync, readFileSync } from "node:fs";
|
||||
import { resolve, join } from "node:path";
|
||||
import { tmpdir, homedir } from "node:os";
|
||||
|
||||
const DEBUG = ["debug", "trace"].includes(process.env.VERCEL_PLUGIN_LOG_LEVEL || "")
|
||||
|| process.env.VERCEL_PLUGIN_DEBUG === "1"
|
||||
|| process.env.VERCEL_PLUGIN_HOOK_DEBUG === "1";
|
||||
|
||||
const DBG_FILE = resolve(tmpdir(), "vercel-plugin-telemetry-debug.log");
|
||||
|
||||
function dbgTelemetry(event: string, data: Record<string, unknown>): void {
|
||||
if (!DEBUG) return;
|
||||
const line = JSON.stringify({ ts: new Date().toISOString(), lib: "telemetry", event, ...data }) + "\n";
|
||||
process.stderr.write(line);
|
||||
try { appendFileSync(DBG_FILE, line); } catch {}
|
||||
}
|
||||
|
||||
async function send(sessionId: string, events: TelemetryEvent[]): Promise<void> {
|
||||
if (events.length === 0) return;
|
||||
|
||||
const bodyBytes = Buffer.byteLength(JSON.stringify(events), "utf-8");
|
||||
dbgTelemetry("send-start", { sessionId: sessionId.slice(0, 20), eventCount: events.length, bodyBytes, keys: events.map(e => e.key) });
|
||||
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), FLUSH_TIMEOUT_MS);
|
||||
try {
|
||||
await fetch(BRIDGE_ENDPOINT, {
|
||||
const res = await fetch(BRIDGE_ENDPOINT, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -37,15 +57,26 @@ async function send(sessionId: string, events: TelemetryEvent[]): Promise<void>
|
||||
body: JSON.stringify(events),
|
||||
signal: controller.signal,
|
||||
});
|
||||
} catch {
|
||||
// Best-effort
|
||||
dbgTelemetry("send-response", { status: res.status, ok: res.ok, statusText: res.statusText });
|
||||
} catch (err) {
|
||||
dbgTelemetry("send-error", { error: (err as Error)?.message || String(err) });
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
}
|
||||
|
||||
export function isTelemetryEnabled(): boolean {
|
||||
return process.env.VERCEL_PLUGIN_TELEMETRY === "on";
|
||||
if (process.env.VERCEL_PLUGIN_TELEMETRY === "on") return true;
|
||||
|
||||
// Fallback: read the preference file directly in case the env var
|
||||
// wasn't propagated to this process (new session, env file not sourced yet)
|
||||
try {
|
||||
const prefPath = join(homedir(), ".claude", "vercel-plugin-telemetry-preference");
|
||||
const pref = readFileSync(prefPath, "utf-8").trim();
|
||||
return pref === "enabled";
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function trackEvent(sessionId: string, key: string, value: string): Promise<void> {
|
||||
|
||||
+28
-3
@@ -1,5 +1,8 @@
|
||||
// hooks/src/telemetry.mts
|
||||
import { randomUUID } from "crypto";
|
||||
import { appendFileSync, readFileSync } from "fs";
|
||||
import { resolve, join } from "path";
|
||||
import { tmpdir, homedir } from "os";
|
||||
var MAX_VALUE_BYTES = 1e5;
|
||||
var TRUNCATION_SUFFIX = "[TRUNCATED]";
|
||||
var BRIDGE_ENDPOINT = "https://telemetry.vercel.com/api/vercel-plugin/v1/events";
|
||||
@@ -11,12 +14,25 @@ function truncateValue(value) {
|
||||
const truncated = Buffer.from(value, "utf-8").subarray(0, MAX_VALUE_BYTES).toString("utf-8");
|
||||
return truncated + TRUNCATION_SUFFIX;
|
||||
}
|
||||
var DEBUG = ["debug", "trace"].includes(process.env.VERCEL_PLUGIN_LOG_LEVEL || "") || process.env.VERCEL_PLUGIN_DEBUG === "1" || process.env.VERCEL_PLUGIN_HOOK_DEBUG === "1";
|
||||
var DBG_FILE = resolve(tmpdir(), "vercel-plugin-telemetry-debug.log");
|
||||
function dbgTelemetry(event, data) {
|
||||
if (!DEBUG) return;
|
||||
const line = JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), lib: "telemetry", event, ...data }) + "\n";
|
||||
process.stderr.write(line);
|
||||
try {
|
||||
appendFileSync(DBG_FILE, line);
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
async function send(sessionId, events) {
|
||||
if (events.length === 0) return;
|
||||
const bodyBytes = Buffer.byteLength(JSON.stringify(events), "utf-8");
|
||||
dbgTelemetry("send-start", { sessionId: sessionId.slice(0, 20), eventCount: events.length, bodyBytes, keys: events.map((e) => e.key) });
|
||||
const controller = new AbortController();
|
||||
const timeout = setTimeout(() => controller.abort(), FLUSH_TIMEOUT_MS);
|
||||
try {
|
||||
await fetch(BRIDGE_ENDPOINT, {
|
||||
const res = await fetch(BRIDGE_ENDPOINT, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
@@ -26,13 +42,22 @@ async function send(sessionId, events) {
|
||||
body: JSON.stringify(events),
|
||||
signal: controller.signal
|
||||
});
|
||||
} catch {
|
||||
dbgTelemetry("send-response", { status: res.status, ok: res.ok, statusText: res.statusText });
|
||||
} catch (err) {
|
||||
dbgTelemetry("send-error", { error: err?.message || String(err) });
|
||||
} finally {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
}
|
||||
function isTelemetryEnabled() {
|
||||
return process.env.VERCEL_PLUGIN_TELEMETRY === "on";
|
||||
if (process.env.VERCEL_PLUGIN_TELEMETRY === "on") return true;
|
||||
try {
|
||||
const prefPath = join(homedir(), ".claude", "vercel-plugin-telemetry-preference");
|
||||
const pref = readFileSync(prefPath, "utf-8").trim();
|
||||
return pref === "enabled";
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
async function trackEvent(sessionId, key, value) {
|
||||
if (!isTelemetryEnabled()) return;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"name": "vercel-plugin",
|
||||
"version": "0.2.1",
|
||||
"private": true,
|
||||
"bin": {
|
||||
"vercel-plugin": "src/cli/index.ts"
|
||||
|
||||
+2
-1
@@ -1,3 +1,4 @@
|
||||
{
|
||||
"outputDirectory": "generated"
|
||||
"outputDirectory": "generated",
|
||||
"cleanUrls": true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user