@getargvio/argvio 0.1.0 → 0.5.0
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/README.md +32 -6
- package/dist/index.cjs +529 -198
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +249 -48
- package/dist/index.d.ts +249 -48
- package/dist/index.js +500 -196
- package/dist/index.js.map +1 -1
- package/package.json +8 -9
package/dist/index.cjs
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
var api = require('@opentelemetry/api');
|
|
3
4
|
var apiLogs = require('@opentelemetry/api-logs');
|
|
4
5
|
var sdkLogs = require('@opentelemetry/sdk-logs');
|
|
5
6
|
var sdkMetrics = require('@opentelemetry/sdk-metrics');
|
|
6
7
|
var sdkTraceNode = require('@opentelemetry/sdk-trace-node');
|
|
8
|
+
var os = require('os');
|
|
9
|
+
var path = require('path');
|
|
10
|
+
var fs = require('fs');
|
|
7
11
|
var grpcJs = require('@grpc/grpc-js');
|
|
8
12
|
var exporterTraceOtlpHttp = require('@opentelemetry/exporter-trace-otlp-http');
|
|
9
13
|
var exporterTraceOtlpGrpc = require('@opentelemetry/exporter-trace-otlp-grpc');
|
|
@@ -11,23 +15,23 @@ var exporterMetricsOtlpHttp = require('@opentelemetry/exporter-metrics-otlp-http
|
|
|
11
15
|
var exporterMetricsOtlpGrpc = require('@opentelemetry/exporter-metrics-otlp-grpc');
|
|
12
16
|
var exporterLogsOtlpHttp = require('@opentelemetry/exporter-logs-otlp-http');
|
|
13
17
|
var exporterLogsOtlpGrpc = require('@opentelemetry/exporter-logs-otlp-grpc');
|
|
14
|
-
var fs = require('fs');
|
|
15
18
|
var resources = require('@opentelemetry/resources');
|
|
16
19
|
var semanticConventions = require('@opentelemetry/semantic-conventions');
|
|
17
20
|
var crypto = require('crypto');
|
|
18
|
-
var os = require('os');
|
|
19
|
-
var path = require('path');
|
|
20
21
|
|
|
21
22
|
var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
|
|
22
23
|
// src/client.ts
|
|
23
24
|
|
|
24
25
|
// src/tier.ts
|
|
25
26
|
var Tier = {
|
|
26
|
-
/**
|
|
27
|
+
/**
|
|
28
|
+
* No persistent or cross-invocation identifiers: command name, exit
|
|
29
|
+
* code, duration, CLI version, OS/arch and CI environment only.
|
|
30
|
+
*/
|
|
27
31
|
Anonymous: 0,
|
|
28
|
-
/** Adds
|
|
32
|
+
/** Adds an opaque per-process session ID, an opaque per-install ID, flag names (never values), and classified (not raw) error types. */
|
|
29
33
|
Basic: 1,
|
|
30
|
-
/**
|
|
34
|
+
/** Reserved: the server allowlist permits some fields here, but this SDK sends nothing beyond `Tier.Basic`. */
|
|
31
35
|
Full: 2,
|
|
32
36
|
/**
|
|
33
37
|
* The explicit opt-in tier: raw error detail (stack traces, messages)
|
|
@@ -41,7 +45,7 @@ var TIER_STRINGS = {
|
|
|
41
45
|
[Tier.Anonymous]: "anonymous",
|
|
42
46
|
[Tier.Basic]: "basic",
|
|
43
47
|
[Tier.Full]: "full",
|
|
44
|
-
[Tier.OptIn]: "
|
|
48
|
+
[Tier.OptIn]: "optin_plus"
|
|
45
49
|
};
|
|
46
50
|
function tierToString(tier) {
|
|
47
51
|
return TIER_STRINGS[tier] ?? "unknown";
|
|
@@ -54,6 +58,7 @@ function parseTier(value) {
|
|
|
54
58
|
return Tier.Basic;
|
|
55
59
|
case "full":
|
|
56
60
|
return Tier.Full;
|
|
61
|
+
case "optin_plus":
|
|
57
62
|
case "opt-in":
|
|
58
63
|
return Tier.OptIn;
|
|
59
64
|
default:
|
|
@@ -85,6 +90,72 @@ function isValidErrorCategory(value) {
|
|
|
85
90
|
return ERROR_CATEGORY_VALUES.has(value);
|
|
86
91
|
}
|
|
87
92
|
|
|
93
|
+
// src/errors.ts
|
|
94
|
+
var ArgvioError = class extends Error {
|
|
95
|
+
constructor(message) {
|
|
96
|
+
super(`argvio: ${message}`);
|
|
97
|
+
this.name = "ArgvioError";
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
var errNoProviderResolved = new ArgvioError("no consent provider resolved a tier");
|
|
101
|
+
var errNoConsentFile = new ArgvioError("no consent config file path resolved");
|
|
102
|
+
var errUnknownTierValue = new ArgvioError("unknown tier value in consent config");
|
|
103
|
+
|
|
104
|
+
// src/consentFile.ts
|
|
105
|
+
function userConfigDir() {
|
|
106
|
+
switch (process.platform) {
|
|
107
|
+
case "win32":
|
|
108
|
+
return process.env["APPDATA"];
|
|
109
|
+
case "darwin":
|
|
110
|
+
return path.join(os.homedir(), "Library", "Application Support");
|
|
111
|
+
default:
|
|
112
|
+
return process.env["XDG_CONFIG_HOME"] || path.join(os.homedir(), ".config");
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
function defaultInstallIdPath(cliName) {
|
|
116
|
+
const consentPath = defaultConsentPath(cliName);
|
|
117
|
+
return consentPath ? path.join(path.dirname(consentPath), "argvio-install-id") : void 0;
|
|
118
|
+
}
|
|
119
|
+
function defaultConsentPath(cliName) {
|
|
120
|
+
const dir = userConfigDir();
|
|
121
|
+
if (!dir) return void 0;
|
|
122
|
+
return path.join(dir, cliName || "argvio", "argvio-consent.json");
|
|
123
|
+
}
|
|
124
|
+
var FileConsentProvider = class {
|
|
125
|
+
/** The resolved config file path (after defaults/overrides), primarily for diagnostics and tests. */
|
|
126
|
+
path;
|
|
127
|
+
constructor(cliName, options = {}) {
|
|
128
|
+
this.path = options.path || defaultConsentPath(cliName);
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* A missing file, unreadable file, or malformed contents all throw
|
|
132
|
+
* synchronously, so callers should place `FileConsentProvider` inside
|
|
133
|
+
* `chainProviders` with a sane fallback (e.g.
|
|
134
|
+
* `new StaticProvider(Tier.Anonymous)`).
|
|
135
|
+
*/
|
|
136
|
+
resolve() {
|
|
137
|
+
if (!this.path) throw errNoConsentFile;
|
|
138
|
+
const data = fs.readFileSync(this.path, "utf8");
|
|
139
|
+
const parsed = JSON.parse(data);
|
|
140
|
+
const tier = parseTier(parsed.tier ?? "");
|
|
141
|
+
if (tier === void 0) throw errUnknownTierValue;
|
|
142
|
+
return tier;
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Persists `tier` as the chosen consent tier, creating the parent
|
|
146
|
+
* directory if needed. Intended for use by a vendor's first-run
|
|
147
|
+
* consent prompt. Does not itself enforce any tier ceiling — it's the
|
|
148
|
+
* vendor's own UX writing the user's explicit choice.
|
|
149
|
+
*/
|
|
150
|
+
store(tier) {
|
|
151
|
+
if (!this.path) throw errNoConsentFile;
|
|
152
|
+
if (!isValidTier(tier)) throw errUnknownTierValue;
|
|
153
|
+
fs.mkdirSync(path.dirname(this.path), { recursive: true, mode: 448 });
|
|
154
|
+
const contents = { tier: tierToString(tier) };
|
|
155
|
+
fs.writeFileSync(this.path, JSON.stringify(contents), { mode: 384 });
|
|
156
|
+
}
|
|
157
|
+
};
|
|
158
|
+
|
|
88
159
|
// src/options.ts
|
|
89
160
|
var DEFAULT_ENDPOINT_HTTP = "https://ingest.argvio.io:4318";
|
|
90
161
|
var DEFAULT_ENDPOINT_GRPC = "ingest.argvio.io:4317";
|
|
@@ -92,6 +163,7 @@ var DEFAULT_AUTH_HEADER = "x-argvio-api-key";
|
|
|
92
163
|
var DEFAULT_EXPORT_TIMEOUT_MS = 1e4;
|
|
93
164
|
var DEFAULT_SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
94
165
|
var DEFAULT_QUEUE_SIZE = 2048;
|
|
166
|
+
var MAX_INSTALL_METHOD_LENGTH = 32;
|
|
95
167
|
function resolveConfig(apiKey, cliName, cliVersion, options = {}) {
|
|
96
168
|
const transport = options.transport ?? "http";
|
|
97
169
|
return {
|
|
@@ -108,7 +180,9 @@ function resolveConfig(apiKey, cliName, cliVersion, options = {}) {
|
|
|
108
180
|
exportTimeoutMs: options.exportTimeoutMs && options.exportTimeoutMs > 0 ? options.exportTimeoutMs : DEFAULT_EXPORT_TIMEOUT_MS,
|
|
109
181
|
shutdownTimeoutMs: options.shutdownTimeoutMs && options.shutdownTimeoutMs > 0 ? options.shutdownTimeoutMs : DEFAULT_SHUTDOWN_TIMEOUT_MS,
|
|
110
182
|
queueSize: options.queueSize && options.queueSize > 0 ? options.queueSize : DEFAULT_QUEUE_SIZE,
|
|
111
|
-
resourceAttributes: options.resourceAttributes ?? {}
|
|
183
|
+
resourceAttributes: options.resourceAttributes ?? {},
|
|
184
|
+
installIdPath: options.installIdPath !== void 0 ? options.installIdPath || void 0 : defaultInstallIdPath(cliName),
|
|
185
|
+
installMethod: options.installMethod?.slice(0, MAX_INSTALL_METHOD_LENGTH) || void 0
|
|
112
186
|
};
|
|
113
187
|
}
|
|
114
188
|
var SCHEME_RE = /^[a-z][a-z0-9+.-]*:\/\//i;
|
|
@@ -156,37 +230,6 @@ function buildExporters(cfg) {
|
|
|
156
230
|
})
|
|
157
231
|
};
|
|
158
232
|
}
|
|
159
|
-
var SDK_NAME = "@getargvio/argvio";
|
|
160
|
-
function sdkVersion() {
|
|
161
|
-
try {
|
|
162
|
-
const pkgUrl = new URL("../package.json", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
|
|
163
|
-
const pkg = JSON.parse(fs.readFileSync(pkgUrl, "utf8"));
|
|
164
|
-
return pkg.version ?? "unknown";
|
|
165
|
-
} catch {
|
|
166
|
-
return "unknown";
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
function buildResource(cfg) {
|
|
170
|
-
return resources.resourceFromAttributes({
|
|
171
|
-
[semanticConventions.ATTR_SERVICE_NAME]: cfg.cliName,
|
|
172
|
-
[semanticConventions.ATTR_SERVICE_VERSION]: cfg.cliVersion,
|
|
173
|
-
"telemetry.sdk.name": SDK_NAME,
|
|
174
|
-
"telemetry.sdk.version": sdkVersion(),
|
|
175
|
-
"telemetry.sdk.language": "nodejs",
|
|
176
|
-
...cfg.resourceAttributes
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// src/errors.ts
|
|
181
|
-
var ArgvioError = class extends Error {
|
|
182
|
-
constructor(message) {
|
|
183
|
-
super(`argvio: ${message}`);
|
|
184
|
-
this.name = "ArgvioError";
|
|
185
|
-
}
|
|
186
|
-
};
|
|
187
|
-
var errNoProviderResolved = new ArgvioError("no consent provider resolved a tier");
|
|
188
|
-
var errNoConsentFile = new ArgvioError("no consent config file path resolved");
|
|
189
|
-
var errUnknownTierValue = new ArgvioError("unknown tier value in consent config");
|
|
190
233
|
|
|
191
234
|
// src/consentEnv.ts
|
|
192
235
|
var CI_ENV_VARS = [
|
|
@@ -213,6 +256,21 @@ function isCI() {
|
|
|
213
256
|
return value !== void 0 && value !== "" && value !== "false" && value !== "0";
|
|
214
257
|
});
|
|
215
258
|
}
|
|
259
|
+
var CI_PROVIDERS = [
|
|
260
|
+
["GITHUB_ACTIONS", "github_actions"],
|
|
261
|
+
["GITLAB_CI", "gitlab_ci"],
|
|
262
|
+
["CIRCLECI", "circleci"],
|
|
263
|
+
["BUILDKITE", "buildkite"],
|
|
264
|
+
["JENKINS_URL", "jenkins"],
|
|
265
|
+
["TRAVIS", "travis"]
|
|
266
|
+
];
|
|
267
|
+
function detectCIProvider() {
|
|
268
|
+
for (const [envVar, provider] of CI_PROVIDERS) {
|
|
269
|
+
const value = process.env[envVar];
|
|
270
|
+
if (value && value !== "false" && value !== "0") return provider;
|
|
271
|
+
}
|
|
272
|
+
return isCI() ? "unknown" : void 0;
|
|
273
|
+
}
|
|
216
274
|
function isDoNotTrackRequested() {
|
|
217
275
|
const value = process.env["DO_NOT_TRACK"];
|
|
218
276
|
if (value === void 0) return false;
|
|
@@ -224,15 +282,139 @@ var EnvConsentProvider = class {
|
|
|
224
282
|
throw errNoProviderResolved;
|
|
225
283
|
}
|
|
226
284
|
};
|
|
227
|
-
|
|
228
|
-
|
|
285
|
+
|
|
286
|
+
// src/resource.ts
|
|
287
|
+
var ResourceAttrCLIVersion = "cli.version";
|
|
288
|
+
var ResourceAttrOS = "cli.os";
|
|
289
|
+
var ResourceAttrArch = "cli.arch";
|
|
290
|
+
var ResourceAttrIsCI = "cli.is_ci";
|
|
291
|
+
var ResourceAttrCIProvider = "cli.ci_provider";
|
|
292
|
+
function normalizeOS(platform) {
|
|
293
|
+
switch (platform) {
|
|
294
|
+
case "linux":
|
|
295
|
+
case "darwin":
|
|
296
|
+
return platform;
|
|
297
|
+
case "win32":
|
|
298
|
+
return "windows";
|
|
299
|
+
default:
|
|
300
|
+
return "other";
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
function normalizeArch(arch) {
|
|
304
|
+
switch (arch) {
|
|
305
|
+
case "x64":
|
|
306
|
+
return "amd64";
|
|
307
|
+
case "arm64":
|
|
308
|
+
case "arm":
|
|
309
|
+
return arch;
|
|
310
|
+
case "ia32":
|
|
311
|
+
return "386";
|
|
312
|
+
default:
|
|
313
|
+
return "other";
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
var SDK_NAME = "@getargvio/argvio";
|
|
317
|
+
function sdkVersion() {
|
|
318
|
+
try {
|
|
319
|
+
const pkgUrl = new URL("../package.json", (typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
|
|
320
|
+
const pkg = JSON.parse(fs.readFileSync(pkgUrl, "utf8"));
|
|
321
|
+
return pkg.version ?? "unknown";
|
|
322
|
+
} catch {
|
|
323
|
+
return "unknown";
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
function buildResource(cfg) {
|
|
327
|
+
const ciProvider = detectCIProvider();
|
|
328
|
+
return resources.resourceFromAttributes({
|
|
329
|
+
[semanticConventions.ATTR_SERVICE_NAME]: cfg.cliName,
|
|
330
|
+
[semanticConventions.ATTR_SERVICE_VERSION]: cfg.cliVersion,
|
|
331
|
+
"telemetry.sdk.name": SDK_NAME,
|
|
332
|
+
"telemetry.sdk.version": sdkVersion(),
|
|
333
|
+
"telemetry.sdk.language": "nodejs",
|
|
334
|
+
[ResourceAttrCLIVersion]: cfg.cliVersion,
|
|
335
|
+
[ResourceAttrOS]: normalizeOS(process.platform),
|
|
336
|
+
[ResourceAttrArch]: normalizeArch(process.arch),
|
|
337
|
+
[ResourceAttrIsCI]: ciProvider !== void 0,
|
|
338
|
+
...ciProvider ? { [ResourceAttrCIProvider]: ciProvider } : {},
|
|
339
|
+
...cfg.resourceAttributes
|
|
340
|
+
});
|
|
341
|
+
}
|
|
342
|
+
var AttrCommandName = "cli.command.name";
|
|
229
343
|
var AttrExitCode = "cli.command.exit_code";
|
|
230
|
-
var
|
|
231
|
-
var
|
|
232
|
-
var
|
|
344
|
+
var AttrDurationMs = "cli.command.duration_ms";
|
|
345
|
+
var AttrPeakRssBytes = "cli.process.peak_rss_bytes";
|
|
346
|
+
var AttrCpuTimeMs = "cli.process.cpu_time_ms";
|
|
347
|
+
var AttrBinarySizeBytes = "cli.binary.size_bytes";
|
|
348
|
+
var AttrStartupDurationMs = "cli.startup.duration_ms";
|
|
349
|
+
var AttrFlagNames = "cli.flag.names";
|
|
350
|
+
var AttrSessionId = "cli.session_id";
|
|
351
|
+
var AttrInstallId = "cli.install_id";
|
|
352
|
+
var AttrHelpFlag = "cli.help.flag";
|
|
353
|
+
var AttrInstallMethod = "cli.install.method";
|
|
354
|
+
var AttrActivationFeature = "cli.activation.feature";
|
|
355
|
+
var AttrErrorType = "cli.error.type";
|
|
356
|
+
var AttrErrorReason = "cli.error.reason";
|
|
233
357
|
var AttrErrorMessage = "cli.error.message";
|
|
234
|
-
var AttrErrorStack = "cli.error.
|
|
358
|
+
var AttrErrorStack = "cli.error.stacktrace";
|
|
359
|
+
var AttrCounterName = "cli.counter.name";
|
|
360
|
+
var AttrCounterLabel = "cli.counter.label";
|
|
361
|
+
var EventSessionStarted = "cli.session.started";
|
|
362
|
+
var EventCommandInvoked = "cli.command.invoked";
|
|
363
|
+
var EventCommandCompleted = "cli.command.completed";
|
|
364
|
+
var EventFeatureActivated = "cli.feature.activated";
|
|
365
|
+
var EventHelpViewed = "cli.help.viewed";
|
|
366
|
+
var EventErrorRaised = "cli.error.raised";
|
|
367
|
+
var SpanCommandPrefix = "cli.command.";
|
|
368
|
+
var MetricCommandInvocations = "cli.command.invocations";
|
|
235
369
|
var MetricCommandDuration = "cli.command.duration";
|
|
370
|
+
var MetricErrors = "cli.errors";
|
|
371
|
+
var MetricCounter = "cli.counter";
|
|
372
|
+
var MAX_COMMAND_NAME_LENGTH = 128;
|
|
373
|
+
var MAX_ACTIVATION_FEATURE_LENGTH = 64;
|
|
374
|
+
var MAX_IDENTIFIER_LENGTH = 64;
|
|
375
|
+
var IDENTIFIER_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
|
|
376
|
+
function isValidIdentifier(value) {
|
|
377
|
+
return typeof value === "string" && value.length <= MAX_IDENTIFIER_LENGTH && IDENTIFIER_PATTERN.test(value);
|
|
378
|
+
}
|
|
379
|
+
function commandName(cliName, commandPath) {
|
|
380
|
+
let parts = commandPath.split(/\s+/).filter(Boolean);
|
|
381
|
+
if (parts.length > 1 && parts[0] === cliName) parts = parts.slice(1);
|
|
382
|
+
const name = parts.join(".") || cliName || "root";
|
|
383
|
+
return name.slice(0, MAX_COMMAND_NAME_LENGTH);
|
|
384
|
+
}
|
|
385
|
+
function readProcessResourceUsage() {
|
|
386
|
+
const usage = {};
|
|
387
|
+
try {
|
|
388
|
+
const rssBytes = Math.round(process.resourceUsage().maxRSS * 1024);
|
|
389
|
+
if (Number.isFinite(rssBytes) && rssBytes > 0) usage.peakRssBytes = rssBytes;
|
|
390
|
+
} catch {
|
|
391
|
+
}
|
|
392
|
+
try {
|
|
393
|
+
const { user, system } = process.cpuUsage();
|
|
394
|
+
const cpuMs = Math.round((user + system) / 1e3);
|
|
395
|
+
if (Number.isFinite(cpuMs) && cpuMs > 0) usage.cpuTimeMs = cpuMs;
|
|
396
|
+
} catch {
|
|
397
|
+
}
|
|
398
|
+
return usage;
|
|
399
|
+
}
|
|
400
|
+
function readStartupDurationMs() {
|
|
401
|
+
try {
|
|
402
|
+
const ms = Math.round(process.uptime() * 1e3);
|
|
403
|
+
return Number.isFinite(ms) && ms > 0 ? ms : void 0;
|
|
404
|
+
} catch {
|
|
405
|
+
return void 0;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
function readBinarySizeBytes() {
|
|
409
|
+
try {
|
|
410
|
+
const sea = process.getBuiltinModule?.("node:sea");
|
|
411
|
+
if (!sea?.isSea()) return void 0;
|
|
412
|
+
const size = fs.statSync(process.execPath).size;
|
|
413
|
+
return Number.isFinite(size) && size > 0 ? size : void 0;
|
|
414
|
+
} catch {
|
|
415
|
+
return void 0;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
236
418
|
function newSessionId() {
|
|
237
419
|
try {
|
|
238
420
|
return crypto.randomBytes(16).toString("hex");
|
|
@@ -240,6 +422,24 @@ function newSessionId() {
|
|
|
240
422
|
return "";
|
|
241
423
|
}
|
|
242
424
|
}
|
|
425
|
+
var INSTALL_ID_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
426
|
+
function loadOrCreateInstallId(path$1) {
|
|
427
|
+
if (!path$1) return "";
|
|
428
|
+
try {
|
|
429
|
+
const existing = fs.readFileSync(path$1, "utf8").trim();
|
|
430
|
+
if (INSTALL_ID_PATTERN.test(existing)) return existing;
|
|
431
|
+
} catch {
|
|
432
|
+
}
|
|
433
|
+
try {
|
|
434
|
+
const id = crypto.randomUUID();
|
|
435
|
+
fs.mkdirSync(path.dirname(path$1), { recursive: true, mode: 448 });
|
|
436
|
+
fs.writeFileSync(path$1, `${id}
|
|
437
|
+
`, { mode: 384 });
|
|
438
|
+
return id;
|
|
439
|
+
} catch {
|
|
440
|
+
return "";
|
|
441
|
+
}
|
|
442
|
+
}
|
|
243
443
|
|
|
244
444
|
// src/safety.ts
|
|
245
445
|
var ARGVIO_DEBUG = process.env["ARGVIO_DEBUG"] === "1";
|
|
@@ -357,14 +557,21 @@ var Client = class {
|
|
|
357
557
|
shuttingDown = false;
|
|
358
558
|
shutdownStarted = false;
|
|
359
559
|
sessionId;
|
|
560
|
+
sessionStarted = false;
|
|
561
|
+
cliName;
|
|
562
|
+
installIdPath;
|
|
563
|
+
installMethod;
|
|
564
|
+
installId;
|
|
565
|
+
activatedFeatures = /* @__PURE__ */ new Set();
|
|
566
|
+
tracer;
|
|
360
567
|
tracerProvider;
|
|
361
568
|
meterProvider;
|
|
362
569
|
loggerProvider;
|
|
363
570
|
logger;
|
|
364
571
|
latencyHist;
|
|
365
572
|
invocationsCounter;
|
|
366
|
-
sessionsCounter;
|
|
367
573
|
errorEventsCounter;
|
|
574
|
+
productCounter;
|
|
368
575
|
/** @internal use {@link createClient} */
|
|
369
576
|
constructor(params) {
|
|
370
577
|
this.disabled = params.disabled;
|
|
@@ -378,8 +585,12 @@ var Client = class {
|
|
|
378
585
|
this.logger = params.logger;
|
|
379
586
|
this.latencyHist = params.latencyHist;
|
|
380
587
|
this.invocationsCounter = params.invocationsCounter;
|
|
381
|
-
this.sessionsCounter = params.sessionsCounter;
|
|
382
588
|
this.errorEventsCounter = params.errorEventsCounter;
|
|
589
|
+
this.productCounter = params.productCounter;
|
|
590
|
+
this.cliName = params.cliName ?? "";
|
|
591
|
+
this.installIdPath = params.installIdPath;
|
|
592
|
+
this.installMethod = params.installMethod;
|
|
593
|
+
this.tracer = params.tracer;
|
|
383
594
|
}
|
|
384
595
|
enabled() {
|
|
385
596
|
return !this.disabled && !this.shuttingDown;
|
|
@@ -401,109 +612,193 @@ var Client = class {
|
|
|
401
612
|
}
|
|
402
613
|
emitEvent(name, severity, attributes = {}) {
|
|
403
614
|
this.queue.push(() => {
|
|
404
|
-
this.
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
615
|
+
this.emitEventNow(name, severity, attributes);
|
|
616
|
+
});
|
|
617
|
+
}
|
|
618
|
+
emitEventNow(name, severity, attributes = {}) {
|
|
619
|
+
this.logger?.emit({
|
|
620
|
+
eventName: name,
|
|
621
|
+
timestamp: Date.now(),
|
|
622
|
+
severityNumber: severity,
|
|
623
|
+
attributes: { ...this.tierAttr(), ...attributes }
|
|
410
624
|
});
|
|
411
625
|
}
|
|
626
|
+
name(commandPath) {
|
|
627
|
+
return commandName(this.cliName, commandPath);
|
|
628
|
+
}
|
|
412
629
|
/**
|
|
413
|
-
* Lazily generates a session correlation ID at `Tier.
|
|
630
|
+
* Lazily generates a session correlation ID at `Tier.Basic`+. A random,
|
|
414
631
|
* non-persistent identifier scoped to a single process invocation —
|
|
415
632
|
* not a user or device identifier.
|
|
416
633
|
*/
|
|
417
634
|
ensureSessionId() {
|
|
418
|
-
if (!tierAllows(this.tier, Tier.
|
|
635
|
+
if (!tierAllows(this.tier, Tier.Basic)) return "";
|
|
419
636
|
if (this.sessionId === void 0) {
|
|
420
637
|
this.sessionId = newSessionId();
|
|
421
638
|
}
|
|
422
639
|
return this.sessionId;
|
|
423
640
|
}
|
|
424
641
|
/**
|
|
425
|
-
*
|
|
426
|
-
*
|
|
427
|
-
*
|
|
428
|
-
|
|
429
|
-
|
|
642
|
+
* The session and install ID attributes for a record, empty below
|
|
643
|
+
* `Tier.Basic`. May read or create the install ID file, so call it from
|
|
644
|
+
* inside a queued job rather than on the caller's stack.
|
|
645
|
+
*/
|
|
646
|
+
identityAttrs(sessionId) {
|
|
647
|
+
const attrs = {};
|
|
648
|
+
if (sessionId) attrs[AttrSessionId] = sessionId;
|
|
649
|
+
if (tierAllows(this.tier, Tier.Basic)) {
|
|
650
|
+
this.installId ??= loadOrCreateInstallId(this.installIdPath);
|
|
651
|
+
if (this.installId) attrs[AttrInstallId] = this.installId;
|
|
652
|
+
}
|
|
653
|
+
return attrs;
|
|
654
|
+
}
|
|
655
|
+
/**
|
|
656
|
+
* Records the start of a CLI process session, which is what the server
|
|
657
|
+
* counts sessions, active installs and retention from. Call it as early
|
|
658
|
+
* as possible (the yargs/commander integrations do it for you); only the
|
|
659
|
+
* first call per Client emits anything.
|
|
660
|
+
*
|
|
661
|
+
* At `Tier.Anonymous` the event carries no identifiers. At `Tier.Basic`+
|
|
662
|
+
* it also carries the per-process session ID and the install ID, a
|
|
663
|
+
* random UUID persisted in the CLI's config directory the first time it
|
|
664
|
+
* is needed — never derived from the user or machine.
|
|
665
|
+
*
|
|
666
|
+
* At `Tier.Basic`+ it also carries startup overhead
|
|
667
|
+
* (`cli.startup.duration_ms`): the time from process start to this call,
|
|
668
|
+
* so it covers module loading and framework setup. Call it just before
|
|
669
|
+
* the command runs, as the integrations do. And when running as a Node
|
|
670
|
+
* single executable application, it carries the binary's size
|
|
671
|
+
* (`cli.binary.size_bytes`). Either is omitted if unavailable, never 0.
|
|
430
672
|
*/
|
|
431
673
|
recordSessionStart() {
|
|
432
674
|
safeRun(() => {
|
|
433
|
-
if (!this.enabled()) return;
|
|
675
|
+
if (!this.enabled() || this.sessionStarted) return;
|
|
676
|
+
this.sessionStarted = true;
|
|
677
|
+
const sessionId = this.ensureSessionId();
|
|
678
|
+
const basic = tierAllows(this.tier, Tier.Basic);
|
|
679
|
+
const startupMs = basic ? readStartupDurationMs() : void 0;
|
|
434
680
|
this.queue.push(() => {
|
|
435
|
-
|
|
681
|
+
const attrs = this.identityAttrs(sessionId);
|
|
682
|
+
if (startupMs !== void 0) attrs[AttrStartupDurationMs] = startupMs;
|
|
683
|
+
if (basic) {
|
|
684
|
+
const sizeBytes = readBinarySizeBytes();
|
|
685
|
+
if (sizeBytes !== void 0) attrs[AttrBinarySizeBytes] = sizeBytes;
|
|
686
|
+
}
|
|
687
|
+
if (this.installMethod) attrs[AttrInstallMethod] = this.installMethod;
|
|
688
|
+
this.emitEventNow(EventSessionStarted, apiLogs.SeverityNumber.INFO, attrs);
|
|
436
689
|
});
|
|
437
|
-
if (!tierAllows(this.tier, Tier.Basic)) return;
|
|
438
|
-
const attrs = {};
|
|
439
|
-
const id = this.ensureSessionId();
|
|
440
|
-
if (id) attrs[AttrSessionId] = id;
|
|
441
|
-
this.emitEvent("cli.session_start", apiLogs.SeverityNumber.INFO, attrs);
|
|
442
|
-
}, this.counters);
|
|
443
|
-
}
|
|
444
|
-
/** Records the end of a CLI process session. Call once, symmetric with {@link recordSessionStart}. */
|
|
445
|
-
recordSessionEnd() {
|
|
446
|
-
safeRun(() => {
|
|
447
|
-
if (!this.enabled() || !tierAllows(this.tier, Tier.Basic)) return;
|
|
448
|
-
const attrs = {};
|
|
449
|
-
const id = this.ensureSessionId();
|
|
450
|
-
if (id) attrs[AttrSessionId] = id;
|
|
451
|
-
this.emitEvent("cli.session_end", apiLogs.SeverityNumber.INFO, attrs);
|
|
452
690
|
}, this.counters);
|
|
453
691
|
}
|
|
454
692
|
/**
|
|
455
693
|
* Records that a command began executing. `commandPath` should be the
|
|
456
|
-
* full command path (e.g. `"mycli sub subsub"`)
|
|
457
|
-
* command name. `flagNames` is the list of flag
|
|
458
|
-
* never flag values, which this SDK never
|
|
459
|
-
* taxonomy.
|
|
694
|
+
* full command path (e.g. `"mycli sub subsub"`); it is reported as the
|
|
695
|
+
* dotted command name (`"sub.subsub"`). `flagNames` is the list of flag
|
|
696
|
+
* *names* the user set — never flag values, which this SDK never
|
|
697
|
+
* accepts anywhere in the taxonomy.
|
|
460
698
|
*
|
|
461
|
-
*
|
|
462
|
-
*
|
|
463
|
-
* path and flag names are included.
|
|
699
|
+
* The command name is recorded at every tier; flag names and the
|
|
700
|
+
* session ID at `Tier.Basic`+.
|
|
464
701
|
*/
|
|
465
702
|
recordCommandInvocation(commandPath, flagNames = []) {
|
|
466
703
|
safeRun(() => {
|
|
467
704
|
if (!this.enabled()) return;
|
|
705
|
+
const attrs = { [AttrCommandName]: this.name(commandPath) };
|
|
706
|
+
if (tierAllows(this.tier, Tier.Basic) && flagNames.length > 0) {
|
|
707
|
+
attrs[AttrFlagNames] = [...flagNames];
|
|
708
|
+
}
|
|
709
|
+
const sessionId = this.ensureSessionId();
|
|
468
710
|
this.queue.push(() => {
|
|
469
|
-
this.
|
|
711
|
+
this.emitEventNow(EventCommandInvoked, apiLogs.SeverityNumber.INFO, {
|
|
712
|
+
...attrs,
|
|
713
|
+
...this.identityAttrs(sessionId)
|
|
714
|
+
});
|
|
470
715
|
});
|
|
471
|
-
if (!tierAllows(this.tier, Tier.Basic)) return;
|
|
472
|
-
const attrs = { [AttrCommandPath]: commandPath };
|
|
473
|
-
if (flagNames.length > 0) attrs[AttrCommandFlags] = [...flagNames];
|
|
474
|
-
this.emitEvent("cli.command_invocation", apiLogs.SeverityNumber.INFO, attrs);
|
|
475
716
|
}, this.counters);
|
|
476
717
|
}
|
|
477
718
|
/**
|
|
478
|
-
* Records
|
|
479
|
-
*
|
|
480
|
-
* `
|
|
481
|
-
*
|
|
719
|
+
* Records a completed command: a `cli.command.<name>` span covering its
|
|
720
|
+
* execution, plus the `cli.command.invocations` and
|
|
721
|
+
* `cli.command.duration` metrics. This is what the server's command
|
|
722
|
+
* frequency, latency, error-rate and exit-code dashboards are built
|
|
723
|
+
* from, so call it once for every command that ran (the integrations
|
|
724
|
+
* do). Available at every tier; flag names, peak memory and CPU time
|
|
725
|
+
* only at `Tier.Basic`+.
|
|
726
|
+
*
|
|
727
|
+
* Peak memory (`cli.process.peak_rss_bytes`) and CPU time
|
|
728
|
+
* (`cli.process.cpu_time_ms`) are read once from the process, so they are
|
|
729
|
+
* process-wide and cumulative: right for a CLI that runs one command per
|
|
730
|
+
* process, but in a long-lived or multi-command process each call
|
|
731
|
+
* reports totals since the process started. A measure the platform can't
|
|
732
|
+
* provide is omitted.
|
|
482
733
|
*/
|
|
483
|
-
|
|
734
|
+
recordCommand(result) {
|
|
484
735
|
safeRun(() => {
|
|
485
|
-
if (!this.
|
|
486
|
-
const
|
|
487
|
-
|
|
488
|
-
|
|
736
|
+
if (!this.enabled()) return;
|
|
737
|
+
const durationMs = Math.max(0, result.durationMs);
|
|
738
|
+
const startMs = result.startMs ?? Date.now() - durationMs;
|
|
739
|
+
const name = this.name(result.path);
|
|
740
|
+
const attrs = {
|
|
741
|
+
...this.tierAttr(),
|
|
742
|
+
[AttrCommandName]: name,
|
|
743
|
+
[AttrExitCode]: result.exitCode
|
|
744
|
+
};
|
|
745
|
+
const spanAttrs = { ...attrs, [AttrDurationMs]: Math.round(durationMs) };
|
|
746
|
+
if (tierAllows(this.tier, Tier.Basic) && result.flagNames && result.flagNames.length > 0) {
|
|
747
|
+
spanAttrs[AttrFlagNames] = [...result.flagNames];
|
|
489
748
|
}
|
|
490
|
-
|
|
491
|
-
|
|
749
|
+
if (tierAllows(this.tier, Tier.Basic)) {
|
|
750
|
+
const { peakRssBytes, cpuTimeMs } = readProcessResourceUsage();
|
|
751
|
+
if (peakRssBytes !== void 0) spanAttrs[AttrPeakRssBytes] = peakRssBytes;
|
|
752
|
+
if (cpuTimeMs !== void 0) spanAttrs[AttrCpuTimeMs] = cpuTimeMs;
|
|
753
|
+
}
|
|
754
|
+
const sessionId = this.ensureSessionId();
|
|
755
|
+
this.queue.push(() => {
|
|
756
|
+
const span = this.tracer?.startSpan(SpanCommandPrefix + name, {
|
|
757
|
+
startTime: startMs,
|
|
758
|
+
attributes: spanAttrs
|
|
759
|
+
});
|
|
760
|
+
if (result.exitCode !== 0) span?.setStatus({ code: api.SpanStatusCode.ERROR });
|
|
761
|
+
span?.end(startMs + durationMs);
|
|
762
|
+
this.invocationsCounter?.add(1, attrs);
|
|
763
|
+
this.latencyHist?.record(durationMs, attrs);
|
|
764
|
+
this.emitEventNow(EventCommandCompleted, apiLogs.SeverityNumber.INFO, {
|
|
765
|
+
[AttrCommandName]: name,
|
|
766
|
+
[AttrExitCode]: result.exitCode,
|
|
767
|
+
[AttrDurationMs]: Math.round(durationMs),
|
|
768
|
+
...this.identityAttrs(sessionId)
|
|
769
|
+
});
|
|
770
|
+
});
|
|
492
771
|
}, this.counters);
|
|
493
772
|
}
|
|
494
773
|
/**
|
|
495
|
-
* Records
|
|
496
|
-
*
|
|
774
|
+
* Records that the user used one of the vendor's features, so the server
|
|
775
|
+
* can tell which features an install has adopted and how soon after
|
|
776
|
+
* installing. Emitted at `Tier.Basic`+ only; nothing is sent at
|
|
777
|
+
* `Tier.Anonymous`. The event carries the feature name, the command name
|
|
778
|
+
* when `commandPath` is given, and the session and install IDs.
|
|
779
|
+
*
|
|
780
|
+
* `feature` must be a fixed identifier chosen by the vendor in code
|
|
781
|
+
* (e.g. `"export-csv"`), **never user input** such as a flag value, path
|
|
782
|
+
* or argument. It is truncated to the server's 64 character limit.
|
|
783
|
+
*
|
|
784
|
+
* Each feature is reported at most once per process, however often this
|
|
785
|
+
* is called, to keep volume down. The server derives "first use" per
|
|
786
|
+
* install itself, so repeat reports within a run add nothing.
|
|
497
787
|
*/
|
|
498
|
-
|
|
788
|
+
recordFeatureActivated(feature, commandPath) {
|
|
499
789
|
safeRun(() => {
|
|
500
|
-
if (!this.gate(Tier.
|
|
501
|
-
const
|
|
502
|
-
if (
|
|
503
|
-
|
|
504
|
-
}
|
|
790
|
+
if (!this.gate(Tier.Basic)) return;
|
|
791
|
+
const name = feature.slice(0, MAX_ACTIVATION_FEATURE_LENGTH);
|
|
792
|
+
if (!name || this.activatedFeatures.has(name)) return;
|
|
793
|
+
this.activatedFeatures.add(name);
|
|
794
|
+
const attrs = { [AttrActivationFeature]: name };
|
|
795
|
+
if (commandPath) attrs[AttrCommandName] = this.name(commandPath);
|
|
796
|
+
const sessionId = this.ensureSessionId();
|
|
505
797
|
this.queue.push(() => {
|
|
506
|
-
this.
|
|
798
|
+
this.emitEventNow(EventFeatureActivated, apiLogs.SeverityNumber.INFO, {
|
|
799
|
+
...attrs,
|
|
800
|
+
...this.identityAttrs(sessionId)
|
|
801
|
+
});
|
|
507
802
|
});
|
|
508
803
|
}, this.counters);
|
|
509
804
|
}
|
|
@@ -511,11 +806,10 @@ var Client = class {
|
|
|
511
806
|
recordHelpFlagUsage(commandPath) {
|
|
512
807
|
safeRun(() => {
|
|
513
808
|
if (!this.gate(Tier.Anonymous)) return;
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
}
|
|
518
|
-
this.emitEvent("cli.help_flag_used", apiLogs.SeverityNumber.INFO, attrs);
|
|
809
|
+
this.emitEvent(EventHelpViewed, apiLogs.SeverityNumber.INFO, {
|
|
810
|
+
[AttrCommandName]: this.name(commandPath),
|
|
811
|
+
[AttrHelpFlag]: "help"
|
|
812
|
+
});
|
|
519
813
|
}, this.counters);
|
|
520
814
|
}
|
|
521
815
|
/**
|
|
@@ -524,19 +818,73 @@ var Client = class {
|
|
|
524
818
|
* deliberately the only error-recording capability available below
|
|
525
819
|
* `Tier.OptIn`. Raw error detail is only reachable via {@link optIn}.
|
|
526
820
|
*
|
|
527
|
-
*
|
|
528
|
-
*
|
|
821
|
+
* At every tier this counts the error on the `cli.errors` metric by
|
|
822
|
+
* command. At `Tier.Basic`+ the category is attached as the error type
|
|
823
|
+
* and a `cli.error.raised` event is emitted too, tied to the session and
|
|
824
|
+
* install.
|
|
825
|
+
*
|
|
826
|
+
* `reason` is an optional, finer classification from a fixed vocabulary
|
|
827
|
+
* you define (e.g. `"no-token"`, `"invalid-url"`), sent at `Tier.Basic`+
|
|
828
|
+
* as `cli.error.reason` on both of those. It must be an identifier chosen
|
|
829
|
+
* in code, **never error text or user input**: this SDK does not scrub it.
|
|
830
|
+
* It must match `^[a-z0-9][a-z0-9._-]*$` and be at most 64 bytes; one that
|
|
831
|
+
* doesn't is dropped (the error is still recorded, without a reason).
|
|
529
832
|
*/
|
|
530
|
-
recordError(commandPath, category = ErrorCategory.Unknown) {
|
|
833
|
+
recordError(commandPath, category = ErrorCategory.Unknown, reason) {
|
|
531
834
|
safeRun(() => {
|
|
532
|
-
if (!this.
|
|
835
|
+
if (!this.enabled()) return;
|
|
533
836
|
const cat = isValidErrorCategory(category) ? category : ErrorCategory.Unknown;
|
|
534
|
-
const
|
|
535
|
-
|
|
837
|
+
const basic = tierAllows(this.tier, Tier.Basic);
|
|
838
|
+
const attrs = { [AttrCommandName]: this.name(commandPath) };
|
|
839
|
+
if (basic) {
|
|
840
|
+
attrs[AttrErrorType] = cat;
|
|
841
|
+
if (isValidIdentifier(reason)) attrs[AttrErrorReason] = reason;
|
|
842
|
+
}
|
|
843
|
+
const sessionId = this.ensureSessionId();
|
|
536
844
|
this.queue.push(() => {
|
|
537
|
-
this.errorEventsCounter?.add(1, { ...this.tierAttr(),
|
|
845
|
+
this.errorEventsCounter?.add(1, { ...this.tierAttr(), ...attrs });
|
|
846
|
+
if (basic) {
|
|
847
|
+
this.emitEventNow(EventErrorRaised, apiLogs.SeverityNumber.ERROR, {
|
|
848
|
+
...attrs,
|
|
849
|
+
...this.identityAttrs(sessionId)
|
|
850
|
+
});
|
|
851
|
+
}
|
|
538
852
|
});
|
|
539
|
-
|
|
853
|
+
}, this.counters);
|
|
854
|
+
}
|
|
855
|
+
/**
|
|
856
|
+
* Adds `n` to a vendor-defined counter, sent as the `cli.counter` metric
|
|
857
|
+
* with the `cli.counter.name` and, if given, `cli.counter.label`
|
|
858
|
+
* attributes. Increments are summed in memory: one data point per name
|
|
859
|
+
* and label is sent at each export, however often this is called, so it
|
|
860
|
+
* is cheap in a hot loop.
|
|
861
|
+
*
|
|
862
|
+
* **Counters are accepted at the anonymous tier.** They are sent at every
|
|
863
|
+
* tier, including `Tier.Anonymous` and in CI, even when the user has made
|
|
864
|
+
* no consent choice. Nothing identifies the user, but `name` and `label`
|
|
865
|
+
* are strings you choose, so a mistake here puts text into anonymous
|
|
866
|
+
* data. They must be fixed identifiers chosen in code, **never user
|
|
867
|
+
* input, file paths, host names, URLs, tokens or anything derived from
|
|
868
|
+
* them**. This SDK does not scrub them.
|
|
869
|
+
*
|
|
870
|
+
* `name` and `label` must match `^[a-z0-9][a-z0-9._-]*$` and be at most
|
|
871
|
+
* 64 bytes. If either does not, nothing is recorded: the increment is
|
|
872
|
+
* dropped, not trimmed or sent without its label. `n` must be a positive
|
|
873
|
+
* integer; anything else is ignored. The server caps the number of
|
|
874
|
+
* distinct names and labels per tenant and folds the rest into `other`.
|
|
875
|
+
*
|
|
876
|
+
* Never throws, and does nothing on a disabled Client.
|
|
877
|
+
*/
|
|
878
|
+
recordCounter(name, n, options = {}) {
|
|
879
|
+
safeRun(() => {
|
|
880
|
+
if (!this.gate(Tier.Anonymous)) return;
|
|
881
|
+
if (!isValidIdentifier(name)) return;
|
|
882
|
+
if (typeof n !== "number" || !Number.isSafeInteger(n) || n <= 0) return;
|
|
883
|
+
const label = options?.label;
|
|
884
|
+
if (label !== void 0 && !isValidIdentifier(label)) return;
|
|
885
|
+
const attrs = { ...this.tierAttr(), [AttrCounterName]: name };
|
|
886
|
+
if (label !== void 0) attrs[AttrCounterLabel] = label;
|
|
887
|
+
this.productCounter?.add(n, attrs);
|
|
540
888
|
}, this.counters);
|
|
541
889
|
}
|
|
542
890
|
/**
|
|
@@ -554,10 +902,12 @@ var Client = class {
|
|
|
554
902
|
_recordErrorDetail(commandPath, err, stack) {
|
|
555
903
|
safeRun(() => {
|
|
556
904
|
if (!this.gate(Tier.OptIn)) return;
|
|
557
|
-
const attrs = {
|
|
558
|
-
|
|
905
|
+
const attrs = {
|
|
906
|
+
[AttrCommandName]: this.name(commandPath),
|
|
907
|
+
[AttrErrorMessage]: err.message
|
|
908
|
+
};
|
|
559
909
|
if (stack) attrs[AttrErrorStack] = stack;
|
|
560
|
-
this.emitEvent(
|
|
910
|
+
this.emitEvent(EventErrorRaised, apiLogs.SeverityNumber.ERROR, attrs);
|
|
561
911
|
}, this.counters);
|
|
562
912
|
}
|
|
563
913
|
/**
|
|
@@ -570,13 +920,11 @@ var Client = class {
|
|
|
570
920
|
if (!this.enabled()) return;
|
|
571
921
|
await this.queue.waitForDrain(timeoutMs);
|
|
572
922
|
await withTimeout(
|
|
573
|
-
Promise.
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
].filter((p) => Boolean(p))
|
|
579
|
-
).then(() => void 0),
|
|
923
|
+
Promise.allSettled([
|
|
924
|
+
this.tracerProvider?.forceFlush(),
|
|
925
|
+
this.meterProvider?.forceFlush(),
|
|
926
|
+
this.loggerProvider?.forceFlush()
|
|
927
|
+
]).then(() => void 0),
|
|
580
928
|
timeoutMs
|
|
581
929
|
);
|
|
582
930
|
}
|
|
@@ -592,13 +940,11 @@ var Client = class {
|
|
|
592
940
|
this.shuttingDown = true;
|
|
593
941
|
await this.queue.waitForDrain(timeoutMs);
|
|
594
942
|
await withTimeout(
|
|
595
|
-
Promise.
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
].filter((p) => Boolean(p))
|
|
601
|
-
).then(() => void 0),
|
|
943
|
+
Promise.allSettled([
|
|
944
|
+
this.tracerProvider?.shutdown(),
|
|
945
|
+
this.meterProvider?.shutdown(),
|
|
946
|
+
this.loggerProvider?.shutdown()
|
|
947
|
+
]).then(() => void 0),
|
|
602
948
|
timeoutMs
|
|
603
949
|
);
|
|
604
950
|
}
|
|
@@ -675,6 +1021,10 @@ function createClient(apiKey, cliName, cliVersion, options = {}) {
|
|
|
675
1021
|
const meter = meterProvider.getMeter(INSTRUMENTATION_NAME);
|
|
676
1022
|
const logger = loggerProvider.getLogger(INSTRUMENTATION_NAME);
|
|
677
1023
|
return new Client({
|
|
1024
|
+
cliName,
|
|
1025
|
+
installIdPath: cfg.installIdPath,
|
|
1026
|
+
installMethod: cfg.installMethod,
|
|
1027
|
+
tracer: tracerProvider.getTracer(INSTRUMENTATION_NAME),
|
|
678
1028
|
disabled: false,
|
|
679
1029
|
initError: null,
|
|
680
1030
|
tier,
|
|
@@ -688,14 +1038,17 @@ function createClient(apiKey, cliName, cliVersion, options = {}) {
|
|
|
688
1038
|
unit: "ms",
|
|
689
1039
|
description: "Duration of a CLI command invocation."
|
|
690
1040
|
}),
|
|
691
|
-
invocationsCounter: meter.createCounter(
|
|
692
|
-
|
|
1041
|
+
invocationsCounter: meter.createCounter(MetricCommandInvocations, {
|
|
1042
|
+
unit: "1",
|
|
1043
|
+
description: "Count of completed CLI command invocations."
|
|
693
1044
|
}),
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
}),
|
|
697
|
-
errorEventsCounter: meter.createCounter("cli.errors", {
|
|
1045
|
+
errorEventsCounter: meter.createCounter(MetricErrors, {
|
|
1046
|
+
unit: "1",
|
|
698
1047
|
description: "Count of recorded CLI errors."
|
|
1048
|
+
}),
|
|
1049
|
+
productCounter: meter.createCounter(MetricCounter, {
|
|
1050
|
+
unit: "1",
|
|
1051
|
+
description: "Vendor-defined counters, by fixed name and optional label."
|
|
699
1052
|
})
|
|
700
1053
|
});
|
|
701
1054
|
} catch (err) {
|
|
@@ -733,64 +1086,26 @@ function chainProviders(...providers) {
|
|
|
733
1086
|
}
|
|
734
1087
|
};
|
|
735
1088
|
}
|
|
736
|
-
function userConfigDir() {
|
|
737
|
-
switch (process.platform) {
|
|
738
|
-
case "win32":
|
|
739
|
-
return process.env["APPDATA"];
|
|
740
|
-
case "darwin":
|
|
741
|
-
return path.join(os.homedir(), "Library", "Application Support");
|
|
742
|
-
default:
|
|
743
|
-
return process.env["XDG_CONFIG_HOME"] || path.join(os.homedir(), ".config");
|
|
744
|
-
}
|
|
745
|
-
}
|
|
746
|
-
function defaultConsentPath(cliName) {
|
|
747
|
-
const dir = userConfigDir();
|
|
748
|
-
if (!dir) return void 0;
|
|
749
|
-
return path.join(dir, cliName || "argvio", "argvio-consent.json");
|
|
750
|
-
}
|
|
751
|
-
var FileConsentProvider = class {
|
|
752
|
-
/** The resolved config file path (after defaults/overrides), primarily for diagnostics and tests. */
|
|
753
|
-
path;
|
|
754
|
-
constructor(cliName, options = {}) {
|
|
755
|
-
this.path = options.path || defaultConsentPath(cliName);
|
|
756
|
-
}
|
|
757
|
-
/**
|
|
758
|
-
* A missing file, unreadable file, or malformed contents all throw
|
|
759
|
-
* synchronously, so callers should place `FileConsentProvider` inside
|
|
760
|
-
* `chainProviders` with a sane fallback (e.g.
|
|
761
|
-
* `new StaticProvider(Tier.Anonymous)`).
|
|
762
|
-
*/
|
|
763
|
-
resolve() {
|
|
764
|
-
if (!this.path) throw errNoConsentFile;
|
|
765
|
-
const data = fs.readFileSync(this.path, "utf8");
|
|
766
|
-
const parsed = JSON.parse(data);
|
|
767
|
-
const tier = parseTier(parsed.tier ?? "");
|
|
768
|
-
if (tier === void 0) throw errUnknownTierValue;
|
|
769
|
-
return tier;
|
|
770
|
-
}
|
|
771
|
-
/**
|
|
772
|
-
* Persists `tier` as the chosen consent tier, creating the parent
|
|
773
|
-
* directory if needed. Intended for use by a vendor's first-run
|
|
774
|
-
* consent prompt. Does not itself enforce any tier ceiling — it's the
|
|
775
|
-
* vendor's own UX writing the user's explicit choice.
|
|
776
|
-
*/
|
|
777
|
-
store(tier) {
|
|
778
|
-
if (!this.path) throw errNoConsentFile;
|
|
779
|
-
if (!isValidTier(tier)) throw errUnknownTierValue;
|
|
780
|
-
fs.mkdirSync(path.dirname(this.path), { recursive: true, mode: 448 });
|
|
781
|
-
const contents = { tier: tierToString(tier) };
|
|
782
|
-
fs.writeFileSync(this.path, JSON.stringify(contents), { mode: 384 });
|
|
783
|
-
}
|
|
784
|
-
};
|
|
785
1089
|
|
|
786
|
-
exports.
|
|
787
|
-
exports.
|
|
788
|
-
exports.
|
|
1090
|
+
exports.AttrActivationFeature = AttrActivationFeature;
|
|
1091
|
+
exports.AttrBinarySizeBytes = AttrBinarySizeBytes;
|
|
1092
|
+
exports.AttrCommandName = AttrCommandName;
|
|
1093
|
+
exports.AttrCounterLabel = AttrCounterLabel;
|
|
1094
|
+
exports.AttrCounterName = AttrCounterName;
|
|
1095
|
+
exports.AttrCpuTimeMs = AttrCpuTimeMs;
|
|
1096
|
+
exports.AttrDurationMs = AttrDurationMs;
|
|
789
1097
|
exports.AttrErrorMessage = AttrErrorMessage;
|
|
1098
|
+
exports.AttrErrorReason = AttrErrorReason;
|
|
790
1099
|
exports.AttrErrorStack = AttrErrorStack;
|
|
1100
|
+
exports.AttrErrorType = AttrErrorType;
|
|
791
1101
|
exports.AttrExitCode = AttrExitCode;
|
|
792
|
-
exports.
|
|
1102
|
+
exports.AttrFlagNames = AttrFlagNames;
|
|
1103
|
+
exports.AttrHelpFlag = AttrHelpFlag;
|
|
1104
|
+
exports.AttrInstallId = AttrInstallId;
|
|
1105
|
+
exports.AttrInstallMethod = AttrInstallMethod;
|
|
1106
|
+
exports.AttrPeakRssBytes = AttrPeakRssBytes;
|
|
793
1107
|
exports.AttrSessionId = AttrSessionId;
|
|
1108
|
+
exports.AttrStartupDurationMs = AttrStartupDurationMs;
|
|
794
1109
|
exports.Client = Client;
|
|
795
1110
|
exports.DEFAULT_AUTH_HEADER = DEFAULT_AUTH_HEADER;
|
|
796
1111
|
exports.DEFAULT_ENDPOINT_GRPC = DEFAULT_ENDPOINT_GRPC;
|
|
@@ -801,15 +1116,31 @@ exports.DEFAULT_SHUTDOWN_TIMEOUT_MS = DEFAULT_SHUTDOWN_TIMEOUT_MS;
|
|
|
801
1116
|
exports.DisabledProvider = DisabledProvider;
|
|
802
1117
|
exports.EnvConsentProvider = EnvConsentProvider;
|
|
803
1118
|
exports.ErrorCategory = ErrorCategory;
|
|
1119
|
+
exports.EventCommandCompleted = EventCommandCompleted;
|
|
1120
|
+
exports.EventCommandInvoked = EventCommandInvoked;
|
|
1121
|
+
exports.EventErrorRaised = EventErrorRaised;
|
|
1122
|
+
exports.EventFeatureActivated = EventFeatureActivated;
|
|
1123
|
+
exports.EventHelpViewed = EventHelpViewed;
|
|
1124
|
+
exports.EventSessionStarted = EventSessionStarted;
|
|
804
1125
|
exports.FileConsentProvider = FileConsentProvider;
|
|
805
1126
|
exports.MetricCommandDuration = MetricCommandDuration;
|
|
1127
|
+
exports.MetricCommandInvocations = MetricCommandInvocations;
|
|
1128
|
+
exports.MetricCounter = MetricCounter;
|
|
1129
|
+
exports.MetricErrors = MetricErrors;
|
|
806
1130
|
exports.OptInScope = OptInScope;
|
|
807
1131
|
exports.RESOURCE_ATTR_TIER = RESOURCE_ATTR_TIER;
|
|
1132
|
+
exports.ResourceAttrArch = ResourceAttrArch;
|
|
1133
|
+
exports.ResourceAttrCIProvider = ResourceAttrCIProvider;
|
|
1134
|
+
exports.ResourceAttrCLIVersion = ResourceAttrCLIVersion;
|
|
1135
|
+
exports.ResourceAttrIsCI = ResourceAttrIsCI;
|
|
1136
|
+
exports.ResourceAttrOS = ResourceAttrOS;
|
|
1137
|
+
exports.SpanCommandPrefix = SpanCommandPrefix;
|
|
808
1138
|
exports.StaticProvider = StaticProvider;
|
|
809
1139
|
exports.Tier = Tier;
|
|
810
1140
|
exports.chainProviders = chainProviders;
|
|
811
1141
|
exports.clampTier = clampTier;
|
|
812
1142
|
exports.createClient = createClient;
|
|
1143
|
+
exports.detectCIProvider = detectCIProvider;
|
|
813
1144
|
exports.isCI = isCI;
|
|
814
1145
|
exports.isDoNotTrackRequested = isDoNotTrackRequested;
|
|
815
1146
|
exports.isValidErrorCategory = isValidErrorCategory;
|