getpatter 0.6.4 → 0.6.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-7IIV3BY4.mjs → chunk-YJX2EKON.mjs} +658 -79
- package/dist/cli.js +492 -2
- package/dist/index.d.mts +607 -6
- package/dist/index.d.ts +607 -6
- package/dist/index.js +1839 -189
- package/dist/index.mjs +1114 -70
- package/dist/{test-mode-4QLLWYVV.mjs → test-mode-XFOADUNE.mjs} +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -785,9 +785,9 @@ function loadDashboardHtml() {
|
|
|
785
785
|
(0, import_node_path.join)(here, "dashboard", "ui.html"),
|
|
786
786
|
(0, import_node_path.join)(here, "..", "dashboard", "ui.html")
|
|
787
787
|
];
|
|
788
|
-
for (const
|
|
788
|
+
for (const path4 of candidates) {
|
|
789
789
|
try {
|
|
790
|
-
return (0, import_node_fs.readFileSync)(
|
|
790
|
+
return (0, import_node_fs.readFileSync)(path4, "utf8");
|
|
791
791
|
} catch {
|
|
792
792
|
}
|
|
793
793
|
}
|
|
@@ -987,7 +987,470 @@ function showBanner() {
|
|
|
987
987
|
console.log("\n" + BANNER);
|
|
988
988
|
}
|
|
989
989
|
|
|
990
|
+
// src/telemetry/env.ts
|
|
991
|
+
var CI_ENV_VARS = [
|
|
992
|
+
"CI",
|
|
993
|
+
"CONTINUOUS_INTEGRATION",
|
|
994
|
+
"GITHUB_ACTIONS",
|
|
995
|
+
"GITLAB_CI",
|
|
996
|
+
"TRAVIS",
|
|
997
|
+
"CIRCLECI",
|
|
998
|
+
"APPVEYOR",
|
|
999
|
+
"TF_BUILD",
|
|
1000
|
+
"TEAMCITY_VERSION",
|
|
1001
|
+
"BUILDKITE",
|
|
1002
|
+
"DRONE",
|
|
1003
|
+
"JENKINS_URL",
|
|
1004
|
+
"HUDSON_URL",
|
|
1005
|
+
"BAMBOO_BUILDKEY",
|
|
1006
|
+
"CODEBUILD_BUILD_ID"
|
|
1007
|
+
];
|
|
1008
|
+
var TEST_ENV_VARS = ["VITEST", "JEST_WORKER_ID"];
|
|
1009
|
+
function isTruthy(value) {
|
|
1010
|
+
if (value === void 0) return false;
|
|
1011
|
+
const v = value.trim().toLowerCase();
|
|
1012
|
+
return v !== "" && v !== "0" && v !== "false" && v !== "no" && v !== "off";
|
|
1013
|
+
}
|
|
1014
|
+
function isCi() {
|
|
1015
|
+
return CI_ENV_VARS.some((name) => isTruthy(process.env[name]));
|
|
1016
|
+
}
|
|
1017
|
+
function isTest() {
|
|
1018
|
+
if (TEST_ENV_VARS.some((name) => process.env[name] !== void 0)) return true;
|
|
1019
|
+
const node = (process.env.NODE_ENV ?? "").trim().toLowerCase();
|
|
1020
|
+
const patter = (process.env.PATTER_ENV ?? "").trim().toLowerCase();
|
|
1021
|
+
return node === "test" || patter === "test";
|
|
1022
|
+
}
|
|
1023
|
+
|
|
1024
|
+
// src/telemetry/install-id.ts
|
|
1025
|
+
var import_node_crypto2 = require("crypto");
|
|
1026
|
+
var fs3 = __toESM(require("fs"));
|
|
1027
|
+
var os = __toESM(require("os"));
|
|
1028
|
+
var path3 = __toESM(require("path"));
|
|
1029
|
+
var RUN_ID = (0, import_node_crypto2.randomUUID)().replace(/-/g, "");
|
|
1030
|
+
var HEX32 = /^[0-9a-f]{32}$/;
|
|
1031
|
+
var cachedInstallId = null;
|
|
1032
|
+
function runId() {
|
|
1033
|
+
return RUN_ID;
|
|
1034
|
+
}
|
|
1035
|
+
function statePath() {
|
|
1036
|
+
const base = process.env.PATTER_TELEMETRY_STATE_DIR || process.env.XDG_STATE_HOME;
|
|
1037
|
+
const root = base && base.length > 0 ? base : path3.join(os.homedir(), ".getpatter");
|
|
1038
|
+
return path3.join(root, "install-id");
|
|
1039
|
+
}
|
|
1040
|
+
function installId() {
|
|
1041
|
+
if (cachedInstallId !== null) return cachedInstallId;
|
|
1042
|
+
const p = statePath();
|
|
1043
|
+
try {
|
|
1044
|
+
const existing = fs3.readFileSync(p, "utf8").trim();
|
|
1045
|
+
if (HEX32.test(existing)) {
|
|
1046
|
+
cachedInstallId = existing;
|
|
1047
|
+
return cachedInstallId;
|
|
1048
|
+
}
|
|
1049
|
+
} catch {
|
|
1050
|
+
}
|
|
1051
|
+
const newId = (0, import_node_crypto2.randomUUID)().replace(/-/g, "");
|
|
1052
|
+
try {
|
|
1053
|
+
fs3.mkdirSync(path3.dirname(p), { recursive: true });
|
|
1054
|
+
fs3.writeFileSync(p, newId, "utf8");
|
|
1055
|
+
cachedInstallId = newId;
|
|
1056
|
+
} catch {
|
|
1057
|
+
cachedInstallId = RUN_ID;
|
|
1058
|
+
}
|
|
1059
|
+
return cachedInstallId;
|
|
1060
|
+
}
|
|
1061
|
+
function optOutPath() {
|
|
1062
|
+
return path3.join(path3.dirname(statePath()), "telemetry-disabled");
|
|
1063
|
+
}
|
|
1064
|
+
function isOptedOut() {
|
|
1065
|
+
try {
|
|
1066
|
+
return fs3.existsSync(optOutPath());
|
|
1067
|
+
} catch {
|
|
1068
|
+
return false;
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
function setOptOut(disabled) {
|
|
1072
|
+
const p = optOutPath();
|
|
1073
|
+
if (disabled) {
|
|
1074
|
+
fs3.mkdirSync(path3.dirname(p), { recursive: true });
|
|
1075
|
+
fs3.writeFileSync(p, "1", "utf8");
|
|
1076
|
+
} else {
|
|
1077
|
+
try {
|
|
1078
|
+
fs3.unlinkSync(p);
|
|
1079
|
+
} catch (err) {
|
|
1080
|
+
if (err.code !== "ENOENT") throw err;
|
|
1081
|
+
}
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
// src/telemetry/consent.ts
|
|
1086
|
+
function isEnabled(flag) {
|
|
1087
|
+
if (isTruthy(process.env.DO_NOT_TRACK)) return false;
|
|
1088
|
+
if (isTruthy(process.env.PATTER_TELEMETRY_DISABLED)) return false;
|
|
1089
|
+
if (isOptedOut()) return false;
|
|
1090
|
+
if (flag === false) return false;
|
|
1091
|
+
if (isCi() || isTest()) return false;
|
|
1092
|
+
return true;
|
|
1093
|
+
}
|
|
1094
|
+
|
|
1095
|
+
// src/telemetry/events.ts
|
|
1096
|
+
var os2 = __toESM(require("os"));
|
|
1097
|
+
|
|
1098
|
+
// src/telemetry/stack.ts
|
|
1099
|
+
var STACK_VENDORS = /* @__PURE__ */ new Set([
|
|
1100
|
+
"openai",
|
|
1101
|
+
"anthropic",
|
|
1102
|
+
"google",
|
|
1103
|
+
"cerebras",
|
|
1104
|
+
"groq",
|
|
1105
|
+
"deepgram",
|
|
1106
|
+
"elevenlabs",
|
|
1107
|
+
"cartesia",
|
|
1108
|
+
"whisper",
|
|
1109
|
+
"soniox",
|
|
1110
|
+
"assemblyai",
|
|
1111
|
+
"speechmatics",
|
|
1112
|
+
"lmnt",
|
|
1113
|
+
"rime",
|
|
1114
|
+
"inworld",
|
|
1115
|
+
"telnyx",
|
|
1116
|
+
"other"
|
|
1117
|
+
]);
|
|
1118
|
+
|
|
1119
|
+
// src/telemetry/events.ts
|
|
1120
|
+
var SCHEMA_VERSION = 5;
|
|
1121
|
+
var EVENT_SDK_INITIALIZED = "sdk_initialized";
|
|
1122
|
+
var EVENT_FIRST_RUN = "first_run";
|
|
1123
|
+
var EVENT_CLI_COMMAND = "cli_command";
|
|
1124
|
+
var EVENT_FEATURE_USED = "feature_used";
|
|
1125
|
+
var EVENT_AGENT_CONFIGURED = "agent_configured";
|
|
1126
|
+
var EVENT_CALL_STARTED = "call_started";
|
|
1127
|
+
var EVENT_CALL_COMPLETED = "call_completed";
|
|
1128
|
+
var ALLOWED_EVENTS = /* @__PURE__ */ new Set([
|
|
1129
|
+
EVENT_SDK_INITIALIZED,
|
|
1130
|
+
EVENT_FIRST_RUN,
|
|
1131
|
+
EVENT_CLI_COMMAND,
|
|
1132
|
+
EVENT_FEATURE_USED,
|
|
1133
|
+
EVENT_AGENT_CONFIGURED,
|
|
1134
|
+
EVENT_CALL_STARTED,
|
|
1135
|
+
EVENT_CALL_COMPLETED
|
|
1136
|
+
]);
|
|
1137
|
+
var DIMENSION_VALUES = {
|
|
1138
|
+
carrier: /* @__PURE__ */ new Set(["twilio", "telnyx", "plivo", "none"]),
|
|
1139
|
+
tunnel: /* @__PURE__ */ new Set(["static", "configured", "none"]),
|
|
1140
|
+
engine: /* @__PURE__ */ new Set(["realtime", "convai", "pipeline"]),
|
|
1141
|
+
provider: /* @__PURE__ */ new Set([
|
|
1142
|
+
"openai",
|
|
1143
|
+
"elevenlabs",
|
|
1144
|
+
"deepgram",
|
|
1145
|
+
"cartesia",
|
|
1146
|
+
"cerebras",
|
|
1147
|
+
"anthropic",
|
|
1148
|
+
"google",
|
|
1149
|
+
"whisper",
|
|
1150
|
+
"other"
|
|
1151
|
+
]),
|
|
1152
|
+
// agent_configured dimensions
|
|
1153
|
+
custom_tool_count_bucket: /* @__PURE__ */ new Set(["0", "1", "2_3", "4_6", "7_12", "13_plus"]),
|
|
1154
|
+
integration: /* @__PURE__ */ new Set(["openclaw", "mcp", "hermes", "other", "none"]),
|
|
1155
|
+
integration_kind: /* @__PURE__ */ new Set(["consult", "mcp", "none"]),
|
|
1156
|
+
mcp_server_count_bucket: /* @__PURE__ */ new Set(["0", "1", "2_3", "4_plus"]),
|
|
1157
|
+
// call_started / call_completed: inbound vs outbound — a core usage split.
|
|
1158
|
+
direction: /* @__PURE__ */ new Set(["inbound", "outbound", "none"]),
|
|
1159
|
+
// cli_command: which CLI subcommand was invoked (never args/flags values).
|
|
1160
|
+
cli_command: /* @__PURE__ */ new Set(["dashboard", "eval", "telemetry", "none", "other"]),
|
|
1161
|
+
// call_completed: the call's terminal outcome
|
|
1162
|
+
outcome: /* @__PURE__ */ new Set(["completed", "error", "no_answer", "busy", "failed"]),
|
|
1163
|
+
// call_completed: terminal error code (mirrors ErrorCode, plus "other"). Never
|
|
1164
|
+
// the error message.
|
|
1165
|
+
error_code: /* @__PURE__ */ new Set([
|
|
1166
|
+
"config",
|
|
1167
|
+
"connection",
|
|
1168
|
+
"auth",
|
|
1169
|
+
"timeout",
|
|
1170
|
+
"rate_limit",
|
|
1171
|
+
"webhook_verification",
|
|
1172
|
+
"input_validation",
|
|
1173
|
+
"provider_error",
|
|
1174
|
+
"provision",
|
|
1175
|
+
"internal",
|
|
1176
|
+
"other"
|
|
1177
|
+
]),
|
|
1178
|
+
// feature_used (pipeline): per-layer vendor of the composed stack. A
|
|
1179
|
+
// providerKey not on the closed allowlist collapses to "other"; an absent layer
|
|
1180
|
+
// is omitted (the value set keeps "none" only as a safety token).
|
|
1181
|
+
stt_provider: /* @__PURE__ */ new Set([...STACK_VENDORS, "none"]),
|
|
1182
|
+
tts_provider: /* @__PURE__ */ new Set([...STACK_VENDORS, "none"]),
|
|
1183
|
+
llm_provider: /* @__PURE__ */ new Set([...STACK_VENDORS, "none"]),
|
|
1184
|
+
// sdk_initialized: anonymous deploy-shape (presence-only env/file probes).
|
|
1185
|
+
invoked_by_agent: /* @__PURE__ */ new Set(["claude", "cursor", "copilot", "gemini", "windsurf", "other", "none"]),
|
|
1186
|
+
serverless: /* @__PURE__ */ new Set(["lambda", "cloud_run", "vercel", "azure_functions", "none"]),
|
|
1187
|
+
cloud: /* @__PURE__ */ new Set(["aws", "gcp", "azure", "fly", "none"]),
|
|
1188
|
+
package_manager: /* @__PURE__ */ new Set(["npm", "pnpm", "yarn", "bun", "pip", "uv", "poetry", "pipenv", "conda", "none"]),
|
|
1189
|
+
days_since_install_bucket: /* @__PURE__ */ new Set(["0", "1_7", "8_30", "30_plus"]),
|
|
1190
|
+
// agent_configured: feature-adoption (Realtime tuning).
|
|
1191
|
+
noise_reduction: /* @__PURE__ */ new Set(["near_field", "far_field", "none"]),
|
|
1192
|
+
turn_detection: /* @__PURE__ */ new Set(["default", "custom", "none"]),
|
|
1193
|
+
// call_completed: how many conversational turns the call had.
|
|
1194
|
+
turn_count_bucket: /* @__PURE__ */ new Set(["0", "1", "2_3", "4_6", "7_12", "13_plus"])
|
|
1195
|
+
};
|
|
1196
|
+
var NUMERIC_DIMENSIONS = /* @__PURE__ */ new Set([
|
|
1197
|
+
"builtin_tool_count",
|
|
1198
|
+
"latency_ms",
|
|
1199
|
+
"duration_seconds",
|
|
1200
|
+
"cost_usd"
|
|
1201
|
+
]);
|
|
1202
|
+
var STRING_DIMENSIONS = /* @__PURE__ */ new Set([
|
|
1203
|
+
"stt_model",
|
|
1204
|
+
"tts_model",
|
|
1205
|
+
"llm_model",
|
|
1206
|
+
"previous_sdk_version"
|
|
1207
|
+
]);
|
|
1208
|
+
var MODEL_TOKEN_RE = /^[a-z0-9][a-z0-9.-]{0,40}$/;
|
|
1209
|
+
var BOOL_DIMENSIONS = /* @__PURE__ */ new Set([
|
|
1210
|
+
"container",
|
|
1211
|
+
"preambles_used",
|
|
1212
|
+
"per_tool_timeouts_set",
|
|
1213
|
+
"llm_fallback_configured"
|
|
1214
|
+
]);
|
|
1215
|
+
var ALLOWED_DIMENSIONS = /* @__PURE__ */ new Set([
|
|
1216
|
+
...Object.keys(DIMENSION_VALUES),
|
|
1217
|
+
...NUMERIC_DIMENSIONS,
|
|
1218
|
+
...STRING_DIMENSIONS,
|
|
1219
|
+
...BOOL_DIMENSIONS
|
|
1220
|
+
]);
|
|
1221
|
+
function osFamily() {
|
|
1222
|
+
const p = os2.platform();
|
|
1223
|
+
if (p === "win32") return "windows";
|
|
1224
|
+
return p || "unknown";
|
|
1225
|
+
}
|
|
1226
|
+
function arch2() {
|
|
1227
|
+
const a = os2.arch();
|
|
1228
|
+
if (a === "x64") return "x86_64";
|
|
1229
|
+
if (a === "arm64") return "arm64";
|
|
1230
|
+
return "other";
|
|
1231
|
+
}
|
|
1232
|
+
function runtimeVersion() {
|
|
1233
|
+
const parts = (process.versions.node ?? "0.0").split(".");
|
|
1234
|
+
return `${parts[0] ?? "0"}.${parts[1] ?? "0"}`;
|
|
1235
|
+
}
|
|
1236
|
+
function buildEvent(name, opts) {
|
|
1237
|
+
if (!ALLOWED_EVENTS.has(name)) {
|
|
1238
|
+
throw new Error(`unknown telemetry event: ${name}`);
|
|
1239
|
+
}
|
|
1240
|
+
const event = {
|
|
1241
|
+
event: name,
|
|
1242
|
+
schema_version: SCHEMA_VERSION,
|
|
1243
|
+
run_id: runId(),
|
|
1244
|
+
install_id: installId(),
|
|
1245
|
+
sdk: "typescript",
|
|
1246
|
+
sdk_version: opts.sdkVersion,
|
|
1247
|
+
os: osFamily(),
|
|
1248
|
+
arch: arch2(),
|
|
1249
|
+
runtime: "node",
|
|
1250
|
+
runtime_version: runtimeVersion(),
|
|
1251
|
+
ci: isCi() || isTest()
|
|
1252
|
+
};
|
|
1253
|
+
for (const [key, raw] of Object.entries(opts.dimensions ?? {})) {
|
|
1254
|
+
if (!ALLOWED_DIMENSIONS.has(key) || raw === null || raw === void 0) {
|
|
1255
|
+
continue;
|
|
1256
|
+
}
|
|
1257
|
+
let value = raw;
|
|
1258
|
+
const allowed = DIMENSION_VALUES[key];
|
|
1259
|
+
if (allowed && !(typeof value === "string" && allowed.has(value))) {
|
|
1260
|
+
value = "other";
|
|
1261
|
+
} else if (STRING_DIMENSIONS.has(key)) {
|
|
1262
|
+
if (!(typeof value === "string" && MODEL_TOKEN_RE.test(value))) {
|
|
1263
|
+
continue;
|
|
1264
|
+
}
|
|
1265
|
+
} else if (BOOL_DIMENSIONS.has(key) && typeof value !== "boolean") {
|
|
1266
|
+
continue;
|
|
1267
|
+
}
|
|
1268
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
1269
|
+
event[key] = value;
|
|
1270
|
+
}
|
|
1271
|
+
}
|
|
1272
|
+
return event;
|
|
1273
|
+
}
|
|
1274
|
+
|
|
1275
|
+
// src/telemetry/client.ts
|
|
1276
|
+
var DEFAULT_ENDPOINT = "https://telemetry.getpatter.com/v1/ingest";
|
|
1277
|
+
var TIMEOUT_MS = 3e3;
|
|
1278
|
+
var BUFFER_MAX = 256;
|
|
1279
|
+
var noticeShown = false;
|
|
1280
|
+
var liveClients = /* @__PURE__ */ new Set();
|
|
1281
|
+
var exitHookRegistered = false;
|
|
1282
|
+
function showNoticeOnce() {
|
|
1283
|
+
if (noticeShown) return;
|
|
1284
|
+
noticeShown = true;
|
|
1285
|
+
getLogger().info(
|
|
1286
|
+
"Anonymous usage telemetry is on (no PII, no call content). Collected: a random anonymous install id, SDK version, language, OS family, runtime version, coarse feature flags, the composed stack (provider + model per layer), tool counts, integration category, and per-call duration, latency, cost, and error codes (no call content, no message text). Disable with PATTER_TELEMETRY_DISABLED=1, DO_NOT_TRACK=1, or telemetry: false. Details: https://docs.getpatter.com/telemetry"
|
|
1287
|
+
);
|
|
1288
|
+
}
|
|
1289
|
+
function registerExitHook() {
|
|
1290
|
+
if (exitHookRegistered) return;
|
|
1291
|
+
exitHookRegistered = true;
|
|
1292
|
+
process.once("beforeExit", () => {
|
|
1293
|
+
for (const ref of [...liveClients]) {
|
|
1294
|
+
const client = ref.deref();
|
|
1295
|
+
if (client) void client.close();
|
|
1296
|
+
else liveClients.delete(ref);
|
|
1297
|
+
}
|
|
1298
|
+
});
|
|
1299
|
+
}
|
|
1300
|
+
var TelemetryClient = class {
|
|
1301
|
+
sdkVersion;
|
|
1302
|
+
enabledFlag;
|
|
1303
|
+
endpoint;
|
|
1304
|
+
debug;
|
|
1305
|
+
buffer = [];
|
|
1306
|
+
flushing = false;
|
|
1307
|
+
closed = false;
|
|
1308
|
+
selfRef = new WeakRef(this);
|
|
1309
|
+
constructor(options) {
|
|
1310
|
+
this.sdkVersion = options.sdkVersion;
|
|
1311
|
+
this.enabledFlag = isEnabled(options.flag);
|
|
1312
|
+
this.endpoint = options.endpoint ?? process.env.PATTER_TELEMETRY_ENDPOINT ?? DEFAULT_ENDPOINT;
|
|
1313
|
+
this.debug = isTruthy(process.env.PATTER_TELEMETRY_DEBUG);
|
|
1314
|
+
if (this.enabledFlag && !this.debug) {
|
|
1315
|
+
showNoticeOnce();
|
|
1316
|
+
registerExitHook();
|
|
1317
|
+
liveClients.add(this.selfRef);
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
get enabled() {
|
|
1321
|
+
return this.enabledFlag;
|
|
1322
|
+
}
|
|
1323
|
+
/** Enqueue an event. Fire-and-forget; never throws, never blocks. */
|
|
1324
|
+
record(name, dimensions) {
|
|
1325
|
+
if (!this.enabledFlag || this.closed) return;
|
|
1326
|
+
let event;
|
|
1327
|
+
try {
|
|
1328
|
+
event = buildEvent(name, { sdkVersion: this.sdkVersion, dimensions });
|
|
1329
|
+
} catch (err) {
|
|
1330
|
+
getLogger().debug("telemetry buildEvent failed", err);
|
|
1331
|
+
return;
|
|
1332
|
+
}
|
|
1333
|
+
if (this.debug) {
|
|
1334
|
+
try {
|
|
1335
|
+
process.stderr.write(`[patter telemetry] ${JSON.stringify(event)}
|
|
1336
|
+
`);
|
|
1337
|
+
} catch {
|
|
1338
|
+
}
|
|
1339
|
+
return;
|
|
1340
|
+
}
|
|
1341
|
+
try {
|
|
1342
|
+
if (this.buffer.length >= BUFFER_MAX) this.buffer.shift();
|
|
1343
|
+
this.buffer.push(event);
|
|
1344
|
+
this.scheduleFlush();
|
|
1345
|
+
} catch (err) {
|
|
1346
|
+
getLogger().debug("telemetry enqueue failed", err);
|
|
1347
|
+
}
|
|
1348
|
+
}
|
|
1349
|
+
/**
|
|
1350
|
+
* Schedule a flush of any buffered events. Events recorded before the server
|
|
1351
|
+
* is running (e.g. at `new Patter(...)`) sit in the buffer; call this once the
|
|
1352
|
+
* server is up so they ship promptly. Cheap when disabled or buffer is empty.
|
|
1353
|
+
*/
|
|
1354
|
+
flushPending() {
|
|
1355
|
+
if (!this.enabledFlag || this.debug) return;
|
|
1356
|
+
try {
|
|
1357
|
+
this.scheduleFlush();
|
|
1358
|
+
} catch (err) {
|
|
1359
|
+
getLogger().debug("telemetry flushPending failed", err);
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
/** Flush remaining events (graceful shutdown). Never throws. */
|
|
1363
|
+
async close() {
|
|
1364
|
+
if (this.closed) return;
|
|
1365
|
+
this.closed = true;
|
|
1366
|
+
liveClients.delete(this.selfRef);
|
|
1367
|
+
if (!this.enabledFlag || this.debug) return;
|
|
1368
|
+
try {
|
|
1369
|
+
await this.flush();
|
|
1370
|
+
} catch (err) {
|
|
1371
|
+
getLogger().debug("telemetry close flush failed", err);
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
scheduleFlush() {
|
|
1375
|
+
if (this.flushing) return;
|
|
1376
|
+
this.flushing = true;
|
|
1377
|
+
void this.flush().finally(() => {
|
|
1378
|
+
this.flushing = false;
|
|
1379
|
+
});
|
|
1380
|
+
}
|
|
1381
|
+
async flush() {
|
|
1382
|
+
if (this.buffer.length === 0) return;
|
|
1383
|
+
const events = this.buffer.splice(0, this.buffer.length);
|
|
1384
|
+
const controller = new AbortController();
|
|
1385
|
+
const timer = setTimeout(() => controller.abort(), TIMEOUT_MS);
|
|
1386
|
+
timer.unref?.();
|
|
1387
|
+
try {
|
|
1388
|
+
await fetch(this.endpoint, {
|
|
1389
|
+
method: "POST",
|
|
1390
|
+
headers: { "content-type": "application/json" },
|
|
1391
|
+
body: JSON.stringify(events),
|
|
1392
|
+
signal: controller.signal
|
|
1393
|
+
});
|
|
1394
|
+
} catch (err) {
|
|
1395
|
+
getLogger().debug("telemetry flush failed", err);
|
|
1396
|
+
} finally {
|
|
1397
|
+
clearTimeout(timer);
|
|
1398
|
+
}
|
|
1399
|
+
}
|
|
1400
|
+
};
|
|
1401
|
+
|
|
990
1402
|
// src/cli.ts
|
|
1403
|
+
async function emitCliCommand(command) {
|
|
1404
|
+
try {
|
|
1405
|
+
const client = new TelemetryClient({ sdkVersion: VERSION });
|
|
1406
|
+
client.record("cli_command", { cli_command: command });
|
|
1407
|
+
const timeout = new Promise((resolve2) => {
|
|
1408
|
+
const t = setTimeout(resolve2, 600);
|
|
1409
|
+
t.unref?.();
|
|
1410
|
+
});
|
|
1411
|
+
await Promise.race([client.close(), timeout]);
|
|
1412
|
+
} catch {
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
function runTelemetryCommand(action) {
|
|
1416
|
+
const act = action ?? "status";
|
|
1417
|
+
if (act === "disable") {
|
|
1418
|
+
try {
|
|
1419
|
+
setOptOut(true);
|
|
1420
|
+
} catch (err) {
|
|
1421
|
+
console.log(`Could not write the opt-out marker: ${String(err)}`);
|
|
1422
|
+
return 1;
|
|
1423
|
+
}
|
|
1424
|
+
console.log("Anonymous telemetry disabled. No usage data will be sent.");
|
|
1425
|
+
return 0;
|
|
1426
|
+
}
|
|
1427
|
+
if (act === "enable") {
|
|
1428
|
+
try {
|
|
1429
|
+
setOptOut(false);
|
|
1430
|
+
} catch (err) {
|
|
1431
|
+
console.log(`Could not remove the opt-out marker: ${String(err)}`);
|
|
1432
|
+
return 1;
|
|
1433
|
+
}
|
|
1434
|
+
console.log("Anonymous telemetry re-enabled (opt-out model, on by default).");
|
|
1435
|
+
return 0;
|
|
1436
|
+
}
|
|
1437
|
+
if (act !== "status") {
|
|
1438
|
+
console.log("Usage: getpatter telemetry [status|disable|enable]");
|
|
1439
|
+
return 1;
|
|
1440
|
+
}
|
|
1441
|
+
const endpoint = process.env.PATTER_TELEMETRY_ENDPOINT || DEFAULT_ENDPOINT;
|
|
1442
|
+
console.log(`Anonymous usage telemetry: ${isEnabled() ? "ENABLED" : "DISABLED"}`);
|
|
1443
|
+
if (isOptedOut()) {
|
|
1444
|
+
console.log(" Opted out via: getpatter telemetry disable (persisted marker)");
|
|
1445
|
+
}
|
|
1446
|
+
console.log(` Endpoint: ${endpoint}`);
|
|
1447
|
+
console.log(" Inspect what would be sent (prints, sends nothing): PATTER_TELEMETRY_DEBUG=1");
|
|
1448
|
+
console.log(
|
|
1449
|
+
" Disable: getpatter telemetry disable | DO_NOT_TRACK=1 | PATTER_TELEMETRY_DISABLED=1"
|
|
1450
|
+
);
|
|
1451
|
+
console.log(" Details: https://docs.getpatter.com/telemetry");
|
|
1452
|
+
return 0;
|
|
1453
|
+
}
|
|
991
1454
|
function parseArgs(argv) {
|
|
992
1455
|
const args = argv.slice(2);
|
|
993
1456
|
let port = 8e3;
|
|
@@ -1008,17 +1471,44 @@ function printEvalStub() {
|
|
|
1008
1471
|
"Evaluations are not yet available in the TypeScript SDK.\nUse the Python SDK instead:\n\n pip install getpatter\n patter eval --help\n\nSee https://github.com/PatterAI/Patter for docs."
|
|
1009
1472
|
);
|
|
1010
1473
|
}
|
|
1474
|
+
function printHermesStub() {
|
|
1475
|
+
console.log(
|
|
1476
|
+
"The Hermes wizard (doctor / setup / test / trace / diagnose /\nattach-number) lives in the Python CLI today. Use it from the Python SDK:\n\n pip install getpatter\n patter hermes doctor\n patter hermes setup\n patter hermes test\n\nThe HermesLLM provider itself is fully available in this TypeScript SDK\n(import { HermesLLM } from 'getpatter'). See\nhttps://docs.getpatter.com/integrations/hermes for docs."
|
|
1477
|
+
);
|
|
1478
|
+
}
|
|
1479
|
+
function printOpenClawStub() {
|
|
1480
|
+
console.log(
|
|
1481
|
+
"The OpenClaw wizard (doctor / setup / test / call / agents /\nattach-number) lives in the Python CLI today. Use it from the Python SDK:\n\n pip install getpatter\n patter openclaw setup --enable-openclaw --agent receptionist # inbound\n patter openclaw setup --mode outbound --agent sales # outbound\n patter openclaw doctor\n\nThe OpenClawLLM provider itself is fully available in this TypeScript SDK\n(import { OpenClawLLM } from 'getpatter'). See\nhttps://docs.getpatter.com/integrations/openclaw for docs."
|
|
1482
|
+
);
|
|
1483
|
+
}
|
|
1011
1484
|
async function main() {
|
|
1012
1485
|
const command = process.argv[2];
|
|
1486
|
+
if (command === "telemetry") {
|
|
1487
|
+
process.exit(runTelemetryCommand(process.argv[3]));
|
|
1488
|
+
}
|
|
1013
1489
|
if (command === "eval") {
|
|
1490
|
+
await emitCliCommand("eval");
|
|
1014
1491
|
printEvalStub();
|
|
1015
1492
|
process.exit(0);
|
|
1016
1493
|
}
|
|
1494
|
+
if (command === "hermes") {
|
|
1495
|
+
printHermesStub();
|
|
1496
|
+
process.exit(0);
|
|
1497
|
+
}
|
|
1498
|
+
if (command === "openclaw") {
|
|
1499
|
+
printOpenClawStub();
|
|
1500
|
+
process.exit(0);
|
|
1501
|
+
}
|
|
1017
1502
|
if (command !== "dashboard") {
|
|
1503
|
+
await emitCliCommand(command ? "other" : "none");
|
|
1018
1504
|
console.log("Usage: getpatter dashboard [--port 8000]");
|
|
1019
1505
|
console.log(" getpatter eval (stub \u2014 use Python SDK for evals)");
|
|
1506
|
+
console.log(" getpatter hermes (stub \u2014 use Python SDK for the wizard)");
|
|
1507
|
+
console.log(" getpatter openclaw (stub \u2014 use Python SDK for the wizard)");
|
|
1508
|
+
console.log(" getpatter telemetry [status|disable|enable]");
|
|
1020
1509
|
process.exit(command ? 1 : 0);
|
|
1021
1510
|
}
|
|
1511
|
+
await emitCliCommand("dashboard");
|
|
1022
1512
|
const { port } = parseArgs(process.argv);
|
|
1023
1513
|
showBanner();
|
|
1024
1514
|
const store = new MetricsStore();
|