workos 0.15.2 → 0.17.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 +44 -14
- package/dist/bin.js +1449 -1261
- package/dist/bin.js.map +1 -1
- package/dist/cli.config.d.ts +1 -0
- package/dist/cli.config.js +1 -0
- package/dist/cli.config.js.map +1 -1
- package/dist/commands/api/index.js +7 -2
- package/dist/commands/api/index.js.map +1 -1
- package/dist/commands/api/interactive.js +9 -3
- package/dist/commands/api/interactive.js.map +1 -1
- package/dist/commands/debug.d.ts +2 -1
- package/dist/commands/debug.js +43 -3
- package/dist/commands/debug.js.map +1 -1
- package/dist/commands/dev.js +5 -4
- package/dist/commands/dev.js.map +1 -1
- package/dist/commands/doctor.js +13 -4
- package/dist/commands/doctor.js.map +1 -1
- package/dist/commands/emulate.js +2 -2
- package/dist/commands/emulate.js.map +1 -1
- package/dist/commands/env.js +5 -4
- package/dist/commands/env.js.map +1 -1
- package/dist/commands/install-skill.js +4 -3
- package/dist/commands/install-skill.js.map +1 -1
- package/dist/commands/install.js +2 -2
- package/dist/commands/install.js.map +1 -1
- package/dist/commands/login.js +3 -3
- package/dist/commands/login.js.map +1 -1
- package/dist/commands/migrations.d.ts +1 -1
- package/dist/commands/migrations.js +4 -1
- package/dist/commands/migrations.js.map +1 -1
- package/dist/commands/telemetry.d.ts +3 -0
- package/dist/commands/telemetry.js +88 -0
- package/dist/commands/telemetry.js.map +1 -0
- package/dist/commands/uninstall-skill.js +3 -2
- package/dist/commands/uninstall-skill.js.map +1 -1
- package/dist/commands/vault-run.d.ts +13 -0
- package/dist/commands/vault-run.js +194 -0
- package/dist/commands/vault-run.js.map +1 -0
- package/dist/commands/vault.d.ts +3 -2
- package/dist/commands/vault.js +41 -8
- package/dist/commands/vault.js.map +1 -1
- package/dist/lib/adapters/cli-adapter.d.ts +7 -0
- package/dist/lib/adapters/cli-adapter.js +49 -0
- package/dist/lib/adapters/cli-adapter.js.map +1 -1
- package/dist/lib/adapters/dashboard-adapter.d.ts +4 -0
- package/dist/lib/adapters/dashboard-adapter.js +24 -0
- package/dist/lib/adapters/dashboard-adapter.js.map +1 -1
- package/dist/lib/adapters/headless-adapter.d.ts +6 -0
- package/dist/lib/adapters/headless-adapter.js +26 -1
- package/dist/lib/adapters/headless-adapter.js.map +1 -1
- package/dist/lib/api-error-handler.d.ts +15 -3
- package/dist/lib/api-error-handler.js +52 -34
- package/dist/lib/api-error-handler.js.map +1 -1
- package/dist/lib/command-aliases.d.ts +8 -0
- package/dist/lib/command-aliases.js +12 -0
- package/dist/lib/command-aliases.js.map +1 -0
- package/dist/lib/constants.d.ts +0 -1
- package/dist/lib/constants.js +0 -1
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/device-id.d.ts +25 -0
- package/dist/lib/device-id.js +102 -0
- package/dist/lib/device-id.js.map +1 -0
- package/dist/lib/events.d.ts +15 -0
- package/dist/lib/events.js.map +1 -1
- package/dist/lib/installer-core.d.ts +61 -1
- package/dist/lib/installer-core.js +132 -6
- package/dist/lib/installer-core.js.map +1 -1
- package/dist/lib/installer-core.types.d.ts +24 -0
- package/dist/lib/installer-core.types.js.map +1 -1
- package/dist/lib/preferences.d.ts +101 -0
- package/dist/lib/preferences.js +198 -0
- package/dist/lib/preferences.js.map +1 -0
- package/dist/lib/run-with-core.js +40 -15
- package/dist/lib/run-with-core.js.map +1 -1
- package/dist/lib/scaffold/index.d.ts +1 -0
- package/dist/lib/scaffold/index.js +2 -0
- package/dist/lib/scaffold/index.js.map +1 -0
- package/dist/lib/scaffold/scaffold.d.ts +66 -0
- package/dist/lib/scaffold/scaffold.js +156 -0
- package/dist/lib/scaffold/scaffold.js.map +1 -0
- package/dist/lib/settings.d.ts +6 -0
- package/dist/lib/settings.js +7 -0
- package/dist/lib/settings.js.map +1 -1
- package/dist/lib/telemetry-notice.d.ts +25 -0
- package/dist/lib/telemetry-notice.js +56 -0
- package/dist/lib/telemetry-notice.js.map +1 -0
- package/dist/run.d.ts +2 -2
- package/dist/run.js +2 -1
- package/dist/run.js.map +1 -1
- package/dist/test/force-insecure-storage.d.ts +1 -0
- package/dist/test/force-insecure-storage.js +9 -0
- package/dist/test/force-insecure-storage.js.map +1 -0
- package/dist/test/setup.d.ts +1 -0
- package/dist/test/setup.js +38 -0
- package/dist/test/setup.js.map +1 -0
- package/dist/utils/analytics.d.ts +41 -0
- package/dist/utils/analytics.js +199 -12
- package/dist/utils/analytics.js.map +1 -1
- package/dist/utils/box.d.ts +29 -1
- package/dist/utils/box.js +92 -4
- package/dist/utils/box.js.map +1 -1
- package/dist/utils/cli-exit.d.ts +15 -0
- package/dist/utils/cli-exit.js +11 -0
- package/dist/utils/cli-exit.js.map +1 -0
- package/dist/utils/cli-symbols.d.ts +1 -1
- package/dist/utils/command-telemetry.d.ts +17 -0
- package/dist/utils/command-telemetry.js +67 -0
- package/dist/utils/command-telemetry.js.map +1 -0
- package/dist/utils/crash-reporter.d.ts +13 -0
- package/dist/utils/crash-reporter.js +91 -0
- package/dist/utils/crash-reporter.js.map +1 -0
- package/dist/utils/debug.d.ts +1 -0
- package/dist/utils/debug.js +4 -1
- package/dist/utils/debug.js.map +1 -1
- package/dist/utils/exit-codes.d.ts +5 -0
- package/dist/utils/exit-codes.js +30 -1
- package/dist/utils/exit-codes.js.map +1 -1
- package/dist/utils/help-json.d.ts +6 -0
- package/dist/utils/help-json.js +87 -10
- package/dist/utils/help-json.js.map +1 -1
- package/dist/utils/output.d.ts +7 -2
- package/dist/utils/output.js +9 -2
- package/dist/utils/output.js.map +1 -1
- package/dist/utils/telemetry-client.d.ts +30 -2
- package/dist/utils/telemetry-client.js +122 -12
- package/dist/utils/telemetry-client.js.map +1 -1
- package/dist/utils/telemetry-store-forward.d.ts +11 -0
- package/dist/utils/telemetry-store-forward.js +94 -0
- package/dist/utils/telemetry-store-forward.js.map +1 -0
- package/dist/utils/telemetry-types.d.ts +58 -9
- package/dist/utils/telemetry-types.js.map +1 -1
- package/dist/utils/types.d.ts +10 -4
- package/dist/utils/types.js.map +1 -1
- package/package.json +1 -1
package/dist/utils/analytics.js
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
|
+
import os from 'node:os';
|
|
2
|
+
import { basename } from 'node:path';
|
|
1
3
|
import { v4 as uuidv4 } from 'uuid';
|
|
2
4
|
import { debug } from './debug.js';
|
|
3
5
|
import { telemetryClient } from './telemetry-client.js';
|
|
4
|
-
import {
|
|
6
|
+
import { isTelemetryEnabled } from '../lib/preferences.js';
|
|
7
|
+
import { getTelemetryUrl, getVersion } from '../lib/settings.js';
|
|
8
|
+
import { getCredentials, isTokenExpired } from '../lib/credentials.js';
|
|
9
|
+
import { getActiveEnvironment, isUnclaimedEnvironment } from '../lib/config-store.js';
|
|
10
|
+
import { getDeviceId } from '../lib/device-id.js';
|
|
11
|
+
import { sanitizeMessage, sanitizeStack } from './crash-reporter.js';
|
|
5
12
|
export class Analytics {
|
|
6
13
|
tags = {};
|
|
7
14
|
sessionId;
|
|
8
15
|
sessionStartTime;
|
|
9
16
|
distinctId;
|
|
17
|
+
mode;
|
|
18
|
+
authMode = 'none';
|
|
10
19
|
// Agent metrics tracking
|
|
11
20
|
totalInputTokens = 0;
|
|
12
21
|
totalOutputTokens = 0;
|
|
@@ -22,14 +31,90 @@ export class Analytics {
|
|
|
22
31
|
setAccessToken(token) {
|
|
23
32
|
telemetryClient.setAccessToken(token);
|
|
24
33
|
}
|
|
34
|
+
setApiKeyAuth(apiKey) {
|
|
35
|
+
telemetryClient.setApiKeyAuth(apiKey);
|
|
36
|
+
}
|
|
37
|
+
setClaimTokenAuth(clientId, claimToken) {
|
|
38
|
+
telemetryClient.setClaimTokenAuth(clientId, claimToken);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Set the auth mode explicitly for special cases. Normal CLI flows should use
|
|
42
|
+
* `configureAuthFromAvailableSources()` so transport and auth.mode stay aligned.
|
|
43
|
+
*/
|
|
44
|
+
setAuthMode(mode) {
|
|
45
|
+
this.authMode = mode;
|
|
46
|
+
}
|
|
25
47
|
setGatewayUrl(url) {
|
|
26
48
|
telemetryClient.setGatewayUrl(url);
|
|
27
49
|
}
|
|
50
|
+
isEnabled() {
|
|
51
|
+
return isTelemetryEnabled();
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Configure telemetry transport and auth.mode from all available CLI auth
|
|
55
|
+
* sources. Priority: stored JWT, unclaimed-environment claim token, active
|
|
56
|
+
* environment API key, then WORKOS_API_KEY.
|
|
57
|
+
*/
|
|
58
|
+
configureAuthFromAvailableSources() {
|
|
59
|
+
if (!this.isEnabled())
|
|
60
|
+
return this.authMode;
|
|
61
|
+
this.authMode = 'none';
|
|
62
|
+
const creds = getCredentials();
|
|
63
|
+
// Only treat the JWT as usable auth when it is still valid. An expired
|
|
64
|
+
// access token would 401 against the telemetry guard and the event would
|
|
65
|
+
// be dropped, so fall through to claim-token / api-key auth instead.
|
|
66
|
+
if (creds?.accessToken && !isTokenExpired(creds)) {
|
|
67
|
+
telemetryClient.setAccessToken(creds.accessToken);
|
|
68
|
+
this.authMode = 'jwt';
|
|
69
|
+
}
|
|
70
|
+
// Preserve identity even when the token is expired.
|
|
71
|
+
if (creds?.userId) {
|
|
72
|
+
this.distinctId = creds.userId;
|
|
73
|
+
}
|
|
74
|
+
// Check for unclaimed environment — fall back to claim-token auth
|
|
75
|
+
// so unclaimed users' telemetry still reaches the backend.
|
|
76
|
+
try {
|
|
77
|
+
const env = getActiveEnvironment();
|
|
78
|
+
if (env && isUnclaimedEnvironment(env)) {
|
|
79
|
+
telemetryClient.setClaimTokenAuth(env.clientId, env.claimToken);
|
|
80
|
+
// Tag distinctId so unclaimed sessions are identifiable in analytics
|
|
81
|
+
this.distinctId = this.distinctId ?? `unclaimed:${env.clientId}`;
|
|
82
|
+
if (this.authMode === 'none')
|
|
83
|
+
this.authMode = 'claim_token';
|
|
84
|
+
}
|
|
85
|
+
else if (env?.apiKey && this.authMode === 'none') {
|
|
86
|
+
telemetryClient.setApiKeyAuth(env.apiKey);
|
|
87
|
+
if (env.clientId)
|
|
88
|
+
this.distinctId = this.distinctId ?? `env:${env.clientId}`;
|
|
89
|
+
this.authMode = 'api_key';
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
// Config-store failure is non-fatal for telemetry
|
|
94
|
+
}
|
|
95
|
+
// WORKOS_API_KEY covers API-key-only users. Lowest priority — JWT and
|
|
96
|
+
// claim-token auth have richer identity context when available.
|
|
97
|
+
if (this.authMode === 'none' && process.env.WORKOS_API_KEY) {
|
|
98
|
+
telemetryClient.setApiKeyAuth(process.env.WORKOS_API_KEY);
|
|
99
|
+
this.authMode = 'api_key';
|
|
100
|
+
}
|
|
101
|
+
return this.authMode;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Initialize telemetry for non-installer commands.
|
|
105
|
+
* Sets telemetry URL from default config and loads auth credentials.
|
|
106
|
+
*/
|
|
107
|
+
initForNonInstaller() {
|
|
108
|
+
if (!this.isEnabled())
|
|
109
|
+
return;
|
|
110
|
+
telemetryClient.setGatewayUrl(getTelemetryUrl());
|
|
111
|
+
this.configureAuthFromAvailableSources();
|
|
112
|
+
}
|
|
28
113
|
setTag(key, value) {
|
|
29
114
|
this.tags[key] = value;
|
|
30
115
|
}
|
|
31
116
|
capture(eventName, properties) {
|
|
32
|
-
if (!
|
|
117
|
+
if (!this.isEnabled())
|
|
33
118
|
return;
|
|
34
119
|
debug(`[Analytics] capture: ${eventName}`, properties);
|
|
35
120
|
// Accumulate primitive values as tags for the session.end event
|
|
@@ -42,19 +127,64 @@ export class Analytics {
|
|
|
42
127
|
}
|
|
43
128
|
}
|
|
44
129
|
captureException(error, properties = {}) {
|
|
45
|
-
if (!
|
|
130
|
+
if (!this.isEnabled())
|
|
46
131
|
return;
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
132
|
+
// Sanitize BEFORE logging — raw error.message can carry Bearer tokens /
|
|
133
|
+
// sk_ keys / JWTs on auth-failure paths, which would surface in stdout
|
|
134
|
+
// under WORKOS_DEBUG=1.
|
|
135
|
+
const { type, message } = this.extractErrorFields(error);
|
|
136
|
+
debug('[Analytics] captureException:', message, properties);
|
|
137
|
+
this.tags['error.type'] = type;
|
|
138
|
+
this.tags['error.message'] = message;
|
|
50
139
|
}
|
|
51
140
|
async getFeatureFlag(_flagKey) {
|
|
52
141
|
// Feature flags not implemented yet
|
|
53
142
|
return undefined;
|
|
54
143
|
}
|
|
144
|
+
/** All capture methods that record error details MUST go through this. */
|
|
145
|
+
extractErrorFields(error) {
|
|
146
|
+
return {
|
|
147
|
+
type: error.name,
|
|
148
|
+
message: sanitizeMessage(error.message),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
detectCiProvider() {
|
|
152
|
+
if (process.env.GITHUB_ACTIONS)
|
|
153
|
+
return 'github-actions';
|
|
154
|
+
if (process.env.BUILDKITE)
|
|
155
|
+
return 'buildkite';
|
|
156
|
+
if (process.env.CIRCLECI)
|
|
157
|
+
return 'circleci';
|
|
158
|
+
if (process.env.GITLAB_CI)
|
|
159
|
+
return 'gitlab-ci';
|
|
160
|
+
if (process.env.JENKINS_URL)
|
|
161
|
+
return 'jenkins';
|
|
162
|
+
return undefined;
|
|
163
|
+
}
|
|
164
|
+
getEnvFingerprint() {
|
|
165
|
+
let osVersion;
|
|
166
|
+
try {
|
|
167
|
+
osVersion = os.release();
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
osVersion = 'unknown';
|
|
171
|
+
}
|
|
172
|
+
const ciProvider = this.detectCiProvider();
|
|
173
|
+
return {
|
|
174
|
+
'device.id': getDeviceId(),
|
|
175
|
+
'auth.mode': this.authMode,
|
|
176
|
+
'env.os': process.platform,
|
|
177
|
+
'env.os_version': osVersion,
|
|
178
|
+
'env.node_version': process.version,
|
|
179
|
+
'env.shell': basename(process.env.SHELL ?? process.env.COMSPEC ?? 'unknown'),
|
|
180
|
+
'env.ci': Boolean(process.env.CI || process.env.GITHUB_ACTIONS || process.env.BUILDKITE),
|
|
181
|
+
...(ciProvider ? { 'env.ci_provider': ciProvider } : {}),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
55
184
|
sessionStart(mode, version) {
|
|
56
|
-
if (!
|
|
185
|
+
if (!this.isEnabled())
|
|
57
186
|
return;
|
|
187
|
+
this.mode = mode;
|
|
58
188
|
const event = {
|
|
59
189
|
type: 'session.start',
|
|
60
190
|
sessionId: this.sessionId,
|
|
@@ -63,39 +193,42 @@ export class Analytics {
|
|
|
63
193
|
'installer.version': version,
|
|
64
194
|
'installer.mode': mode,
|
|
65
195
|
'workos.user_id': this.distinctId,
|
|
196
|
+
...this.getEnvFingerprint(),
|
|
66
197
|
},
|
|
67
198
|
};
|
|
68
199
|
telemetryClient.queueEvent(event);
|
|
69
200
|
}
|
|
70
201
|
stepCompleted(name, durationMs, success, error) {
|
|
71
|
-
if (!
|
|
202
|
+
if (!this.isEnabled())
|
|
72
203
|
return;
|
|
73
204
|
const event = {
|
|
74
205
|
type: 'step',
|
|
75
206
|
sessionId: this.sessionId,
|
|
76
207
|
timestamp: new Date().toISOString(),
|
|
77
208
|
name,
|
|
209
|
+
startTimestamp: new Date(Date.now() - durationMs).toISOString(),
|
|
78
210
|
durationMs,
|
|
79
211
|
success,
|
|
80
|
-
error: error ?
|
|
212
|
+
error: error ? this.extractErrorFields(error) : undefined,
|
|
81
213
|
};
|
|
82
214
|
telemetryClient.queueEvent(event);
|
|
83
215
|
}
|
|
84
216
|
toolCalled(toolName, durationMs, success) {
|
|
85
|
-
if (!
|
|
217
|
+
if (!this.isEnabled())
|
|
86
218
|
return;
|
|
87
219
|
const event = {
|
|
88
220
|
type: 'agent.tool',
|
|
89
221
|
sessionId: this.sessionId,
|
|
90
222
|
timestamp: new Date().toISOString(),
|
|
91
223
|
toolName,
|
|
224
|
+
startTimestamp: new Date(Date.now() - durationMs).toISOString(),
|
|
92
225
|
durationMs,
|
|
93
226
|
success,
|
|
94
227
|
};
|
|
95
228
|
telemetryClient.queueEvent(event);
|
|
96
229
|
}
|
|
97
230
|
llmRequest(model, inputTokens, outputTokens) {
|
|
98
|
-
if (!
|
|
231
|
+
if (!this.isEnabled())
|
|
99
232
|
return;
|
|
100
233
|
this.totalInputTokens += inputTokens;
|
|
101
234
|
this.totalOutputTokens += outputTokens;
|
|
@@ -112,12 +245,64 @@ export class Analytics {
|
|
|
112
245
|
incrementAgentIterations() {
|
|
113
246
|
this.agentIterations++;
|
|
114
247
|
}
|
|
248
|
+
emitCommandEvent(name, durationMs, success, options) {
|
|
249
|
+
if (!this.isEnabled())
|
|
250
|
+
return;
|
|
251
|
+
const errorFields = options?.error ? this.extractErrorFields(options.error) : undefined;
|
|
252
|
+
const event = {
|
|
253
|
+
type: 'command',
|
|
254
|
+
sessionId: this.sessionId,
|
|
255
|
+
timestamp: new Date().toISOString(),
|
|
256
|
+
attributes: {
|
|
257
|
+
'command.name': name,
|
|
258
|
+
'command.duration_ms': durationMs,
|
|
259
|
+
'command.success': success,
|
|
260
|
+
'cli.version': getVersion(),
|
|
261
|
+
...(this.distinctId ? { 'workos.user_id': this.distinctId } : {}),
|
|
262
|
+
...(errorFields
|
|
263
|
+
? {
|
|
264
|
+
'command.error_type': errorFields.type,
|
|
265
|
+
'command.error_message': errorFields.message,
|
|
266
|
+
}
|
|
267
|
+
: {}),
|
|
268
|
+
...(options?.flags?.length ? { 'command.flags': options.flags.join(',') } : {}),
|
|
269
|
+
...(options?.reason ? { 'termination.reason': options.reason } : {}),
|
|
270
|
+
...(options?.errorCode ? { 'error.code': options.errorCode } : {}),
|
|
271
|
+
...(options?.apiContext?.status !== undefined ? { 'api.status': options.apiContext.status } : {}),
|
|
272
|
+
...(options?.apiContext?.code ? { 'api.code': options.apiContext.code } : {}),
|
|
273
|
+
...(options?.apiContext?.resource ? { 'api.resource': options.apiContext.resource } : {}),
|
|
274
|
+
...this.getEnvFingerprint(),
|
|
275
|
+
},
|
|
276
|
+
};
|
|
277
|
+
telemetryClient.queueEvent(event);
|
|
278
|
+
}
|
|
279
|
+
captureUnhandledCrash(error, options) {
|
|
280
|
+
if (!this.isEnabled())
|
|
281
|
+
return;
|
|
282
|
+
const { type, message } = this.extractErrorFields(error);
|
|
283
|
+
const event = {
|
|
284
|
+
type: 'crash',
|
|
285
|
+
sessionId: this.sessionId,
|
|
286
|
+
timestamp: new Date().toISOString(),
|
|
287
|
+
attributes: {
|
|
288
|
+
'crash.error_type': type,
|
|
289
|
+
'crash.error_message': message,
|
|
290
|
+
'crash.stack': sanitizeStack(error.stack),
|
|
291
|
+
...(options?.command ? { 'crash.command': options.command } : {}),
|
|
292
|
+
'cli.version': options?.version ?? getVersion(),
|
|
293
|
+
...(this.distinctId ? { 'workos.user_id': this.distinctId } : {}),
|
|
294
|
+
...this.getEnvFingerprint(),
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
telemetryClient.queueEvent(event);
|
|
298
|
+
}
|
|
115
299
|
async shutdown(status) {
|
|
116
|
-
if (!
|
|
300
|
+
if (!this.isEnabled())
|
|
117
301
|
return;
|
|
118
302
|
const duration = Date.now() - this.sessionStartTime.getTime();
|
|
119
303
|
// Filter out null/undefined tags
|
|
120
304
|
const extraAttributes = Object.fromEntries(Object.entries(this.tags).filter(([, v]) => v != null));
|
|
305
|
+
const envFingerprint = this.getEnvFingerprint();
|
|
121
306
|
const event = {
|
|
122
307
|
type: 'session.end',
|
|
123
308
|
sessionId: this.sessionId,
|
|
@@ -128,6 +313,8 @@ export class Analytics {
|
|
|
128
313
|
'installer.agent.iterations': this.agentIterations,
|
|
129
314
|
'installer.agent.tokens.input': this.totalInputTokens,
|
|
130
315
|
'installer.agent.tokens.output': this.totalOutputTokens,
|
|
316
|
+
...envFingerprint,
|
|
317
|
+
...(this.mode ? { 'installer.mode': this.mode } : {}),
|
|
131
318
|
...extraAttributes,
|
|
132
319
|
},
|
|
133
320
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../src/utils/analytics.ts"],"names":[],"mappings":"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","sourcesContent":["import { v4 as uuidv4 } from 'uuid';\nimport { debug } from './debug.js';\nimport { telemetryClient } from './telemetry-client.js';\nimport type {\n SessionStartEvent,\n SessionEndEvent,\n StepEvent,\n AgentToolEvent,\n AgentLLMEvent,\n} from './telemetry-types.js';\nimport { WORKOS_TELEMETRY_ENABLED } from '../lib/constants.js';\n\nexport class Analytics {\n private tags: Record<string, string | boolean | number | null | undefined> = {};\n private sessionId: string;\n private sessionStartTime: Date;\n private distinctId?: string;\n\n // Agent metrics tracking\n private totalInputTokens = 0;\n private totalOutputTokens = 0;\n private agentIterations = 0;\n\n constructor() {\n this.sessionId = uuidv4();\n this.sessionStartTime = new Date();\n this.tags = { $app_name: 'authkit-installer' };\n }\n\n setDistinctId(distinctId: string) {\n this.distinctId = distinctId;\n }\n\n setAccessToken(token: string) {\n telemetryClient.setAccessToken(token);\n }\n\n setGatewayUrl(url: string) {\n telemetryClient.setGatewayUrl(url);\n }\n\n setTag(key: string, value: string | boolean | number | null | undefined) {\n this.tags[key] = value;\n }\n\n capture(eventName: string, properties?: Record<string, unknown>) {\n if (!WORKOS_TELEMETRY_ENABLED) return;\n\n debug(`[Analytics] capture: ${eventName}`, properties);\n\n // Accumulate primitive values as tags for the session.end event\n if (properties) {\n for (const [key, value] of Object.entries(properties)) {\n if (['string', 'number', 'boolean'].includes(typeof value)) {\n this.tags[key] = value as string | number | boolean;\n }\n }\n }\n }\n\n captureException(error: Error, properties: Record<string, unknown> = {}) {\n if (!WORKOS_TELEMETRY_ENABLED) return;\n\n debug('[Analytics] captureException:', error.message, properties);\n this.tags['error.type'] = error.name;\n this.tags['error.message'] = error.message;\n }\n\n async getFeatureFlag(_flagKey: string): Promise<string | boolean | undefined> {\n // Feature flags not implemented yet\n return undefined;\n }\n\n sessionStart(mode: 'cli' | 'tui' | 'headless', version: string) {\n if (!WORKOS_TELEMETRY_ENABLED) return;\n\n const event: SessionStartEvent = {\n type: 'session.start',\n sessionId: this.sessionId,\n timestamp: this.sessionStartTime.toISOString(),\n attributes: {\n 'installer.version': version,\n 'installer.mode': mode,\n 'workos.user_id': this.distinctId,\n },\n };\n\n telemetryClient.queueEvent(event);\n }\n\n stepCompleted(name: string, durationMs: number, success: boolean, error?: Error) {\n if (!WORKOS_TELEMETRY_ENABLED) return;\n\n const event: StepEvent = {\n type: 'step',\n sessionId: this.sessionId,\n timestamp: new Date().toISOString(),\n name,\n durationMs,\n success,\n error: error ? { type: error.name, message: error.message } : undefined,\n };\n\n telemetryClient.queueEvent(event);\n }\n\n toolCalled(toolName: string, durationMs: number, success: boolean) {\n if (!WORKOS_TELEMETRY_ENABLED) return;\n\n const event: AgentToolEvent = {\n type: 'agent.tool',\n sessionId: this.sessionId,\n timestamp: new Date().toISOString(),\n toolName,\n durationMs,\n success,\n };\n\n telemetryClient.queueEvent(event);\n }\n\n llmRequest(model: string, inputTokens: number, outputTokens: number) {\n if (!WORKOS_TELEMETRY_ENABLED) return;\n\n this.totalInputTokens += inputTokens;\n this.totalOutputTokens += outputTokens;\n\n const event: AgentLLMEvent = {\n type: 'agent.llm',\n sessionId: this.sessionId,\n timestamp: new Date().toISOString(),\n model,\n inputTokens,\n outputTokens,\n };\n\n telemetryClient.queueEvent(event);\n }\n\n incrementAgentIterations() {\n this.agentIterations++;\n }\n\n async shutdown(status: 'success' | 'error' | 'cancelled') {\n if (!WORKOS_TELEMETRY_ENABLED) return;\n\n const duration = Date.now() - this.sessionStartTime.getTime();\n\n // Filter out null/undefined tags\n const extraAttributes = Object.fromEntries(Object.entries(this.tags).filter(([, v]) => v != null)) as Record<\n string,\n string | number | boolean\n >;\n\n const event: SessionEndEvent = {\n type: 'session.end',\n sessionId: this.sessionId,\n timestamp: new Date().toISOString(),\n attributes: {\n 'installer.outcome': status,\n 'installer.duration_ms': duration,\n 'installer.agent.iterations': this.agentIterations,\n 'installer.agent.tokens.input': this.totalInputTokens,\n 'installer.agent.tokens.output': this.totalOutputTokens,\n ...extraAttributes,\n },\n };\n\n telemetryClient.queueEvent(event);\n await telemetryClient.flush();\n }\n}\n\nexport const analytics = new Analytics();\n"]}
|
|
1
|
+
{"version":3,"file":"analytics.js","sourceRoot":"","sources":["../../src/utils/analytics.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,EAAE,IAAI,MAAM,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AACnC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAaxD,OAAO,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACjE,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AACvE,OAAO,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AACtF,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAClD,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAErE,MAAM,OAAO,SAAS;IACZ,IAAI,GAAiE,EAAE,CAAC;IACxE,SAAS,CAAS;IAClB,gBAAgB,CAAO;IACvB,UAAU,CAAU;IACpB,IAAI,CAA8B;IAClC,QAAQ,GAAa,MAAM,CAAC;IAEpC,yBAAyB;IACjB,gBAAgB,GAAG,CAAC,CAAC;IACrB,iBAAiB,GAAG,CAAC,CAAC;IACtB,eAAe,GAAG,CAAC,CAAC;IAE5B;QACE,IAAI,CAAC,SAAS,GAAG,MAAM,EAAE,CAAC;QAC1B,IAAI,CAAC,gBAAgB,GAAG,IAAI,IAAI,EAAE,CAAC;QACnC,IAAI,CAAC,IAAI,GAAG,EAAE,SAAS,EAAE,mBAAmB,EAAE,CAAC;IACjD,CAAC;IAED,aAAa,CAAC,UAAkB;QAC9B,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;IAED,cAAc,CAAC,KAAa;QAC1B,eAAe,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IACxC,CAAC;IAED,aAAa,CAAC,MAAc;QAC1B,eAAe,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;IACxC,CAAC;IAED,iBAAiB,CAAC,QAAgB,EAAE,UAAkB;QACpD,eAAe,CAAC,iBAAiB,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAC1D,CAAC;IAED;;;OAGG;IACH,WAAW,CAAC,IAAc;QACxB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;IACvB,CAAC;IAED,aAAa,CAAC,GAAW;QACvB,eAAe,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC;IACrC,CAAC;IAEO,SAAS;QACf,OAAO,kBAAkB,EAAE,CAAC;IAC9B,CAAC;IAED;;;;OAIG;IACH,iCAAiC;QAC/B,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YAAE,OAAO,IAAI,CAAC,QAAQ,CAAC;QAE5C,IAAI,CAAC,QAAQ,GAAG,MAAM,CAAC;QACvB,MAAM,KAAK,GAAG,cAAc,EAAE,CAAC;QAC/B,uEAAuE;QACvE,yEAAyE;QACzE,qEAAqE;QACrE,IAAI,KAAK,EAAE,WAAW,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,EAAE,CAAC;YACjD,eAAe,CAAC,cAAc,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;YAClD,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC;QACxB,CAAC;QACD,oDAAoD;QACpD,IAAI,KAAK,EAAE,MAAM,EAAE,CAAC;YAClB,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,MAAM,CAAC;QACjC,CAAC;QAED,kEAAkE;QAClE,2DAA2D;QAC3D,IAAI,CAAC;YACH,MAAM,GAAG,GAAG,oBAAoB,EAAE,CAAC;YACnC,IAAI,GAAG,IAAI,sBAAsB,CAAC,GAAG,CAAC,EAAE,CAAC;gBACvC,eAAe,CAAC,iBAAiB,CAAC,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,UAAU,CAAC,CAAC;gBAChE,qEAAqE;gBACrE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,aAAa,GAAG,CAAC,QAAQ,EAAE,CAAC;gBACjE,IAAI,IAAI,CAAC,QAAQ,KAAK,MAAM;oBAAE,IAAI,CAAC,QAAQ,GAAG,aAAa,CAAC;YAC9D,CAAC;iBAAM,IAAI,GAAG,EAAE,MAAM,IAAI,IAAI,CAAC,QAAQ,KAAK,MAAM,EAAE,CAAC;gBACnD,eAAe,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;gBAC1C,IAAI,GAAG,CAAC,QAAQ;oBAAE,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,OAAO,GAAG,CAAC,QAAQ,EAAE,CAAC;gBAC7E,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC;YAC5B,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,kDAAkD;QACpD,CAAC;QAED,sEAAsE;QACtE,gEAAgE;QAChE,IAAI,IAAI,CAAC,QAAQ,KAAK,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,CAAC;YAC3D,eAAe,CAAC,aAAa,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;YAC1D,IAAI,CAAC,QAAQ,GAAG,SAAS,CAAC;QAC5B,CAAC;QAED,OAAO,IAAI,CAAC,QAAQ,CAAC;IACvB,CAAC;IAED;;;OAGG;IACH,mBAAmB;QACjB,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YAAE,OAAO;QAE9B,eAAe,CAAC,aAAa,CAAC,eAAe,EAAE,CAAC,CAAC;QACjD,IAAI,CAAC,iCAAiC,EAAE,CAAC;IAC3C,CAAC;IAED,MAAM,CAAC,GAAW,EAAE,KAAmD;QACrE,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACzB,CAAC;IAED,OAAO,CAAC,SAAiB,EAAE,UAAoC;QAC7D,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YAAE,OAAO;QAE9B,KAAK,CAAC,wBAAwB,SAAS,EAAE,EAAE,UAAU,CAAC,CAAC;QAEvD,gEAAgE;QAChE,IAAI,UAAU,EAAE,CAAC;YACf,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;gBACtD,IAAI,CAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,OAAO,KAAK,CAAC,EAAE,CAAC;oBAC3D,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,KAAkC,CAAC;gBACtD,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,gBAAgB,CAAC,KAAY,EAAE,aAAsC,EAAE;QACrE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YAAE,OAAO;QAE9B,wEAAwE;QACxE,uEAAuE;QACvE,wBAAwB;QACxB,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;QACzD,KAAK,CAAC,+BAA+B,EAAE,OAAO,EAAE,UAAU,CAAC,CAAC;QAC5D,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,GAAG,IAAI,CAAC;QAC/B,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC,GAAG,OAAO,CAAC;IACvC,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,QAAgB;QACnC,oCAAoC;QACpC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,0EAA0E;IAClE,kBAAkB,CAAC,KAAY;QACrC,OAAO;YACL,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,OAAO,EAAE,eAAe,CAAC,KAAK,CAAC,OAAO,CAAC;SACxC,CAAC;IACJ,CAAC;IAEO,gBAAgB;QACtB,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc;YAAE,OAAO,gBAAgB,CAAC;QACxD,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS;YAAE,OAAO,WAAW,CAAC;QAC9C,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ;YAAE,OAAO,UAAU,CAAC;QAC5C,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS;YAAE,OAAO,WAAW,CAAC;QAC9C,IAAI,OAAO,CAAC,GAAG,CAAC,WAAW;YAAE,OAAO,SAAS,CAAC;QAC9C,OAAO,SAAS,CAAC;IACnB,CAAC;IAEO,iBAAiB;QACvB,IAAI,SAAiB,CAAC;QACtB,IAAI,CAAC;YACH,SAAS,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;QAC3B,CAAC;QAAC,MAAM,CAAC;YACP,SAAS,GAAG,SAAS,CAAC;QACxB,CAAC;QAED,MAAM,UAAU,GAAG,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAE3C,OAAO;YACL,WAAW,EAAE,WAAW,EAAE;YAC1B,WAAW,EAAE,IAAI,CAAC,QAAQ;YAC1B,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,gBAAgB,EAAE,SAAS;YAC3B,kBAAkB,EAAE,OAAO,CAAC,OAAO;YACnC,WAAW,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,SAAS,CAAC;YAC5E,QAAQ,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,IAAI,OAAO,CAAC,GAAG,CAAC,cAAc,IAAI,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC;YACxF,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,iBAAiB,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SACzD,CAAC;IACJ,CAAC;IAED,YAAY,CAAC,IAAgC,EAAE,OAAe;QAC5D,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YAAE,OAAO;QAE9B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QAEjB,MAAM,KAAK,GAAsB;YAC/B,IAAI,EAAE,eAAe;YACrB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,SAAS,EAAE,IAAI,CAAC,gBAAgB,CAAC,WAAW,EAAE;YAC9C,UAAU,EAAE;gBACV,mBAAmB,EAAE,OAAO;gBAC5B,gBAAgB,EAAE,IAAI;gBACtB,gBAAgB,EAAE,IAAI,CAAC,UAAU;gBACjC,GAAG,IAAI,CAAC,iBAAiB,EAAE;aAC5B;SACF,CAAC;QAEF,eAAe,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;IAED,aAAa,CAAC,IAAY,EAAE,UAAkB,EAAE,OAAgB,EAAE,KAAa;QAC7E,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YAAE,OAAO;QAE9B,MAAM,KAAK,GAAc;YACvB,IAAI,EAAE,MAAM;YACZ,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,IAAI;YACJ,cAAc,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC,CAAC,WAAW,EAAE;YAC/D,UAAU;YACV,OAAO;YACP,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS;SAC1D,CAAC;QAEF,eAAe,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;IAED,UAAU,CAAC,QAAgB,EAAE,UAAkB,EAAE,OAAgB;QAC/D,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YAAE,OAAO;QAE9B,MAAM,KAAK,GAAmB;YAC5B,IAAI,EAAE,YAAY;YAClB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,QAAQ;YACR,cAAc,EAAE,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC,CAAC,WAAW,EAAE;YAC/D,UAAU;YACV,OAAO;SACR,CAAC;QAEF,eAAe,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;IAED,UAAU,CAAC,KAAa,EAAE,WAAmB,EAAE,YAAoB;QACjE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YAAE,OAAO;QAE9B,IAAI,CAAC,gBAAgB,IAAI,WAAW,CAAC;QACrC,IAAI,CAAC,iBAAiB,IAAI,YAAY,CAAC;QAEvC,MAAM,KAAK,GAAkB;YAC3B,IAAI,EAAE,WAAW;YACjB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,KAAK;YACL,WAAW;YACX,YAAY;SACb,CAAC;QAEF,eAAe,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;IAED,wBAAwB;QACtB,IAAI,CAAC,eAAe,EAAE,CAAC;IACzB,CAAC;IAED,gBAAgB,CACd,IAAY,EACZ,UAAkB,EAClB,OAAgB,EAChB,OAMC;QAED,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YAAE,OAAO;QAE9B,MAAM,WAAW,GAAG,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,kBAAkB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;QAExF,MAAM,KAAK,GAAiB;YAC1B,IAAI,EAAE,SAAS;YACf,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,UAAU,EAAE;gBACV,cAAc,EAAE,IAAI;gBACpB,qBAAqB,EAAE,UAAU;gBACjC,iBAAiB,EAAE,OAAO;gBAC1B,aAAa,EAAE,UAAU,EAAE;gBAC3B,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACjE,GAAG,CAAC,WAAW;oBACb,CAAC,CAAC;wBACE,oBAAoB,EAAE,WAAW,CAAC,IAAI;wBACtC,uBAAuB,EAAE,WAAW,CAAC,OAAO;qBAC7C;oBACH,CAAC,CAAC,EAAE,CAAC;gBACP,GAAG,CAAC,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC/E,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC,EAAE,oBAAoB,EAAE,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACpE,GAAG,CAAC,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAClE,GAAG,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACjG,GAAG,CAAC,OAAO,EAAE,UAAU,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC7E,GAAG,CAAC,OAAO,EAAE,UAAU,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,cAAc,EAAE,OAAO,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACzF,GAAG,IAAI,CAAC,iBAAiB,EAAE;aAC5B;SACF,CAAC;QAEF,eAAe,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;IAED,qBAAqB,CAAC,KAAY,EAAE,OAAgD;QAClF,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YAAE,OAAO;QAE9B,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAC,kBAAkB,CAAC,KAAK,CAAC,CAAC;QAEzD,MAAM,KAAK,GAAe;YACxB,IAAI,EAAE,OAAO;YACb,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,UAAU,EAAE;gBACV,kBAAkB,EAAE,IAAI;gBACxB,qBAAqB,EAAE,OAAO;gBAC9B,aAAa,EAAE,aAAa,CAAC,KAAK,CAAC,KAAK,CAAC;gBACzC,GAAG,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,eAAe,EAAE,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACjE,aAAa,EAAE,OAAO,EAAE,OAAO,IAAI,UAAU,EAAE;gBAC/C,GAAG,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,IAAI,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACjE,GAAG,IAAI,CAAC,iBAAiB,EAAE;aAC5B;SACF,CAAC;QAEF,eAAe,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IACpC,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,MAAyC;QACtD,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE;YAAE,OAAO;QAE9B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,gBAAgB,CAAC,OAAO,EAAE,CAAC;QAE9D,iCAAiC;QACjC,MAAM,eAAe,GAAG,MAAM,CAAC,WAAW,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,IAAI,IAAI,CAAC,CAGhG,CAAC;QAEF,MAAM,cAAc,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAEhD,MAAM,KAAK,GAAoB;YAC7B,IAAI,EAAE,aAAa;YACnB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,UAAU,EAAE;gBACV,mBAAmB,EAAE,MAAM;gBAC3B,uBAAuB,EAAE,QAAQ;gBACjC,4BAA4B,EAAE,IAAI,CAAC,eAAe;gBAClD,8BAA8B,EAAE,IAAI,CAAC,gBAAgB;gBACrD,+BAA+B,EAAE,IAAI,CAAC,iBAAiB;gBACvD,GAAG,cAAc;gBACjB,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,gBAAgB,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACrD,GAAG,eAAe;aACnB;SACF,CAAC;QAEF,eAAe,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;QAClC,MAAM,eAAe,CAAC,KAAK,EAAE,CAAC;IAChC,CAAC;CACF;AAED,MAAM,CAAC,MAAM,SAAS,GAAG,IAAI,SAAS,EAAE,CAAC","sourcesContent":["import os from 'node:os';\nimport { basename } from 'node:path';\nimport { v4 as uuidv4 } from 'uuid';\nimport { debug } from './debug.js';\nimport { telemetryClient } from './telemetry-client.js';\nimport type {\n AuthMode,\n SessionStartEvent,\n SessionEndEvent,\n StepEvent,\n AgentToolEvent,\n AgentLLMEvent,\n CommandEvent,\n CrashEvent,\n TerminationReason,\n EnvFingerprint,\n} from './telemetry-types.js';\nimport { isTelemetryEnabled } from '../lib/preferences.js';\nimport { getTelemetryUrl, getVersion } from '../lib/settings.js';\nimport { getCredentials, isTokenExpired } from '../lib/credentials.js';\nimport { getActiveEnvironment, isUnclaimedEnvironment } from '../lib/config-store.js';\nimport { getDeviceId } from '../lib/device-id.js';\nimport { sanitizeMessage, sanitizeStack } from './crash-reporter.js';\n\nexport class Analytics {\n private tags: Record<string, string | boolean | number | null | undefined> = {};\n private sessionId: string;\n private sessionStartTime: Date;\n private distinctId?: string;\n private mode?: 'cli' | 'tui' | 'headless';\n private authMode: AuthMode = 'none';\n\n // Agent metrics tracking\n private totalInputTokens = 0;\n private totalOutputTokens = 0;\n private agentIterations = 0;\n\n constructor() {\n this.sessionId = uuidv4();\n this.sessionStartTime = new Date();\n this.tags = { $app_name: 'authkit-installer' };\n }\n\n setDistinctId(distinctId: string) {\n this.distinctId = distinctId;\n }\n\n setAccessToken(token: string) {\n telemetryClient.setAccessToken(token);\n }\n\n setApiKeyAuth(apiKey: string) {\n telemetryClient.setApiKeyAuth(apiKey);\n }\n\n setClaimTokenAuth(clientId: string, claimToken: string) {\n telemetryClient.setClaimTokenAuth(clientId, claimToken);\n }\n\n /**\n * Set the auth mode explicitly for special cases. Normal CLI flows should use\n * `configureAuthFromAvailableSources()` so transport and auth.mode stay aligned.\n */\n setAuthMode(mode: AuthMode) {\n this.authMode = mode;\n }\n\n setGatewayUrl(url: string) {\n telemetryClient.setGatewayUrl(url);\n }\n\n private isEnabled(): boolean {\n return isTelemetryEnabled();\n }\n\n /**\n * Configure telemetry transport and auth.mode from all available CLI auth\n * sources. Priority: stored JWT, unclaimed-environment claim token, active\n * environment API key, then WORKOS_API_KEY.\n */\n configureAuthFromAvailableSources(): AuthMode {\n if (!this.isEnabled()) return this.authMode;\n\n this.authMode = 'none';\n const creds = getCredentials();\n // Only treat the JWT as usable auth when it is still valid. An expired\n // access token would 401 against the telemetry guard and the event would\n // be dropped, so fall through to claim-token / api-key auth instead.\n if (creds?.accessToken && !isTokenExpired(creds)) {\n telemetryClient.setAccessToken(creds.accessToken);\n this.authMode = 'jwt';\n }\n // Preserve identity even when the token is expired.\n if (creds?.userId) {\n this.distinctId = creds.userId;\n }\n\n // Check for unclaimed environment — fall back to claim-token auth\n // so unclaimed users' telemetry still reaches the backend.\n try {\n const env = getActiveEnvironment();\n if (env && isUnclaimedEnvironment(env)) {\n telemetryClient.setClaimTokenAuth(env.clientId, env.claimToken);\n // Tag distinctId so unclaimed sessions are identifiable in analytics\n this.distinctId = this.distinctId ?? `unclaimed:${env.clientId}`;\n if (this.authMode === 'none') this.authMode = 'claim_token';\n } else if (env?.apiKey && this.authMode === 'none') {\n telemetryClient.setApiKeyAuth(env.apiKey);\n if (env.clientId) this.distinctId = this.distinctId ?? `env:${env.clientId}`;\n this.authMode = 'api_key';\n }\n } catch {\n // Config-store failure is non-fatal for telemetry\n }\n\n // WORKOS_API_KEY covers API-key-only users. Lowest priority — JWT and\n // claim-token auth have richer identity context when available.\n if (this.authMode === 'none' && process.env.WORKOS_API_KEY) {\n telemetryClient.setApiKeyAuth(process.env.WORKOS_API_KEY);\n this.authMode = 'api_key';\n }\n\n return this.authMode;\n }\n\n /**\n * Initialize telemetry for non-installer commands.\n * Sets telemetry URL from default config and loads auth credentials.\n */\n initForNonInstaller(): void {\n if (!this.isEnabled()) return;\n\n telemetryClient.setGatewayUrl(getTelemetryUrl());\n this.configureAuthFromAvailableSources();\n }\n\n setTag(key: string, value: string | boolean | number | null | undefined) {\n this.tags[key] = value;\n }\n\n capture(eventName: string, properties?: Record<string, unknown>) {\n if (!this.isEnabled()) return;\n\n debug(`[Analytics] capture: ${eventName}`, properties);\n\n // Accumulate primitive values as tags for the session.end event\n if (properties) {\n for (const [key, value] of Object.entries(properties)) {\n if (['string', 'number', 'boolean'].includes(typeof value)) {\n this.tags[key] = value as string | number | boolean;\n }\n }\n }\n }\n\n captureException(error: Error, properties: Record<string, unknown> = {}) {\n if (!this.isEnabled()) return;\n\n // Sanitize BEFORE logging — raw error.message can carry Bearer tokens /\n // sk_ keys / JWTs on auth-failure paths, which would surface in stdout\n // under WORKOS_DEBUG=1.\n const { type, message } = this.extractErrorFields(error);\n debug('[Analytics] captureException:', message, properties);\n this.tags['error.type'] = type;\n this.tags['error.message'] = message;\n }\n\n async getFeatureFlag(_flagKey: string): Promise<string | boolean | undefined> {\n // Feature flags not implemented yet\n return undefined;\n }\n\n /** All capture methods that record error details MUST go through this. */\n private extractErrorFields(error: Error): { type: string; message: string } {\n return {\n type: error.name,\n message: sanitizeMessage(error.message),\n };\n }\n\n private detectCiProvider(): string | undefined {\n if (process.env.GITHUB_ACTIONS) return 'github-actions';\n if (process.env.BUILDKITE) return 'buildkite';\n if (process.env.CIRCLECI) return 'circleci';\n if (process.env.GITLAB_CI) return 'gitlab-ci';\n if (process.env.JENKINS_URL) return 'jenkins';\n return undefined;\n }\n\n private getEnvFingerprint(): EnvFingerprint {\n let osVersion: string;\n try {\n osVersion = os.release();\n } catch {\n osVersion = 'unknown';\n }\n\n const ciProvider = this.detectCiProvider();\n\n return {\n 'device.id': getDeviceId(),\n 'auth.mode': this.authMode,\n 'env.os': process.platform,\n 'env.os_version': osVersion,\n 'env.node_version': process.version,\n 'env.shell': basename(process.env.SHELL ?? process.env.COMSPEC ?? 'unknown'),\n 'env.ci': Boolean(process.env.CI || process.env.GITHUB_ACTIONS || process.env.BUILDKITE),\n ...(ciProvider ? { 'env.ci_provider': ciProvider } : {}),\n };\n }\n\n sessionStart(mode: 'cli' | 'tui' | 'headless', version: string) {\n if (!this.isEnabled()) return;\n\n this.mode = mode;\n\n const event: SessionStartEvent = {\n type: 'session.start',\n sessionId: this.sessionId,\n timestamp: this.sessionStartTime.toISOString(),\n attributes: {\n 'installer.version': version,\n 'installer.mode': mode,\n 'workos.user_id': this.distinctId,\n ...this.getEnvFingerprint(),\n },\n };\n\n telemetryClient.queueEvent(event);\n }\n\n stepCompleted(name: string, durationMs: number, success: boolean, error?: Error) {\n if (!this.isEnabled()) return;\n\n const event: StepEvent = {\n type: 'step',\n sessionId: this.sessionId,\n timestamp: new Date().toISOString(),\n name,\n startTimestamp: new Date(Date.now() - durationMs).toISOString(),\n durationMs,\n success,\n error: error ? this.extractErrorFields(error) : undefined,\n };\n\n telemetryClient.queueEvent(event);\n }\n\n toolCalled(toolName: string, durationMs: number, success: boolean) {\n if (!this.isEnabled()) return;\n\n const event: AgentToolEvent = {\n type: 'agent.tool',\n sessionId: this.sessionId,\n timestamp: new Date().toISOString(),\n toolName,\n startTimestamp: new Date(Date.now() - durationMs).toISOString(),\n durationMs,\n success,\n };\n\n telemetryClient.queueEvent(event);\n }\n\n llmRequest(model: string, inputTokens: number, outputTokens: number) {\n if (!this.isEnabled()) return;\n\n this.totalInputTokens += inputTokens;\n this.totalOutputTokens += outputTokens;\n\n const event: AgentLLMEvent = {\n type: 'agent.llm',\n sessionId: this.sessionId,\n timestamp: new Date().toISOString(),\n model,\n inputTokens,\n outputTokens,\n };\n\n telemetryClient.queueEvent(event);\n }\n\n incrementAgentIterations() {\n this.agentIterations++;\n }\n\n emitCommandEvent(\n name: string,\n durationMs: number,\n success: boolean,\n options?: {\n error?: Error;\n flags?: string[];\n reason?: TerminationReason;\n errorCode?: string;\n apiContext?: { status?: number; code?: string; resource?: string };\n },\n ) {\n if (!this.isEnabled()) return;\n\n const errorFields = options?.error ? this.extractErrorFields(options.error) : undefined;\n\n const event: CommandEvent = {\n type: 'command',\n sessionId: this.sessionId,\n timestamp: new Date().toISOString(),\n attributes: {\n 'command.name': name,\n 'command.duration_ms': durationMs,\n 'command.success': success,\n 'cli.version': getVersion(),\n ...(this.distinctId ? { 'workos.user_id': this.distinctId } : {}),\n ...(errorFields\n ? {\n 'command.error_type': errorFields.type,\n 'command.error_message': errorFields.message,\n }\n : {}),\n ...(options?.flags?.length ? { 'command.flags': options.flags.join(',') } : {}),\n ...(options?.reason ? { 'termination.reason': options.reason } : {}),\n ...(options?.errorCode ? { 'error.code': options.errorCode } : {}),\n ...(options?.apiContext?.status !== undefined ? { 'api.status': options.apiContext.status } : {}),\n ...(options?.apiContext?.code ? { 'api.code': options.apiContext.code } : {}),\n ...(options?.apiContext?.resource ? { 'api.resource': options.apiContext.resource } : {}),\n ...this.getEnvFingerprint(),\n },\n };\n\n telemetryClient.queueEvent(event);\n }\n\n captureUnhandledCrash(error: Error, options?: { command?: string; version?: string }) {\n if (!this.isEnabled()) return;\n\n const { type, message } = this.extractErrorFields(error);\n\n const event: CrashEvent = {\n type: 'crash',\n sessionId: this.sessionId,\n timestamp: new Date().toISOString(),\n attributes: {\n 'crash.error_type': type,\n 'crash.error_message': message,\n 'crash.stack': sanitizeStack(error.stack),\n ...(options?.command ? { 'crash.command': options.command } : {}),\n 'cli.version': options?.version ?? getVersion(),\n ...(this.distinctId ? { 'workos.user_id': this.distinctId } : {}),\n ...this.getEnvFingerprint(),\n },\n };\n\n telemetryClient.queueEvent(event);\n }\n\n async shutdown(status: 'success' | 'error' | 'cancelled') {\n if (!this.isEnabled()) return;\n\n const duration = Date.now() - this.sessionStartTime.getTime();\n\n // Filter out null/undefined tags\n const extraAttributes = Object.fromEntries(Object.entries(this.tags).filter(([, v]) => v != null)) as Record<\n string,\n string | number | boolean\n >;\n\n const envFingerprint = this.getEnvFingerprint();\n\n const event: SessionEndEvent = {\n type: 'session.end',\n sessionId: this.sessionId,\n timestamp: new Date().toISOString(),\n attributes: {\n 'installer.outcome': status,\n 'installer.duration_ms': duration,\n 'installer.agent.iterations': this.agentIterations,\n 'installer.agent.tokens.input': this.totalInputTokens,\n 'installer.agent.tokens.output': this.totalOutputTokens,\n ...envFingerprint,\n ...(this.mode ? { 'installer.mode': this.mode } : {}),\n ...extraAttributes,\n },\n };\n\n telemetryClient.queueEvent(event);\n await telemetryClient.flush();\n }\n}\n\nexport const analytics = new Analytics();\n"]}
|
package/dist/utils/box.d.ts
CHANGED
|
@@ -1,5 +1,33 @@
|
|
|
1
1
|
import type chalk from 'chalk';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Word-wrap a string to a maximum visible width, preserving ANSI color.
|
|
4
|
+
*
|
|
5
|
+
* chalk emits self-closing color spans (e.g. `\x1b[36m…\x1b[39m`), so each
|
|
6
|
+
* colored fragment is atomic: we tokenize the input into whole colored spans
|
|
7
|
+
* and plain words, then greedily pack tokens into lines measured by their
|
|
8
|
+
* VISIBLE width. Because every token carries its own open+close codes, color
|
|
9
|
+
* never bleeds across a line break onto the border or padding.
|
|
10
|
+
*
|
|
11
|
+
* A single token wider than `maxWidth` (rare — only a very narrow terminal vs.
|
|
12
|
+
* a long unbroken word) overflows its own line rather than being split mid-span.
|
|
13
|
+
* Such an overflow can push the rendered box border past the terminal width;
|
|
14
|
+
* acceptable at standard widths. Note that a colored command produced by
|
|
15
|
+
* `formatWorkOSCommand` can be long (e.g. `npx workos@latest telemetry opt-out`)
|
|
16
|
+
* and stays a single unbreakable token by design.
|
|
17
|
+
*
|
|
18
|
+
* Limitation: a colored span is grouped atomically only when it is a single SGR
|
|
19
|
+
* layer (one open code + one close code, as `chalk.cyan('…')` emits). Stacked
|
|
20
|
+
* styles such as bold+color (`\x1b[1m\x1b[36m…\x1b[39m\x1b[22m`) or two adjacent
|
|
21
|
+
* spans with no separating space are not guaranteed to stay on one line and may
|
|
22
|
+
* leave a reset code mid-line. All current callers use single-color spans only.
|
|
23
|
+
*/
|
|
24
|
+
export declare function wrapAnsiAware(input: string, maxWidth: number): string[];
|
|
25
|
+
/**
|
|
26
|
+
* Render a bordered box to stderr, wrapping to the terminal width.
|
|
27
|
+
*
|
|
28
|
+
* When the content fits on one line it renders exactly as a single-line box
|
|
29
|
+
* (the historical behavior). When it would overflow the terminal, the content
|
|
30
|
+
* is word-wrapped (ANSI-aware) and the box grows to multiple lines so the
|
|
31
|
+
* border never breaks on a narrow terminal.
|
|
4
32
|
*/
|
|
5
33
|
export declare function renderStderrBox(inner: string, color: typeof chalk.yellow | typeof chalk.green): void;
|
package/dist/utils/box.js
CHANGED
|
@@ -1,13 +1,101 @@
|
|
|
1
1
|
import { stripAnsii } from './string.js';
|
|
2
|
+
/** Visible (printable) width of a string, ignoring ANSI escape sequences. */
|
|
3
|
+
function visibleWidth(str) {
|
|
4
|
+
return stripAnsii(str).length;
|
|
5
|
+
}
|
|
6
|
+
/** Terminal width for stderr output, falling back to stdout then 80 columns. */
|
|
7
|
+
function terminalWidth() {
|
|
8
|
+
return process.stderr.columns || process.stdout.columns || 80;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Word-wrap a string to a maximum visible width, preserving ANSI color.
|
|
12
|
+
*
|
|
13
|
+
* chalk emits self-closing color spans (e.g. `\x1b[36m…\x1b[39m`), so each
|
|
14
|
+
* colored fragment is atomic: we tokenize the input into whole colored spans
|
|
15
|
+
* and plain words, then greedily pack tokens into lines measured by their
|
|
16
|
+
* VISIBLE width. Because every token carries its own open+close codes, color
|
|
17
|
+
* never bleeds across a line break onto the border or padding.
|
|
18
|
+
*
|
|
19
|
+
* A single token wider than `maxWidth` (rare — only a very narrow terminal vs.
|
|
20
|
+
* a long unbroken word) overflows its own line rather than being split mid-span.
|
|
21
|
+
* Such an overflow can push the rendered box border past the terminal width;
|
|
22
|
+
* acceptable at standard widths. Note that a colored command produced by
|
|
23
|
+
* `formatWorkOSCommand` can be long (e.g. `npx workos@latest telemetry opt-out`)
|
|
24
|
+
* and stays a single unbreakable token by design.
|
|
25
|
+
*
|
|
26
|
+
* Limitation: a colored span is grouped atomically only when it is a single SGR
|
|
27
|
+
* layer (one open code + one close code, as `chalk.cyan('…')` emits). Stacked
|
|
28
|
+
* styles such as bold+color (`\x1b[1m\x1b[36m…\x1b[39m\x1b[22m`) or two adjacent
|
|
29
|
+
* spans with no separating space are not guaranteed to stay on one line and may
|
|
30
|
+
* leave a reset code mid-line. All current callers use single-color spans only.
|
|
31
|
+
*/
|
|
32
|
+
export function wrapAnsiAware(input, maxWidth) {
|
|
33
|
+
// A token is either: a full SGR-wrapped span (open code, content that may
|
|
34
|
+
// contain spaces, close code), a run of non-space/non-escape characters
|
|
35
|
+
// (a plain word), or a lone escape. Whitespace between tokens is dropped and
|
|
36
|
+
// re-inserted as single separating spaces.
|
|
37
|
+
const tokenRe = /\x1b\[[0-9;]*m[^\x1b]*?\x1b\[[0-9;]*m|[^\s\x1b]+|\x1b\[[0-9;]*m/g;
|
|
38
|
+
const tokens = input.match(tokenRe) ?? [];
|
|
39
|
+
const lines = [];
|
|
40
|
+
let line = '';
|
|
41
|
+
let lineWidth = 0;
|
|
42
|
+
for (const token of tokens) {
|
|
43
|
+
const tokenWidth = visibleWidth(token);
|
|
44
|
+
if (lineWidth === 0) {
|
|
45
|
+
line = token;
|
|
46
|
+
lineWidth = tokenWidth;
|
|
47
|
+
}
|
|
48
|
+
else if (lineWidth + 1 + tokenWidth <= maxWidth) {
|
|
49
|
+
line += ` ${token}`;
|
|
50
|
+
lineWidth += 1 + tokenWidth;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
lines.push(line);
|
|
54
|
+
line = token;
|
|
55
|
+
lineWidth = tokenWidth;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (line)
|
|
59
|
+
lines.push(line);
|
|
60
|
+
return lines.length > 0 ? lines : [''];
|
|
61
|
+
}
|
|
2
62
|
/**
|
|
3
|
-
* Render a
|
|
63
|
+
* Render a bordered box to stderr, wrapping to the terminal width.
|
|
64
|
+
*
|
|
65
|
+
* When the content fits on one line it renders exactly as a single-line box
|
|
66
|
+
* (the historical behavior). When it would overflow the terminal, the content
|
|
67
|
+
* is word-wrapped (ANSI-aware) and the box grows to multiple lines so the
|
|
68
|
+
* border never breaks on a narrow terminal.
|
|
4
69
|
*/
|
|
5
70
|
export function renderStderrBox(inner, color) {
|
|
6
|
-
const
|
|
7
|
-
const
|
|
71
|
+
const cols = terminalWidth();
|
|
72
|
+
const plainLen = visibleWidth(inner);
|
|
73
|
+
// Fast path: content (including its own padding spaces) fits within the
|
|
74
|
+
// terminal. Render the single-line box byte-for-byte as before.
|
|
75
|
+
if (plainLen <= cols - 4) {
|
|
76
|
+
const border = '─'.repeat(plainLen);
|
|
77
|
+
console.error('');
|
|
78
|
+
console.error(color(` ┌${border}┐`));
|
|
79
|
+
console.error(color(' │') + inner + color('│'));
|
|
80
|
+
console.error(color(` └${border}┘`));
|
|
81
|
+
console.error('');
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
// Wrap path: trim the caller's outer padding, wrap to the available width,
|
|
85
|
+
// then re-pad each line to a uniform inner width with one space of padding
|
|
86
|
+
// on each side. Layout per line: " │ " + text + " │" = text + 6 columns.
|
|
87
|
+
const content = inner.replace(/^[ \t]+/, '').replace(/[ \t]+$/, '');
|
|
88
|
+
const maxTextWidth = Math.max(1, cols - 6);
|
|
89
|
+
const wrapped = wrapAnsiAware(content, maxTextWidth);
|
|
90
|
+
// Snug the box to the longest wrapped line rather than the full terminal.
|
|
91
|
+
const textWidth = Math.max(...wrapped.map(visibleWidth));
|
|
92
|
+
const border = '─'.repeat(textWidth + 2);
|
|
8
93
|
console.error('');
|
|
9
94
|
console.error(color(` ┌${border}┐`));
|
|
10
|
-
|
|
95
|
+
for (const ln of wrapped) {
|
|
96
|
+
const pad = ' '.repeat(Math.max(0, textWidth - visibleWidth(ln)));
|
|
97
|
+
console.error(`${color(' │')} ${ln}${pad} ${color('│')}`);
|
|
98
|
+
}
|
|
11
99
|
console.error(color(` └${border}┘`));
|
|
12
100
|
console.error('');
|
|
13
101
|
}
|
package/dist/utils/box.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"box.js","sourceRoot":"","sources":["../../src/utils/box.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzC
|
|
1
|
+
{"version":3,"file":"box.js","sourceRoot":"","sources":["../../src/utils/box.ts"],"names":[],"mappings":"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","sourcesContent":["import type chalk from 'chalk';\nimport { stripAnsii } from './string.js';\n\n/** Visible (printable) width of a string, ignoring ANSI escape sequences. */\nfunction visibleWidth(str: string): number {\n return stripAnsii(str).length;\n}\n\n/** Terminal width for stderr output, falling back to stdout then 80 columns. */\nfunction terminalWidth(): number {\n return process.stderr.columns || process.stdout.columns || 80;\n}\n\n/**\n * Word-wrap a string to a maximum visible width, preserving ANSI color.\n *\n * chalk emits self-closing color spans (e.g. `\\x1b[36m…\\x1b[39m`), so each\n * colored fragment is atomic: we tokenize the input into whole colored spans\n * and plain words, then greedily pack tokens into lines measured by their\n * VISIBLE width. Because every token carries its own open+close codes, color\n * never bleeds across a line break onto the border or padding.\n *\n * A single token wider than `maxWidth` (rare — only a very narrow terminal vs.\n * a long unbroken word) overflows its own line rather than being split mid-span.\n * Such an overflow can push the rendered box border past the terminal width;\n * acceptable at standard widths. Note that a colored command produced by\n * `formatWorkOSCommand` can be long (e.g. `npx workos@latest telemetry opt-out`)\n * and stays a single unbreakable token by design.\n *\n * Limitation: a colored span is grouped atomically only when it is a single SGR\n * layer (one open code + one close code, as `chalk.cyan('…')` emits). Stacked\n * styles such as bold+color (`\\x1b[1m\\x1b[36m…\\x1b[39m\\x1b[22m`) or two adjacent\n * spans with no separating space are not guaranteed to stay on one line and may\n * leave a reset code mid-line. All current callers use single-color spans only.\n */\nexport function wrapAnsiAware(input: string, maxWidth: number): string[] {\n // A token is either: a full SGR-wrapped span (open code, content that may\n // contain spaces, close code), a run of non-space/non-escape characters\n // (a plain word), or a lone escape. Whitespace between tokens is dropped and\n // re-inserted as single separating spaces.\n const tokenRe = /\\x1b\\[[0-9;]*m[^\\x1b]*?\\x1b\\[[0-9;]*m|[^\\s\\x1b]+|\\x1b\\[[0-9;]*m/g;\n const tokens = input.match(tokenRe) ?? [];\n\n const lines: string[] = [];\n let line = '';\n let lineWidth = 0;\n\n for (const token of tokens) {\n const tokenWidth = visibleWidth(token);\n if (lineWidth === 0) {\n line = token;\n lineWidth = tokenWidth;\n } else if (lineWidth + 1 + tokenWidth <= maxWidth) {\n line += ` ${token}`;\n lineWidth += 1 + tokenWidth;\n } else {\n lines.push(line);\n line = token;\n lineWidth = tokenWidth;\n }\n }\n if (line) lines.push(line);\n return lines.length > 0 ? lines : [''];\n}\n\n/**\n * Render a bordered box to stderr, wrapping to the terminal width.\n *\n * When the content fits on one line it renders exactly as a single-line box\n * (the historical behavior). When it would overflow the terminal, the content\n * is word-wrapped (ANSI-aware) and the box grows to multiple lines so the\n * border never breaks on a narrow terminal.\n */\nexport function renderStderrBox(inner: string, color: typeof chalk.yellow | typeof chalk.green): void {\n const cols = terminalWidth();\n const plainLen = visibleWidth(inner);\n\n // Fast path: content (including its own padding spaces) fits within the\n // terminal. Render the single-line box byte-for-byte as before.\n if (plainLen <= cols - 4) {\n const border = '─'.repeat(plainLen);\n console.error('');\n console.error(color(` ┌${border}┐`));\n console.error(color(' │') + inner + color('│'));\n console.error(color(` └${border}┘`));\n console.error('');\n return;\n }\n\n // Wrap path: trim the caller's outer padding, wrap to the available width,\n // then re-pad each line to a uniform inner width with one space of padding\n // on each side. Layout per line: \" │ \" + text + \" │\" = text + 6 columns.\n const content = inner.replace(/^[ \\t]+/, '').replace(/[ \\t]+$/, '');\n const maxTextWidth = Math.max(1, cols - 6);\n const wrapped = wrapAnsiAware(content, maxTextWidth);\n\n // Snug the box to the longest wrapped line rather than the full terminal.\n const textWidth = Math.max(...wrapped.map(visibleWidth));\n const border = '─'.repeat(textWidth + 2);\n\n console.error('');\n console.error(color(` ┌${border}┐`));\n for (const ln of wrapped) {\n const pad = ' '.repeat(Math.max(0, textWidth - visibleWidth(ln)));\n console.error(`${color(' │')} ${ln}${pad} ${color('│')}`);\n }\n console.error(color(` └${border}┘`));\n console.error('');\n}\n"]}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { TerminationReason } from './telemetry-types.js';
|
|
2
|
+
export interface CliExitContext {
|
|
3
|
+
reason: TerminationReason;
|
|
4
|
+
errorCode?: string;
|
|
5
|
+
apiContext?: {
|
|
6
|
+
status?: number;
|
|
7
|
+
code?: string;
|
|
8
|
+
resource?: string;
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export declare class CliExit extends Error {
|
|
12
|
+
readonly exitCode: number;
|
|
13
|
+
readonly context?: CliExitContext | undefined;
|
|
14
|
+
constructor(exitCode: number, context?: CliExitContext | undefined);
|
|
15
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export class CliExit extends Error {
|
|
2
|
+
exitCode;
|
|
3
|
+
context;
|
|
4
|
+
constructor(exitCode, context) {
|
|
5
|
+
super(`CLI exit: code ${exitCode}`);
|
|
6
|
+
this.exitCode = exitCode;
|
|
7
|
+
this.context = context;
|
|
8
|
+
this.name = 'CliExit';
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=cli-exit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli-exit.js","sourceRoot":"","sources":["../../src/utils/cli-exit.ts"],"names":[],"mappings":"AAQA,MAAM,OAAO,OAAQ,SAAQ,KAAK;IAErB;IACA;IAFX,YACW,QAAgB,EAChB,OAAwB;QAEjC,KAAK,CAAC,kBAAkB,QAAQ,EAAE,CAAC,CAAC;QAH3B,aAAQ,GAAR,QAAQ,CAAQ;QAChB,YAAO,GAAP,OAAO,CAAiB;QAGjC,IAAI,CAAC,IAAI,GAAG,SAAS,CAAC;IACxB,CAAC;CACF","sourcesContent":["import type { TerminationReason } from './telemetry-types.js';\n\nexport interface CliExitContext {\n reason: TerminationReason;\n errorCode?: string;\n apiContext?: { status?: number; code?: string; resource?: string };\n}\n\nexport class CliExit extends Error {\n constructor(\n readonly exitCode: number,\n readonly context?: CliExitContext,\n ) {\n super(`CLI exit: code ${exitCode}`);\n this.name = 'CliExit';\n }\n}\n"]}
|
|
@@ -2,7 +2,7 @@ export declare const symbols: {
|
|
|
2
2
|
readonly success: "✓" | "+";
|
|
3
3
|
readonly error: "✗" | "x";
|
|
4
4
|
readonly warning: "!";
|
|
5
|
-
readonly info: "
|
|
5
|
+
readonly info: "ℹ" | "i";
|
|
6
6
|
readonly arrow: "→" | "->";
|
|
7
7
|
readonly bullet: "*" | "•";
|
|
8
8
|
readonly progressFilled: "#" | "▓";
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare const SKIP_TELEMETRY_COMMANDS: Set<string>;
|
|
2
|
+
export declare function resolveCanonicalName(parts: string[]): string;
|
|
3
|
+
/**
|
|
4
|
+
* Resolve the command name from raw argv for paths where yargs validation
|
|
5
|
+
* fails before middleware runs (e.g. a missing required argument).
|
|
6
|
+
*
|
|
7
|
+
* Returns the first token that resolves to a KNOWN top-level command. Tokens
|
|
8
|
+
* are matched against the command registry rather than trusting position, so
|
|
9
|
+
* an option value preceding the command (e.g. `--api-key sk_live_… org` or
|
|
10
|
+
* `--mode ci org`) can never be recorded as the command name — that would leak
|
|
11
|
+
* secrets/values into telemetry and explode cardinality. Anything that isn't a
|
|
12
|
+
* known command (typos, stray values, `--help`) returns 'root', which is
|
|
13
|
+
* skipped. Only the top-level command is recorded; later positionals can be
|
|
14
|
+
* user values (org names, emails, IDs), so they are never included.
|
|
15
|
+
*/
|
|
16
|
+
export declare function resolveCommandNameFromRawArgs(rawArgs: string[]): string;
|
|
17
|
+
export declare function extractUserFlags(rawArgs: string[]): string[];
|