@e2edev/e2e 0.6.1 → 0.8.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 +15 -11
- package/dist/agent/act-validation.d.ts +9 -3
- package/dist/agent/act-validation.d.ts.map +1 -1
- package/dist/agent/act-validation.js +23 -4
- package/dist/agent/act-validation.js.map +1 -1
- package/dist/agent/act.d.ts +2 -2
- package/dist/agent/act.d.ts.map +1 -1
- package/dist/agent/act.js +231 -41
- package/dist/agent/act.js.map +1 -1
- package/dist/agent/actions.d.ts +14 -14
- package/dist/agent/actions.d.ts.map +1 -1
- package/dist/agent/actions.js +12 -2
- package/dist/agent/actions.js.map +1 -1
- package/dist/agent/default-agent.d.ts +1 -1
- package/dist/agent/default-agent.d.ts.map +1 -1
- package/dist/agent/default-agent.js +19 -84
- package/dist/agent/default-agent.js.map +1 -1
- package/dist/agent/executor.d.ts +14 -0
- package/dist/agent/executor.d.ts.map +1 -1
- package/dist/agent/executor.js.map +1 -1
- package/dist/agent/index.d.ts.map +1 -1
- package/dist/agent/index.js +14 -6
- package/dist/agent/index.js.map +1 -1
- package/dist/agent/invocation.d.ts +1 -8
- package/dist/agent/invocation.d.ts.map +1 -1
- package/dist/agent/invocation.js +7 -51
- package/dist/agent/invocation.js.map +1 -1
- package/dist/agent/ledger.d.ts.map +1 -1
- package/dist/agent/ledger.js +10 -1
- package/dist/agent/ledger.js.map +1 -1
- package/dist/agent/loop-guards.d.ts +21 -0
- package/dist/agent/loop-guards.d.ts.map +1 -1
- package/dist/agent/loop-guards.js +48 -0
- package/dist/agent/loop-guards.js.map +1 -1
- package/dist/agent/model/sdk.d.ts +18 -0
- package/dist/agent/model/sdk.d.ts.map +1 -1
- package/dist/agent/model/sdk.js +10 -4
- package/dist/agent/model/sdk.js.map +1 -1
- package/dist/agent/observation-budget.d.ts +27 -0
- package/dist/agent/observation-budget.d.ts.map +1 -0
- package/dist/agent/observation-budget.js +43 -0
- package/dist/agent/observation-budget.js.map +1 -0
- package/dist/agent/observation.d.ts +40 -7
- package/dist/agent/observation.d.ts.map +1 -1
- package/dist/agent/observation.js +56 -11
- package/dist/agent/observation.js.map +1 -1
- package/dist/agent/phases.d.ts +1 -1
- package/dist/agent/phases.d.ts.map +1 -1
- package/dist/agent/phases.js +11 -13
- package/dist/agent/phases.js.map +1 -1
- package/dist/agent/primitives.d.ts +14 -2
- package/dist/agent/primitives.d.ts.map +1 -1
- package/dist/agent/primitives.js +98 -54
- package/dist/agent/primitives.js.map +1 -1
- package/dist/agent/screen-update.d.ts +47 -0
- package/dist/agent/screen-update.d.ts.map +1 -0
- package/dist/agent/screen-update.js +222 -0
- package/dist/agent/screen-update.js.map +1 -0
- package/dist/agent/step-cache.d.ts +46 -18
- package/dist/agent/step-cache.d.ts.map +1 -1
- package/dist/agent/step-cache.js +100 -60
- package/dist/agent/step-cache.js.map +1 -1
- package/dist/agent/tool-loop.d.ts.map +1 -1
- package/dist/agent/tool-loop.js +28 -4
- package/dist/agent/tool-loop.js.map +1 -1
- package/dist/cache/anchors.d.ts.map +1 -1
- package/dist/cache/anchors.js +21 -1
- package/dist/cache/anchors.js.map +1 -1
- package/dist/cache/decide.d.ts +13 -3
- package/dist/cache/decide.d.ts.map +1 -1
- package/dist/cache/decide.js +35 -0
- package/dist/cache/decide.js.map +1 -1
- package/dist/cache/relocate.d.ts +11 -1
- package/dist/cache/relocate.d.ts.map +1 -1
- package/dist/cache/relocate.js +48 -17
- package/dist/cache/relocate.js.map +1 -1
- package/dist/cache/store.d.ts.map +1 -1
- package/dist/cache/store.js +3 -14
- package/dist/cache/store.js.map +1 -1
- package/dist/cache/trace.d.ts +12 -0
- package/dist/cache/trace.d.ts.map +1 -1
- package/dist/cache/trace.js +21 -0
- package/dist/cache/trace.js.map +1 -1
- package/dist/cli/docs-url.d.ts +1 -1
- package/dist/cli/docs-url.d.ts.map +1 -1
- package/dist/cli/docs-url.js +1 -1
- package/dist/cli/docs-url.js.map +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +60 -9
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/init/engines.d.ts +2 -3
- package/dist/cli/init/engines.d.ts.map +1 -1
- package/dist/cli/init/engines.js +3 -4
- package/dist/cli/init/engines.js.map +1 -1
- package/dist/cli/init/scaffold.d.ts.map +1 -1
- package/dist/cli/init/scaffold.js +2 -0
- package/dist/cli/init/scaffold.js.map +1 -1
- package/dist/cli/signals.d.ts +3 -3
- package/dist/cli/signals.js +3 -3
- package/dist/cli/telemetry.d.ts +12 -0
- package/dist/cli/telemetry.d.ts.map +1 -0
- package/dist/cli/telemetry.js +40 -0
- package/dist/cli/telemetry.js.map +1 -0
- package/dist/collect/select.d.ts +2 -0
- package/dist/collect/select.d.ts.map +1 -1
- package/dist/collect/select.js +15 -11
- package/dist/collect/select.js.map +1 -1
- package/dist/config/agent.d.ts +6 -2
- package/dist/config/agent.d.ts.map +1 -1
- package/dist/config/agent.js +30 -3
- package/dist/config/agent.js.map +1 -1
- package/dist/config/diagnose.d.ts.map +1 -1
- package/dist/config/diagnose.js +1 -0
- package/dist/config/diagnose.js.map +1 -1
- package/dist/config/load.d.ts +2 -2
- package/dist/config/load.js +2 -2
- package/dist/config/resolve.d.ts +16 -4
- package/dist/config/resolve.d.ts.map +1 -1
- package/dist/config/resolve.js +107 -44
- package/dist/config/resolve.js.map +1 -1
- package/dist/engine/contract.d.ts +6 -1
- package/dist/engine/contract.d.ts.map +1 -1
- package/dist/engine/contract.js.map +1 -1
- package/dist/engine/index.d.ts +61 -6
- package/dist/engine/index.d.ts.map +1 -1
- package/dist/engine/index.js +25 -5
- package/dist/engine/index.js.map +1 -1
- package/dist/engine/session.d.ts.map +1 -1
- package/dist/engine/session.js +6 -0
- package/dist/engine/session.js.map +1 -1
- package/dist/engine/surface.d.ts +6 -2
- package/dist/engine/surface.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/internal/ai-trace.d.ts +1 -5
- package/dist/internal/ai-trace.d.ts.map +1 -1
- package/dist/internal/ai-trace.js +82 -11
- package/dist/internal/ai-trace.js.map +1 -1
- package/dist/internal/atomic-write.d.ts +17 -0
- package/dist/internal/atomic-write.d.ts.map +1 -0
- package/dist/internal/atomic-write.js +44 -0
- package/dist/internal/atomic-write.js.map +1 -0
- package/dist/internal/env.d.ts +6 -0
- package/dist/internal/env.d.ts.map +1 -0
- package/dist/internal/env.js +12 -0
- package/dist/internal/env.js.map +1 -0
- package/dist/internal/options.d.ts +9 -0
- package/dist/internal/options.d.ts.map +1 -0
- package/dist/internal/options.js +27 -0
- package/dist/internal/options.js.map +1 -0
- package/dist/locator/expression.js +1 -1
- package/dist/locator/expression.js.map +1 -1
- package/dist/locator/screen.d.ts.map +1 -1
- package/dist/locator/screen.js +4 -2
- package/dist/locator/screen.js.map +1 -1
- package/dist/report/build.d.ts +2 -1
- package/dist/report/build.d.ts.map +1 -1
- package/dist/report/build.js +11 -4
- package/dist/report/build.js.map +1 -1
- package/dist/report/list-model.d.ts +14 -5
- package/dist/report/list-model.d.ts.map +1 -1
- package/dist/report/list.d.ts +59 -5
- package/dist/report/list.d.ts.map +1 -1
- package/dist/report/list.js +171 -31
- package/dist/report/list.js.map +1 -1
- package/dist/report/running-tree.d.ts +7 -6
- package/dist/report/running-tree.d.ts.map +1 -1
- package/dist/report/running-tree.js +13 -8
- package/dist/report/running-tree.js.map +1 -1
- package/dist/report/write.d.ts +1 -1
- package/dist/report/write.d.ts.map +1 -1
- package/dist/report/write.js +3 -6
- package/dist/report/write.js.map +1 -1
- package/dist/run/artifacts.d.ts.map +1 -1
- package/dist/run/artifacts.js +20 -2
- package/dist/run/artifacts.js.map +1 -1
- package/dist/run/events.d.ts +73 -15
- package/dist/run/events.d.ts.map +1 -1
- package/dist/run/events.js +7 -7
- package/dist/run/events.js.map +1 -1
- package/dist/run/execute.d.ts +31 -5
- package/dist/run/execute.d.ts.map +1 -1
- package/dist/run/execute.js +73 -25
- package/dist/run/execute.js.map +1 -1
- package/dist/run/fixtures.d.ts +9 -1
- package/dist/run/fixtures.d.ts.map +1 -1
- package/dist/run/fixtures.js +5 -4
- package/dist/run/fixtures.js.map +1 -1
- package/dist/run/in-process.d.ts +1 -1
- package/dist/run/in-process.d.ts.map +1 -1
- package/dist/run/in-process.js +6 -3
- package/dist/run/in-process.js.map +1 -1
- package/dist/run/managed-process.d.ts +8 -2
- package/dist/run/managed-process.d.ts.map +1 -1
- package/dist/run/managed-process.js +12 -0
- package/dist/run/managed-process.js.map +1 -1
- package/dist/run/records.d.ts +15 -1
- package/dist/run/records.d.ts.map +1 -1
- package/dist/run/runner.d.ts +16 -7
- package/dist/run/runner.d.ts.map +1 -1
- package/dist/run/runner.js +182 -98
- package/dist/run/runner.js.map +1 -1
- package/dist/run/scheduler.d.ts +6 -6
- package/dist/run/scheduler.d.ts.map +1 -1
- package/dist/run/scheduler.js +54 -23
- package/dist/run/scheduler.js.map +1 -1
- package/dist/run/serial.d.ts +2 -4
- package/dist/run/serial.d.ts.map +1 -1
- package/dist/run/serial.js +4 -2
- package/dist/run/serial.js.map +1 -1
- package/dist/run/sessions.d.ts.map +1 -1
- package/dist/run/sessions.js +3 -4
- package/dist/run/sessions.js.map +1 -1
- package/dist/run/steps.d.ts +28 -1
- package/dist/run/steps.d.ts.map +1 -1
- package/dist/run/steps.js +14 -0
- package/dist/run/steps.js.map +1 -1
- package/dist/run/unit-runner.d.ts +7 -2
- package/dist/run/unit-runner.d.ts.map +1 -1
- package/dist/run/units.d.ts +8 -0
- package/dist/run/units.d.ts.map +1 -1
- package/dist/run/units.js +11 -0
- package/dist/run/units.js.map +1 -1
- package/dist/run/worker/entry.js +1 -0
- package/dist/run/worker/entry.js.map +1 -1
- package/dist/run/worker/handle.d.ts +1 -1
- package/dist/run/worker/handle.d.ts.map +1 -1
- package/dist/run/worker/handle.js +1 -1
- package/dist/run/worker/handle.js.map +1 -1
- package/dist/run/worker/protocol.d.ts +12 -1
- package/dist/run/worker/protocol.d.ts.map +1 -1
- package/dist/run/worker/protocol.js.map +1 -1
- package/dist/run/worker/session.d.ts +2 -0
- package/dist/run/worker/session.d.ts.map +1 -1
- package/dist/run/worker/session.js +5 -0
- package/dist/run/worker/session.js.map +1 -1
- package/dist/run/worker-models.d.ts +27 -0
- package/dist/run/worker-models.d.ts.map +1 -0
- package/dist/run/worker-models.js +49 -0
- package/dist/run/worker-models.js.map +1 -0
- package/dist/telemetry/environment.d.ts +33 -0
- package/dist/telemetry/environment.d.ts.map +1 -0
- package/dist/telemetry/environment.js +96 -0
- package/dist/telemetry/environment.js.map +1 -0
- package/dist/telemetry/events.d.ts +25 -0
- package/dist/telemetry/events.d.ts.map +1 -0
- package/dist/telemetry/events.js +107 -0
- package/dist/telemetry/events.js.map +1 -0
- package/dist/telemetry/posthog.d.ts +31 -0
- package/dist/telemetry/posthog.d.ts.map +1 -0
- package/dist/telemetry/posthog.js +29 -0
- package/dist/telemetry/posthog.js.map +1 -0
- package/dist/telemetry/project.d.ts +18 -0
- package/dist/telemetry/project.d.ts.map +1 -0
- package/dist/telemetry/project.js +55 -0
- package/dist/telemetry/project.js.map +1 -0
- package/dist/telemetry/store.d.ts +53 -0
- package/dist/telemetry/store.d.ts.map +1 -0
- package/dist/telemetry/store.js +153 -0
- package/dist/telemetry/store.js.map +1 -0
- package/dist/telemetry/telemetry.d.ts +89 -0
- package/dist/telemetry/telemetry.d.ts.map +1 -0
- package/dist/telemetry/telemetry.js +187 -0
- package/dist/telemetry/telemetry.js.map +1 -0
- package/dist/types.d.ts +103 -38
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -6
- package/skills/e2e/SKILL.md +0 -2
- package/skills/e2e/references/agent.md +33 -15
- package/skills/e2e/references/debugging.md +4 -2
- package/skills/e2e/references/running.md +7 -5
- package/skills/e2e/references/setup.md +16 -10
- package/skills/e2e/references/writing-tests.md +6 -5
- package/dist/internal/trace.d.ts +0 -18
- package/dist/internal/trace.d.ts.map +0 -1
- package/dist/internal/trace.js +0 -46
- package/dist/internal/trace.js.map +0 -1
- package/dist/run/public.d.ts +0 -19
- package/dist/run/public.d.ts.map +0 -1
- package/dist/run/public.js +0 -11
- package/dist/run/public.js.map +0 -1
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The anonymous machine facts every event carries: platform, architecture,
|
|
3
|
+
* resource class, container, CI vendor, the coding agent driving the shell,
|
|
4
|
+
* and the runtime versions. Each field is a closed enumeration, a number, or
|
|
5
|
+
* a version string; none is a path, a hostname, a username, or a value read
|
|
6
|
+
* from the project.
|
|
7
|
+
*/
|
|
8
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
9
|
+
import os from 'node:os';
|
|
10
|
+
import { isCiMode } from '../config/resolve.js';
|
|
11
|
+
import { envValue } from '../internal/env.js';
|
|
12
|
+
import { detectPackageManager } from '../internal/package-manager.js';
|
|
13
|
+
/** Environment variables a CI vendor sets, and the name we report for it. */
|
|
14
|
+
const CI_VENDORS = [
|
|
15
|
+
['GITHUB_ACTIONS', 'github-actions'],
|
|
16
|
+
['GITLAB_CI', 'gitlab'],
|
|
17
|
+
['CIRCLECI', 'circleci'],
|
|
18
|
+
['BUILDKITE', 'buildkite'],
|
|
19
|
+
['JENKINS_URL', 'jenkins'],
|
|
20
|
+
['TRAVIS', 'travis'],
|
|
21
|
+
['BITBUCKET_BUILD_NUMBER', 'bitbucket'],
|
|
22
|
+
['TF_BUILD', 'azure-pipelines'],
|
|
23
|
+
['TEAMCITY_VERSION', 'teamcity'],
|
|
24
|
+
['CODEBUILD_BUILD_ID', 'aws-codebuild'],
|
|
25
|
+
['VERCEL', 'vercel'],
|
|
26
|
+
['NETLIFY', 'netlify'],
|
|
27
|
+
['DRONE', 'drone'],
|
|
28
|
+
['SEMAPHORE', 'semaphore'],
|
|
29
|
+
['APPVEYOR', 'appveyor'],
|
|
30
|
+
['WOODPECKER', 'woodpecker'],
|
|
31
|
+
];
|
|
32
|
+
/**
|
|
33
|
+
* Environment variables coding agents set in the shells they spawn. The
|
|
34
|
+
* markers are the ones `@vercel/detect-agent` checks; the value never matters,
|
|
35
|
+
* only that the variable is set.
|
|
36
|
+
*/
|
|
37
|
+
const CODING_AGENTS = [
|
|
38
|
+
['CLAUDECODE', 'claude-code'],
|
|
39
|
+
['CLAUDE_CODE', 'claude-code'],
|
|
40
|
+
['CURSOR_AGENT', 'cursor'],
|
|
41
|
+
['CURSOR_TRACE_ID', 'cursor'],
|
|
42
|
+
['CODEX_SANDBOX', 'codex'],
|
|
43
|
+
['CODEX_THREAD_ID', 'codex'],
|
|
44
|
+
['CODEX_CI', 'codex'],
|
|
45
|
+
['GEMINI_CLI', 'gemini-cli'],
|
|
46
|
+
['COPILOT_GITHUB_TOKEN', 'copilot'],
|
|
47
|
+
['COPILOT_MODEL', 'copilot'],
|
|
48
|
+
['COPILOT_ALLOW_ALL', 'copilot'],
|
|
49
|
+
['OPENCODE_CLIENT', 'opencode'],
|
|
50
|
+
['AUGMENT_AGENT', 'augment'],
|
|
51
|
+
['ANTIGRAVITY_AGENT', 'antigravity'],
|
|
52
|
+
['REPL_ID', 'replit'],
|
|
53
|
+
['AI_AGENT', 'other'],
|
|
54
|
+
];
|
|
55
|
+
/** The name paired with the first marker variable that is set. */
|
|
56
|
+
function firstMarker(env, markers) {
|
|
57
|
+
return markers.find(([variable]) => envValue(env, variable) !== undefined)?.[1];
|
|
58
|
+
}
|
|
59
|
+
/** The first vendor whose marker is set; `unknown` when only `CI` is. */
|
|
60
|
+
export function ciName(env) {
|
|
61
|
+
return firstMarker(env, CI_VENDORS) ?? (isCiMode(env) ? 'unknown' : null);
|
|
62
|
+
}
|
|
63
|
+
function inDocker() {
|
|
64
|
+
if (existsSync('/.dockerenv'))
|
|
65
|
+
return true;
|
|
66
|
+
try {
|
|
67
|
+
return readFileSync('/proc/1/cgroup', 'utf8').includes('docker');
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function inWsl(env, release) {
|
|
74
|
+
return envValue(env, 'WSL_DISTRO_NAME') !== undefined || release.toLowerCase().includes('microsoft');
|
|
75
|
+
}
|
|
76
|
+
export function collectEnvironment(options) {
|
|
77
|
+
const { env } = options;
|
|
78
|
+
const release = os.release();
|
|
79
|
+
return {
|
|
80
|
+
os: os.platform(),
|
|
81
|
+
os_release: release,
|
|
82
|
+
arch: os.arch(),
|
|
83
|
+
cpu_count: os.cpus().length,
|
|
84
|
+
memory_gb: Math.round(os.totalmem() / 2 ** 30),
|
|
85
|
+
docker: inDocker(),
|
|
86
|
+
wsl: inWsl(env, release),
|
|
87
|
+
tty: process.stdout.isTTY === true,
|
|
88
|
+
ci: isCiMode(env),
|
|
89
|
+
ci_name: ciName(env),
|
|
90
|
+
coding_agent: firstMarker(env, CODING_AGENTS) ?? null,
|
|
91
|
+
node_version: process.versions.node,
|
|
92
|
+
package_manager: detectPackageManager(options.cwd, undefined, env),
|
|
93
|
+
e2e_version: options.version,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=environment.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"environment.js","sourceRoot":"","sources":["../../src/telemetry/environment.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,gCAAgC,CAAC;AAqBtE,6EAA6E;AAC7E,MAAM,UAAU,GAA2D;IACzE,CAAC,gBAAgB,EAAE,gBAAgB,CAAC;IACpC,CAAC,WAAW,EAAE,QAAQ,CAAC;IACvB,CAAC,UAAU,EAAE,UAAU,CAAC;IACxB,CAAC,WAAW,EAAE,WAAW,CAAC;IAC1B,CAAC,aAAa,EAAE,SAAS,CAAC;IAC1B,CAAC,QAAQ,EAAE,QAAQ,CAAC;IACpB,CAAC,wBAAwB,EAAE,WAAW,CAAC;IACvC,CAAC,UAAU,EAAE,iBAAiB,CAAC;IAC/B,CAAC,kBAAkB,EAAE,UAAU,CAAC;IAChC,CAAC,oBAAoB,EAAE,eAAe,CAAC;IACvC,CAAC,QAAQ,EAAE,QAAQ,CAAC;IACpB,CAAC,SAAS,EAAE,SAAS,CAAC;IACtB,CAAC,OAAO,EAAE,OAAO,CAAC;IAClB,CAAC,WAAW,EAAE,WAAW,CAAC;IAC1B,CAAC,UAAU,EAAE,UAAU,CAAC;IACxB,CAAC,YAAY,EAAE,YAAY,CAAC;CAC7B,CAAC;AAEF;;;;GAIG;AACH,MAAM,aAAa,GAA2D;IAC5E,CAAC,YAAY,EAAE,aAAa,CAAC;IAC7B,CAAC,aAAa,EAAE,aAAa,CAAC;IAC9B,CAAC,cAAc,EAAE,QAAQ,CAAC;IAC1B,CAAC,iBAAiB,EAAE,QAAQ,CAAC;IAC7B,CAAC,eAAe,EAAE,OAAO,CAAC;IAC1B,CAAC,iBAAiB,EAAE,OAAO,CAAC;IAC5B,CAAC,UAAU,EAAE,OAAO,CAAC;IACrB,CAAC,YAAY,EAAE,YAAY,CAAC;IAC5B,CAAC,sBAAsB,EAAE,SAAS,CAAC;IACnC,CAAC,eAAe,EAAE,SAAS,CAAC;IAC5B,CAAC,mBAAmB,EAAE,SAAS,CAAC;IAChC,CAAC,iBAAiB,EAAE,UAAU,CAAC;IAC/B,CAAC,eAAe,EAAE,SAAS,CAAC;IAC5B,CAAC,mBAAmB,EAAE,aAAa,CAAC;IACpC,CAAC,SAAS,EAAE,QAAQ,CAAC;IACrB,CAAC,UAAU,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF,kEAAkE;AAClE,SAAS,WAAW,CAAC,GAAsB,EAAE,OAA+C;IAC1F,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,KAAK,SAAS,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;AAClF,CAAC;AAED,yEAAyE;AACzE,MAAM,UAAU,MAAM,CAAC,GAAsB;IAC3C,OAAO,WAAW,CAAC,GAAG,EAAE,UAAU,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;AAC5E,CAAC;AAED,SAAS,QAAQ;IACf,IAAI,UAAU,CAAC,aAAa,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3C,IAAI,CAAC;QACH,OAAO,YAAY,CAAC,gBAAgB,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,SAAS,KAAK,CAAC,GAAsB,EAAE,OAAe;IACpD,OAAO,QAAQ,CAAC,GAAG,EAAE,iBAAiB,CAAC,KAAK,SAAS,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;AACvG,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,OAIlC;IACC,MAAM,EAAE,GAAG,EAAE,GAAG,OAAO,CAAC;IACxB,MAAM,OAAO,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC;IAC7B,OAAO;QACL,EAAE,EAAE,EAAE,CAAC,QAAQ,EAAE;QACjB,UAAU,EAAE,OAAO;QACnB,IAAI,EAAE,EAAE,CAAC,IAAI,EAAE;QACf,SAAS,EAAE,EAAE,CAAC,IAAI,EAAE,CAAC,MAAM;QAC3B,SAAS,EAAE,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,QAAQ,EAAE,GAAG,CAAC,IAAI,EAAE,CAAC;QAC9C,MAAM,EAAE,QAAQ,EAAE;QAClB,GAAG,EAAE,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC;QACxB,GAAG,EAAE,OAAO,CAAC,MAAM,CAAC,KAAK,KAAK,IAAI;QAClC,EAAE,EAAE,QAAQ,CAAC,GAAG,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC,GAAG,CAAC;QACpB,YAAY,EAAE,WAAW,CAAC,GAAG,EAAE,aAAa,CAAC,IAAI,IAAI;QACrD,YAAY,EAAE,OAAO,CAAC,QAAQ,CAAC,IAAI;QACnC,eAAe,EAAE,oBAAoB,CAAC,OAAO,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,CAAC;QAClE,WAAW,EAAE,OAAO,CAAC,OAAO;KAC7B,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The events e2e sends, built from facts the CLI already holds. Every
|
|
3
|
+
* property is a count, a duration, a version, or a short token. The builders
|
|
4
|
+
* copy no title, file, URL, instruction, message, or stack out of the report.
|
|
5
|
+
* The names a project declares for its engines, platforms, and model pass
|
|
6
|
+
* through when they are plain tokens, so a homegrown engine counts as itself;
|
|
7
|
+
* a name shaped like a path, a URL, or a sentence folds to `other`, and an
|
|
8
|
+
* error code that is not an upper-case token, the shape of every runner
|
|
9
|
+
* code, folds to `OTHER`. The unit tests hold the payload to that promise.
|
|
10
|
+
*/
|
|
11
|
+
import type { Report1Document } from '../report/build.ts';
|
|
12
|
+
import type { JsonValue } from '../types.ts';
|
|
13
|
+
export interface TelemetryEvent {
|
|
14
|
+
readonly name: string;
|
|
15
|
+
readonly properties: Readonly<Record<string, JsonValue>>;
|
|
16
|
+
/** When the event happened, RFC 3339; the flush time when absent. */
|
|
17
|
+
readonly at?: string;
|
|
18
|
+
}
|
|
19
|
+
/** One per CLI invocation: which command, with which flags. */
|
|
20
|
+
export declare const EVENT_CLI_SESSION = "e2e_cli_session";
|
|
21
|
+
/** One per `e2e run`, from the report the run wrote. */
|
|
22
|
+
export declare const EVENT_RUN_COMPLETED = "e2e_run_completed";
|
|
23
|
+
export declare function cliSessionEvent(command: string, flags: readonly string[]): TelemetryEvent;
|
|
24
|
+
export declare function runCompletedEvent(report: Report1Document, flags: readonly string[]): TelemetryEvent;
|
|
25
|
+
//# sourceMappingURL=events.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../../src/telemetry/events.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,KAAK,EAAE,eAAe,EAA2B,MAAM,oBAAoB,CAAC;AAEnF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;IACzD,qEAAqE;IACrE,QAAQ,CAAC,EAAE,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,+DAA+D;AAC/D,eAAO,MAAM,iBAAiB,oBAAoB,CAAC;AACnD,wDAAwD;AACxD,eAAO,MAAM,mBAAmB,sBAAsB,CAAC;AAavD,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,cAAc,CAEzF;AAiDD,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,SAAS,MAAM,EAAE,GAAG,cAAc,CA8CnG"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The events e2e sends, built from facts the CLI already holds. Every
|
|
3
|
+
* property is a count, a duration, a version, or a short token. The builders
|
|
4
|
+
* copy no title, file, URL, instruction, message, or stack out of the report.
|
|
5
|
+
* The names a project declares for its engines, platforms, and model pass
|
|
6
|
+
* through when they are plain tokens, so a homegrown engine counts as itself;
|
|
7
|
+
* a name shaped like a path, a URL, or a sentence folds to `other`, and an
|
|
8
|
+
* error code that is not an upper-case token, the shape of every runner
|
|
9
|
+
* code, folds to `OTHER`. The unit tests hold the payload to that promise.
|
|
10
|
+
*/
|
|
11
|
+
import { STEP_KINDS } from '../run/steps.js';
|
|
12
|
+
/** One per CLI invocation: which command, with which flags. */
|
|
13
|
+
export const EVENT_CLI_SESSION = 'e2e_cli_session';
|
|
14
|
+
/** One per `e2e run`, from the report the run wrote. */
|
|
15
|
+
export const EVENT_RUN_COMPLETED = 'e2e_run_completed';
|
|
16
|
+
const SEMVER = /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/u;
|
|
17
|
+
/**
|
|
18
|
+
* The shape of a name worth sending as it is: an engine name, a platform, or
|
|
19
|
+
* a public model id is a short token. A path, a URL, a sentence, or a
|
|
20
|
+
* fine-tuned or routed model id is not, and folds to `other`.
|
|
21
|
+
*/
|
|
22
|
+
const PLAIN_TOKEN = /^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/u;
|
|
23
|
+
/** The shape of every runner error code. A code shaped otherwise is a project's own and folds to `OTHER`. */
|
|
24
|
+
const ERROR_CODE = /^[A-Z][A-Z0-9_]{2,63}$/u;
|
|
25
|
+
const MAX_ERROR_CODES = 20;
|
|
26
|
+
export function cliSessionEvent(command, flags) {
|
|
27
|
+
return { name: EVENT_CLI_SESSION, properties: { command, flags: [...flags] } };
|
|
28
|
+
}
|
|
29
|
+
function plainToken(value) {
|
|
30
|
+
return PLAIN_TOKEN.test(value) ? value : 'other';
|
|
31
|
+
}
|
|
32
|
+
/** `playwright@0.6.1`, and a project's own engine the same way; a name that is not a plain token is `other` alone. */
|
|
33
|
+
function engineLabel(engine) {
|
|
34
|
+
const name = plainToken(engine.name);
|
|
35
|
+
if (name === 'other')
|
|
36
|
+
return name;
|
|
37
|
+
return `${name}@${SEMVER.test(engine.version) ? engine.version : 'unversioned'}`;
|
|
38
|
+
}
|
|
39
|
+
function unique(values) {
|
|
40
|
+
return [...new Set(values)].toSorted();
|
|
41
|
+
}
|
|
42
|
+
/** Every scope the run recorded, across plain results and serial groups. */
|
|
43
|
+
function* scopes(report) {
|
|
44
|
+
for (const result of report.run.results)
|
|
45
|
+
yield* result.attempts;
|
|
46
|
+
for (const group of report.run.serialGroups) {
|
|
47
|
+
for (const attempt of group.attempts) {
|
|
48
|
+
yield attempt;
|
|
49
|
+
yield* attempt.members;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
/** The scope's own errors, then its steps'. */
|
|
54
|
+
function errorsOf(scope) {
|
|
55
|
+
return [
|
|
56
|
+
...(scope.error === undefined ? [] : [scope.error]),
|
|
57
|
+
...scope.secondaryErrors,
|
|
58
|
+
...(scope.steps ?? []).flatMap((step) => (step.error === undefined ? [] : [step.error])),
|
|
59
|
+
];
|
|
60
|
+
}
|
|
61
|
+
function durationMs(startedAt, finishedAt) {
|
|
62
|
+
const duration = Date.parse(finishedAt) - Date.parse(startedAt);
|
|
63
|
+
return Number.isFinite(duration) ? Math.max(0, duration) : null;
|
|
64
|
+
}
|
|
65
|
+
export function runCompletedEvent(report, flags) {
|
|
66
|
+
const { run } = report;
|
|
67
|
+
const recorded = [...scopes(report)];
|
|
68
|
+
const steps = recorded.flatMap((scope) => scope.steps ?? []);
|
|
69
|
+
const models = steps.flatMap((step) => (step.model === undefined ? [] : [step.model]));
|
|
70
|
+
const first = models[0];
|
|
71
|
+
const codes = unique([...run.errors, ...recorded.flatMap(errorsOf)].map((error) => (ERROR_CODE.test(error.code) ? error.code : 'OTHER')));
|
|
72
|
+
return {
|
|
73
|
+
name: EVENT_RUN_COMPLETED,
|
|
74
|
+
at: run.finishedAt,
|
|
75
|
+
properties: {
|
|
76
|
+
status: run.status,
|
|
77
|
+
exit_code: run.exitCode,
|
|
78
|
+
duration_ms: durationMs(run.startedAt, run.finishedAt),
|
|
79
|
+
flags: [...flags],
|
|
80
|
+
tests_discovered: run.summary.discovered,
|
|
81
|
+
tests_selected: run.summary.selected,
|
|
82
|
+
tests_executed: run.summary.executed,
|
|
83
|
+
tests_passed: run.summary.passed,
|
|
84
|
+
tests_failed: run.summary.failed,
|
|
85
|
+
tests_flaky: run.summary.flaky,
|
|
86
|
+
tests_skipped: run.summary.skipped,
|
|
87
|
+
targets: run.targets.length,
|
|
88
|
+
platforms: unique(run.targets.map((target) => plainToken(target.platform))),
|
|
89
|
+
engines: unique(run.targets.map((target) => engineLabel(target.engine))),
|
|
90
|
+
steps_total: steps.length,
|
|
91
|
+
...Object.fromEntries(STEP_KINDS.map((kind) => [`steps_${kind}`, steps.filter((step) => step.kind === kind).length])),
|
|
92
|
+
agent_steps_replayed: steps.filter((step) => step.cache?.mode === 'self-finalized').length,
|
|
93
|
+
agent_steps_partial: steps.filter((step) => step.cache?.mode === 'agent-concluded').length,
|
|
94
|
+
agent_steps_missed: steps.filter((step) => step.cache?.mode === 'missed').length,
|
|
95
|
+
agent_steps_vision: steps.filter((step) => step.visionInput === true).length,
|
|
96
|
+
model_provider: first?.provider ?? null,
|
|
97
|
+
model_id: first === undefined ? null : plainToken(first.model),
|
|
98
|
+
model_calls: models.reduce((total, model) => total + model.calls, 0),
|
|
99
|
+
model_tokens: run.usage.modelTokens,
|
|
100
|
+
estimated_cost_usd: run.usage.estimatedCostUsd ?? null,
|
|
101
|
+
artifact_bytes: run.usage.artifactBytes,
|
|
102
|
+
errors: run.errors.length,
|
|
103
|
+
error_codes: codes.slice(0, MAX_ERROR_CODES),
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/telemetry/events.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport: one POST of every event a CLI invocation produced to PostHog's
|
|
3
|
+
* batch endpoint. The key is the project's public, write-only ingestion key,
|
|
4
|
+
* the kind websites embed in their pages; it can add events and nothing else.
|
|
5
|
+
*
|
|
6
|
+
* The request is bounded and quiet. It aborts when the caller's signal does,
|
|
7
|
+
* never retries, and swallows every failure: telemetry that is lost is
|
|
8
|
+
* telemetry, telemetry that delays or fails a command is a bug.
|
|
9
|
+
*/
|
|
10
|
+
import type { JsonValue } from '../types.ts';
|
|
11
|
+
/** The PostHog Cloud EU ingestion host. */
|
|
12
|
+
export declare const POSTHOG_HOST = "https://eu.i.posthog.com";
|
|
13
|
+
/** The e2e project's ingestion key: public and write-only, like the key posthog-js embeds in a page. */
|
|
14
|
+
export declare const POSTHOG_PROJECT_KEY = "phc_rT8hpREuSj5bRGPHP6KLKNuf4kfwhP4FEf6ahHHV2ZgH";
|
|
15
|
+
/** One item of the batch, exactly as PostHog receives it; `distinct_id` is a property. */
|
|
16
|
+
export interface PostHogEvent {
|
|
17
|
+
readonly event: string;
|
|
18
|
+
/** RFC 3339. */
|
|
19
|
+
readonly timestamp: string;
|
|
20
|
+
readonly properties: Readonly<Record<string, JsonValue>> & {
|
|
21
|
+
readonly distinct_id: string;
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export interface PostBatchOptions {
|
|
25
|
+
/** Aborts the request: the caller's one deadline for everything telemetry does after the command. */
|
|
26
|
+
readonly signal: AbortSignal;
|
|
27
|
+
readonly fetch: typeof fetch;
|
|
28
|
+
}
|
|
29
|
+
/** Sends the batch; resolves to whether PostHog accepted it. */
|
|
30
|
+
export declare function postBatch(events: readonly PostHogEvent[], options: PostBatchOptions): Promise<boolean>;
|
|
31
|
+
//# sourceMappingURL=posthog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"posthog.d.ts","sourceRoot":"","sources":["../../src/telemetry/posthog.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,2CAA2C;AAC3C,eAAO,MAAM,YAAY,6BAA6B,CAAC;AAEvD,wGAAwG;AACxG,eAAO,MAAM,mBAAmB,qDAAqD,CAAC;AAEtF,0FAA0F;AAC1F,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,gBAAgB;IAChB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC,GAAG;QAAE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;KAAE,CAAC;CAC7F;AAED,MAAM,WAAW,gBAAgB;IAC/B,qGAAqG;IACrG,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,OAAO,KAAK,CAAC;CAC9B;AAED,gEAAgE;AAChE,wBAAsB,SAAS,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,EAAE,OAAO,EAAE,gBAAgB,GAAG,OAAO,CAAC,OAAO,CAAC,CAY5G"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transport: one POST of every event a CLI invocation produced to PostHog's
|
|
3
|
+
* batch endpoint. The key is the project's public, write-only ingestion key,
|
|
4
|
+
* the kind websites embed in their pages; it can add events and nothing else.
|
|
5
|
+
*
|
|
6
|
+
* The request is bounded and quiet. It aborts when the caller's signal does,
|
|
7
|
+
* never retries, and swallows every failure: telemetry that is lost is
|
|
8
|
+
* telemetry, telemetry that delays or fails a command is a bug.
|
|
9
|
+
*/
|
|
10
|
+
/** The PostHog Cloud EU ingestion host. */
|
|
11
|
+
export const POSTHOG_HOST = 'https://eu.i.posthog.com';
|
|
12
|
+
/** The e2e project's ingestion key: public and write-only, like the key posthog-js embeds in a page. */
|
|
13
|
+
export const POSTHOG_PROJECT_KEY = 'phc_rT8hpREuSj5bRGPHP6KLKNuf4kfwhP4FEf6ahHHV2ZgH';
|
|
14
|
+
/** Sends the batch; resolves to whether PostHog accepted it. */
|
|
15
|
+
export async function postBatch(events, options) {
|
|
16
|
+
try {
|
|
17
|
+
const response = await options.fetch(`${POSTHOG_HOST}/batch/`, {
|
|
18
|
+
method: 'POST',
|
|
19
|
+
headers: { 'content-type': 'application/json' },
|
|
20
|
+
body: JSON.stringify({ api_key: POSTHOG_PROJECT_KEY, batch: events }),
|
|
21
|
+
signal: options.signal,
|
|
22
|
+
});
|
|
23
|
+
return response.ok;
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=posthog.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"posthog.js","sourceRoot":"","sources":["../../src/telemetry/posthog.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,2CAA2C;AAC3C,MAAM,CAAC,MAAM,YAAY,GAAG,0BAA0B,CAAC;AAEvD,wGAAwG;AACxG,MAAM,CAAC,MAAM,mBAAmB,GAAG,kDAAkD,CAAC;AAgBtF,gEAAgE;AAChE,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAA+B,EAAE,OAAyB;IACxF,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,KAAK,CAAC,GAAG,YAAY,SAAS,EAAE;YAC7D,MAAM,EAAE,MAAM;YACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;YAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,OAAO,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;YACrE,MAAM,EAAE,OAAO,CAAC,MAAM;SACvB,CAAC,CAAC;QACH,OAAO,QAAQ,CAAC,EAAE,CAAC;IACrB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The anonymous project id. Feature counts need to tell "one project run
|
|
3
|
+
* many times" from "many projects", and a per-machine id cannot: the same
|
|
4
|
+
* repository on two laptops would count twice, and one laptop with ten
|
|
5
|
+
* projects would count once.
|
|
6
|
+
*
|
|
7
|
+
* Inside a git repository the id is the SHA-256 of the repository's root
|
|
8
|
+
* commit, which every clone shares and no one can produce without the
|
|
9
|
+
* repository itself; a commit hash carries no name, path, or remote. A
|
|
10
|
+
* shallow clone has no root to offer: its boundary commits pose as roots and
|
|
11
|
+
* move with every fetch, so it is treated like a directory outside git.
|
|
12
|
+
* There, the project root path is hashed together with the machine's local
|
|
13
|
+
* salt, so the id is stable on that machine and unrecoverable anywhere else.
|
|
14
|
+
* In CI there is no salt, so a runner without a full history has no id: its
|
|
15
|
+
* working directory is not a project.
|
|
16
|
+
*/
|
|
17
|
+
export declare function anonymousProjectId(projectRoot: string, salt: string | undefined): Promise<string | undefined>;
|
|
18
|
+
//# sourceMappingURL=project.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../src/telemetry/project.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAkCH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE,MAAM,GAAG,SAAS,GACvB,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAK7B"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The anonymous project id. Feature counts need to tell "one project run
|
|
3
|
+
* many times" from "many projects", and a per-machine id cannot: the same
|
|
4
|
+
* repository on two laptops would count twice, and one laptop with ten
|
|
5
|
+
* projects would count once.
|
|
6
|
+
*
|
|
7
|
+
* Inside a git repository the id is the SHA-256 of the repository's root
|
|
8
|
+
* commit, which every clone shares and no one can produce without the
|
|
9
|
+
* repository itself; a commit hash carries no name, path, or remote. A
|
|
10
|
+
* shallow clone has no root to offer: its boundary commits pose as roots and
|
|
11
|
+
* move with every fetch, so it is treated like a directory outside git.
|
|
12
|
+
* There, the project root path is hashed together with the machine's local
|
|
13
|
+
* salt, so the id is stable on that machine and unrecoverable anywhere else.
|
|
14
|
+
* In CI there is no salt, so a runner without a full history has no id: its
|
|
15
|
+
* working directory is not a project.
|
|
16
|
+
*/
|
|
17
|
+
import { execFile } from 'node:child_process';
|
|
18
|
+
import { promisify } from 'node:util';
|
|
19
|
+
import { sha256Hex } from '../internal/ids.js';
|
|
20
|
+
const execFileAsync = promisify(execFile);
|
|
21
|
+
const COMMIT_HASH = /^[a-f0-9]{40,64}$/u;
|
|
22
|
+
/** One git command's stdout; undefined when git is missing, slow, or refuses. */
|
|
23
|
+
async function git(cwd, args) {
|
|
24
|
+
try {
|
|
25
|
+
const { stdout } = await execFileAsync('git', args, { cwd, timeout: 1_000, windowsHide: true, encoding: 'utf8' });
|
|
26
|
+
return stdout;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
/** The lexically first root commit reachable from HEAD; undefined outside git and in a shallow clone. */
|
|
33
|
+
async function rootCommit(cwd) {
|
|
34
|
+
const [shallow, roots] = await Promise.all([
|
|
35
|
+
git(cwd, ['rev-parse', '--is-shallow-repository']),
|
|
36
|
+
git(cwd, ['rev-list', '--max-parents=0', 'HEAD']),
|
|
37
|
+
]);
|
|
38
|
+
// Only an explicit `false` counts: a git too old for the flag echoes it back instead.
|
|
39
|
+
if (shallow?.trim() !== 'false' || roots === undefined)
|
|
40
|
+
return undefined;
|
|
41
|
+
return roots
|
|
42
|
+
.split('\n')
|
|
43
|
+
.map((line) => line.trim())
|
|
44
|
+
.filter((line) => COMMIT_HASH.test(line))
|
|
45
|
+
.toSorted()[0];
|
|
46
|
+
}
|
|
47
|
+
export async function anonymousProjectId(projectRoot, salt) {
|
|
48
|
+
const commit = await rootCommit(projectRoot);
|
|
49
|
+
if (commit !== undefined)
|
|
50
|
+
return sha256Hex(`git\n${commit}`);
|
|
51
|
+
if (salt === undefined)
|
|
52
|
+
return undefined;
|
|
53
|
+
return sha256Hex(`path\n${salt}\n${projectRoot}`);
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=project.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/telemetry/project.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAC;AAE/C,MAAM,aAAa,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;AAC1C,MAAM,WAAW,GAAG,oBAAoB,CAAC;AAEzC,iFAAiF;AACjF,KAAK,UAAU,GAAG,CAAC,GAAW,EAAE,IAAuB;IACrD,IAAI,CAAC;QACH,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,aAAa,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,CAAC;QAClH,OAAO,MAAM,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAC;IACnB,CAAC;AACH,CAAC;AAED,yGAAyG;AACzG,KAAK,UAAU,UAAU,CAAC,GAAW;IACnC,MAAM,CAAC,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QACzC,GAAG,CAAC,GAAG,EAAE,CAAC,WAAW,EAAE,yBAAyB,CAAC,CAAC;QAClD,GAAG,CAAC,GAAG,EAAE,CAAC,UAAU,EAAE,iBAAiB,EAAE,MAAM,CAAC,CAAC;KAClD,CAAC,CAAC;IACH,sFAAsF;IACtF,IAAI,OAAO,EAAE,IAAI,EAAE,KAAK,OAAO,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACzE,OAAO,KAAK;SACT,KAAK,CAAC,IAAI,CAAC;SACX,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;SAC1B,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;SACxC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC;AACnB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,WAAmB,EACnB,IAAwB;IAExB,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,WAAW,CAAC,CAAC;IAC7C,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC,QAAQ,MAAM,EAAE,CAAC,CAAC;IAC7D,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IACzC,OAAO,SAAS,CAAC,SAAS,IAAI,KAAK,WAAW,EAAE,CAAC,CAAC;AACpD,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The user-level telemetry preferences: `~/.config/e2e/telemetry.json`
|
|
3
|
+
* (`$XDG_CONFIG_HOME/e2e` when set, `%APPDATA%\e2e` on Windows). The file
|
|
4
|
+
* holds the opt-out, the random id every event from this machine is
|
|
5
|
+
* attributed to, the salt that keeps a hashed path unrecoverable, and which
|
|
6
|
+
* notice was shown when. Nothing in it names the person or the machine: the
|
|
7
|
+
* id and the salt are random bytes, generated here and never derived.
|
|
8
|
+
*
|
|
9
|
+
* The store fails closed. `open` completes the file in one write when the id
|
|
10
|
+
* or the salt is missing, and when that write cannot happen there is no
|
|
11
|
+
* store: telemetry treats the absence as an opt-out rather than sending
|
|
12
|
+
* events it could neither attribute nor de-duplicate. After `open`, only the
|
|
13
|
+
* user's own choice and the notice mark write, so an ordinary command never
|
|
14
|
+
* rewrites the file while another process may be saving a choice to it.
|
|
15
|
+
*/
|
|
16
|
+
/** The preferences file inside a config directory. */
|
|
17
|
+
export declare function preferencesPath(directory: string): string;
|
|
18
|
+
/** Where the preferences live for this platform and environment. */
|
|
19
|
+
export declare function telemetryConfigDir(env?: NodeJS.ProcessEnv, platform?: NodeJS.Platform): string;
|
|
20
|
+
export declare class TelemetryStore {
|
|
21
|
+
/** The preferences file, for messages that tell the user where a choice was saved. */
|
|
22
|
+
readonly path: string;
|
|
23
|
+
private preferences;
|
|
24
|
+
private constructor();
|
|
25
|
+
/**
|
|
26
|
+
* Opens the store in `directory`. A complete file is only read, so a
|
|
27
|
+
* read-only preferences file keeps working. A missing file, a file that is
|
|
28
|
+
* not JSON, or one without an id or a salt is completed in one write that
|
|
29
|
+
* also creates the directory; a torn write must not turn into a permanent
|
|
30
|
+
* opt-in or opt-out either way. Returns undefined when that write fails,
|
|
31
|
+
* which the caller reads as off.
|
|
32
|
+
*/
|
|
33
|
+
static open(directory: string): TelemetryStore | undefined;
|
|
34
|
+
/** The user's choice; unset means participating. */
|
|
35
|
+
get enabled(): boolean;
|
|
36
|
+
/** The random per-machine id every event from this machine is attributed to. */
|
|
37
|
+
get anonymousId(): string;
|
|
38
|
+
/** The local salt that keeps a hashed project path unrecoverable; never leaves the machine. */
|
|
39
|
+
get pathSalt(): string;
|
|
40
|
+
/** Saves the choice; false when the file could not be written and the choice stayed in memory. */
|
|
41
|
+
saveEnabled(value: boolean): boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Re-reads the file, so a choice another process saved since `open` is
|
|
44
|
+
* seen before anything is sent. An unreadable file changes nothing.
|
|
45
|
+
*/
|
|
46
|
+
reload(): void;
|
|
47
|
+
/** Whether this version of the notice has been shown. */
|
|
48
|
+
wasNotified(version: number): boolean;
|
|
49
|
+
/** Records that the notice was shown; a failed write only means it may show once more. */
|
|
50
|
+
markNotified(version: number, at: string): void;
|
|
51
|
+
private write;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/telemetry/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAwBH,sDAAsD;AACtD,wBAAgB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,oEAAoE;AACpE,wBAAgB,kBAAkB,CAChC,GAAG,GAAE,MAAM,CAAC,UAAwB,EACpC,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,MAAM,CAOR;AA8BD,qBAAa,cAAc;IACzB,sFAAsF;IACtF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,WAAW,CAAa;IAEhC,OAAO,eAGN;IAED;;;;;;;OAOG;IACH,MAAM,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,cAAc,GAAG,SAAS,CAezD;IAED,oDAAoD;IACpD,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,gFAAgF;IAChF,IAAI,WAAW,IAAI,MAAM,CAExB;IAED,+FAA+F;IAC/F,IAAI,QAAQ,IAAI,MAAM,CAErB;IAED,kGAAkG;IAClG,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGnC;IAED;;;OAGG;IACH,MAAM,IAAI,IAAI,CAQb;IAED,yDAAyD;IACzD,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEpC;IAED,0FAA0F;IAC1F,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI,CAI9C;IAED,OAAO,CAAC,KAAK;CAQd"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The user-level telemetry preferences: `~/.config/e2e/telemetry.json`
|
|
3
|
+
* (`$XDG_CONFIG_HOME/e2e` when set, `%APPDATA%\e2e` on Windows). The file
|
|
4
|
+
* holds the opt-out, the random id every event from this machine is
|
|
5
|
+
* attributed to, the salt that keeps a hashed path unrecoverable, and which
|
|
6
|
+
* notice was shown when. Nothing in it names the person or the machine: the
|
|
7
|
+
* id and the salt are random bytes, generated here and never derived.
|
|
8
|
+
*
|
|
9
|
+
* The store fails closed. `open` completes the file in one write when the id
|
|
10
|
+
* or the salt is missing, and when that write cannot happen there is no
|
|
11
|
+
* store: telemetry treats the absence as an opt-out rather than sending
|
|
12
|
+
* events it could neither attribute nor de-duplicate. After `open`, only the
|
|
13
|
+
* user's own choice and the notice mark write, so an ordinary command never
|
|
14
|
+
* rewrites the file while another process may be saving a choice to it.
|
|
15
|
+
*/
|
|
16
|
+
import { randomBytes } from 'node:crypto';
|
|
17
|
+
import { mkdirSync, readFileSync } from 'node:fs';
|
|
18
|
+
import os from 'node:os';
|
|
19
|
+
import path from 'node:path';
|
|
20
|
+
import { writeFileAtomicSync } from '../internal/atomic-write.js';
|
|
21
|
+
import { envValue } from '../internal/env.js';
|
|
22
|
+
const FILE_NAME = 'telemetry.json';
|
|
23
|
+
const HEX = /^[a-f0-9]{16,128}$/u;
|
|
24
|
+
/** The preferences file inside a config directory. */
|
|
25
|
+
export function preferencesPath(directory) {
|
|
26
|
+
return path.join(directory, FILE_NAME);
|
|
27
|
+
}
|
|
28
|
+
/** Where the preferences live for this platform and environment. */
|
|
29
|
+
export function telemetryConfigDir(env = process.env, platform = process.platform) {
|
|
30
|
+
if (platform === 'win32') {
|
|
31
|
+
const appData = envValue(env, 'APPDATA') ?? path.join(os.homedir(), 'AppData', 'Roaming');
|
|
32
|
+
return path.join(appData, 'e2e');
|
|
33
|
+
}
|
|
34
|
+
const configHome = envValue(env, 'XDG_CONFIG_HOME') ?? path.join(os.homedir(), '.config');
|
|
35
|
+
return path.join(configHome, 'e2e');
|
|
36
|
+
}
|
|
37
|
+
/** Keeps only the fields this version knows, each with the type it expects. */
|
|
38
|
+
function sanitize(value) {
|
|
39
|
+
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
40
|
+
return {};
|
|
41
|
+
const raw = value;
|
|
42
|
+
const preferences = {};
|
|
43
|
+
if (typeof raw['enabled'] === 'boolean')
|
|
44
|
+
preferences.enabled = raw['enabled'];
|
|
45
|
+
if (typeof raw['anonymousId'] === 'string' && HEX.test(raw['anonymousId'])) {
|
|
46
|
+
preferences.anonymousId = raw['anonymousId'];
|
|
47
|
+
}
|
|
48
|
+
if (typeof raw['salt'] === 'string' && HEX.test(raw['salt']))
|
|
49
|
+
preferences.salt = raw['salt'];
|
|
50
|
+
if (typeof raw['notifiedAt'] === 'string')
|
|
51
|
+
preferences.notifiedAt = raw['notifiedAt'];
|
|
52
|
+
if (typeof raw['noticeVersion'] === 'number')
|
|
53
|
+
preferences.noticeVersion = raw['noticeVersion'];
|
|
54
|
+
return preferences;
|
|
55
|
+
}
|
|
56
|
+
/** The file's preferences, or undefined when it is missing or not JSON. */
|
|
57
|
+
function readPreferences(filePath) {
|
|
58
|
+
try {
|
|
59
|
+
return sanitize(JSON.parse(readFileSync(filePath, 'utf8')));
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function randomHex() {
|
|
66
|
+
return randomBytes(16).toString('hex');
|
|
67
|
+
}
|
|
68
|
+
export class TelemetryStore {
|
|
69
|
+
/** The preferences file, for messages that tell the user where a choice was saved. */
|
|
70
|
+
path;
|
|
71
|
+
preferences;
|
|
72
|
+
constructor(filePath, preferences) {
|
|
73
|
+
this.path = filePath;
|
|
74
|
+
this.preferences = preferences;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Opens the store in `directory`. A complete file is only read, so a
|
|
78
|
+
* read-only preferences file keeps working. A missing file, a file that is
|
|
79
|
+
* not JSON, or one without an id or a salt is completed in one write that
|
|
80
|
+
* also creates the directory; a torn write must not turn into a permanent
|
|
81
|
+
* opt-in or opt-out either way. Returns undefined when that write fails,
|
|
82
|
+
* which the caller reads as off.
|
|
83
|
+
*/
|
|
84
|
+
static open(directory) {
|
|
85
|
+
const filePath = preferencesPath(directory);
|
|
86
|
+
const read = readPreferences(filePath);
|
|
87
|
+
const store = new TelemetryStore(filePath, {
|
|
88
|
+
...read,
|
|
89
|
+
anonymousId: read?.anonymousId ?? randomHex(),
|
|
90
|
+
salt: read?.salt ?? randomHex(),
|
|
91
|
+
});
|
|
92
|
+
if (read !== undefined && read.anonymousId !== undefined && read.salt !== undefined)
|
|
93
|
+
return store;
|
|
94
|
+
try {
|
|
95
|
+
mkdirSync(directory, { recursive: true });
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
return store.write() ? store : undefined;
|
|
101
|
+
}
|
|
102
|
+
/** The user's choice; unset means participating. */
|
|
103
|
+
get enabled() {
|
|
104
|
+
return this.preferences.enabled !== false;
|
|
105
|
+
}
|
|
106
|
+
/** The random per-machine id every event from this machine is attributed to. */
|
|
107
|
+
get anonymousId() {
|
|
108
|
+
return this.preferences.anonymousId;
|
|
109
|
+
}
|
|
110
|
+
/** The local salt that keeps a hashed project path unrecoverable; never leaves the machine. */
|
|
111
|
+
get pathSalt() {
|
|
112
|
+
return this.preferences.salt;
|
|
113
|
+
}
|
|
114
|
+
/** Saves the choice; false when the file could not be written and the choice stayed in memory. */
|
|
115
|
+
saveEnabled(value) {
|
|
116
|
+
this.preferences.enabled = value;
|
|
117
|
+
return this.write();
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Re-reads the file, so a choice another process saved since `open` is
|
|
121
|
+
* seen before anything is sent. An unreadable file changes nothing.
|
|
122
|
+
*/
|
|
123
|
+
reload() {
|
|
124
|
+
const read = readPreferences(this.path);
|
|
125
|
+
if (read === undefined)
|
|
126
|
+
return;
|
|
127
|
+
this.preferences = {
|
|
128
|
+
...read,
|
|
129
|
+
anonymousId: read.anonymousId ?? this.preferences.anonymousId,
|
|
130
|
+
salt: read.salt ?? this.preferences.salt,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
/** Whether this version of the notice has been shown. */
|
|
134
|
+
wasNotified(version) {
|
|
135
|
+
return this.preferences.notifiedAt !== undefined && (this.preferences.noticeVersion ?? 0) >= version;
|
|
136
|
+
}
|
|
137
|
+
/** Records that the notice was shown; a failed write only means it may show once more. */
|
|
138
|
+
markNotified(version, at) {
|
|
139
|
+
this.preferences.notifiedAt = at;
|
|
140
|
+
this.preferences.noticeVersion = version;
|
|
141
|
+
this.write();
|
|
142
|
+
}
|
|
143
|
+
write() {
|
|
144
|
+
try {
|
|
145
|
+
writeFileAtomicSync(this.path, `${JSON.stringify(this.preferences, null, 2)}\n`, { mode: 0o600 });
|
|
146
|
+
return true;
|
|
147
|
+
}
|
|
148
|
+
catch {
|
|
149
|
+
return false;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/telemetry/store.ts"],"names":[],"mappings":"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"}
|