opensip-cli 0.1.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/LICENSE +202 -0
- package/NOTICE +8 -0
- package/README.md +51 -0
- package/dist/api.d.ts +17 -0
- package/dist/api.d.ts.map +1 -0
- package/dist/api.js +16 -0
- package/dist/api.js.map +1 -0
- package/dist/bootstrap/admit-tool-package.d.ts +117 -0
- package/dist/bootstrap/admit-tool-package.d.ts.map +1 -0
- package/dist/bootstrap/admit-tool-package.js +170 -0
- package/dist/bootstrap/admit-tool-package.js.map +1 -0
- package/dist/bootstrap/baseline-seams.d.ts +30 -0
- package/dist/bootstrap/baseline-seams.d.ts.map +1 -0
- package/dist/bootstrap/baseline-seams.js +156 -0
- package/dist/bootstrap/baseline-seams.js.map +1 -0
- package/dist/bootstrap/bootstrap-error.d.ts +41 -0
- package/dist/bootstrap/bootstrap-error.d.ts.map +1 -0
- package/dist/bootstrap/bootstrap-error.js +33 -0
- package/dist/bootstrap/bootstrap-error.js.map +1 -0
- package/dist/bootstrap/build-command-registration-input.d.ts +34 -0
- package/dist/bootstrap/build-command-registration-input.d.ts.map +1 -0
- package/dist/bootstrap/build-command-registration-input.js +73 -0
- package/dist/bootstrap/build-command-registration-input.js.map +1 -0
- package/dist/bootstrap/build-per-run-scope.d.ts +62 -0
- package/dist/bootstrap/build-per-run-scope.d.ts.map +1 -0
- package/dist/bootstrap/build-per-run-scope.js +152 -0
- package/dist/bootstrap/build-per-run-scope.js.map +1 -0
- package/dist/bootstrap/build-targets.d.ts +42 -0
- package/dist/bootstrap/build-targets.d.ts.map +1 -0
- package/dist/bootstrap/build-targets.js +117 -0
- package/dist/bootstrap/build-targets.js.map +1 -0
- package/dist/bootstrap/cli-defaults.d.ts +35 -0
- package/dist/bootstrap/cli-defaults.d.ts.map +1 -0
- package/dist/bootstrap/cli-defaults.js +65 -0
- package/dist/bootstrap/cli-defaults.js.map +1 -0
- package/dist/bootstrap/config-and-capabilities.d.ts +74 -0
- package/dist/bootstrap/config-and-capabilities.d.ts.map +1 -0
- package/dist/bootstrap/config-and-capabilities.js +224 -0
- package/dist/bootstrap/config-and-capabilities.js.map +1 -0
- package/dist/bootstrap/deliver-envelope.d.ts +80 -0
- package/dist/bootstrap/deliver-envelope.d.ts.map +1 -0
- package/dist/bootstrap/deliver-envelope.js +195 -0
- package/dist/bootstrap/deliver-envelope.js.map +1 -0
- package/dist/bootstrap/egress-plane.d.ts +22 -0
- package/dist/bootstrap/egress-plane.d.ts.map +1 -0
- package/dist/bootstrap/egress-plane.js +37 -0
- package/dist/bootstrap/egress-plane.js.map +1 -0
- package/dist/bootstrap/host-planes.d.ts +28 -0
- package/dist/bootstrap/host-planes.d.ts.map +1 -0
- package/dist/bootstrap/host-planes.js +152 -0
- package/dist/bootstrap/host-planes.js.map +1 -0
- package/dist/bootstrap/index.d.ts +76 -0
- package/dist/bootstrap/index.d.ts.map +1 -0
- package/dist/bootstrap/index.js +109 -0
- package/dist/bootstrap/index.js.map +1 -0
- package/dist/bootstrap/live-plane.d.ts +51 -0
- package/dist/bootstrap/live-plane.d.ts.map +1 -0
- package/dist/bootstrap/live-plane.js +72 -0
- package/dist/bootstrap/live-plane.js.map +1 -0
- package/dist/bootstrap/load-tool-capabilities.d.ts +42 -0
- package/dist/bootstrap/load-tool-capabilities.d.ts.map +1 -0
- package/dist/bootstrap/load-tool-capabilities.js +76 -0
- package/dist/bootstrap/load-tool-capabilities.js.map +1 -0
- package/dist/bootstrap/output-plane.d.ts +37 -0
- package/dist/bootstrap/output-plane.d.ts.map +1 -0
- package/dist/bootstrap/output-plane.js +114 -0
- package/dist/bootstrap/output-plane.js.map +1 -0
- package/dist/bootstrap/owning-tool-init.d.ts +32 -0
- package/dist/bootstrap/owning-tool-init.d.ts.map +1 -0
- package/dist/bootstrap/owning-tool-init.js +69 -0
- package/dist/bootstrap/owning-tool-init.js.map +1 -0
- package/dist/bootstrap/pre-action-guards.d.ts +44 -0
- package/dist/bootstrap/pre-action-guards.d.ts.map +1 -0
- package/dist/bootstrap/pre-action-guards.js +136 -0
- package/dist/bootstrap/pre-action-guards.js.map +1 -0
- package/dist/bootstrap/pre-action-hook.d.ts +68 -0
- package/dist/bootstrap/pre-action-hook.d.ts.map +1 -0
- package/dist/bootstrap/pre-action-hook.js +289 -0
- package/dist/bootstrap/pre-action-hook.js.map +1 -0
- package/dist/bootstrap/pre-action-messages.d.ts +32 -0
- package/dist/bootstrap/pre-action-messages.d.ts.map +1 -0
- package/dist/bootstrap/pre-action-messages.js +49 -0
- package/dist/bootstrap/pre-action-messages.js.map +1 -0
- package/dist/bootstrap/process-idempotency.d.ts +17 -0
- package/dist/bootstrap/process-idempotency.d.ts.map +1 -0
- package/dist/bootstrap/process-idempotency.js +20 -0
- package/dist/bootstrap/process-idempotency.js.map +1 -0
- package/dist/bootstrap/register-language-adapters.d.ts +23 -0
- package/dist/bootstrap/register-language-adapters.d.ts.map +1 -0
- package/dist/bootstrap/register-language-adapters.js +35 -0
- package/dist/bootstrap/register-language-adapters.js.map +1 -0
- package/dist/bootstrap/register-tools.d.ts +228 -0
- package/dist/bootstrap/register-tools.d.ts.map +1 -0
- package/dist/bootstrap/register-tools.js +696 -0
- package/dist/bootstrap/register-tools.js.map +1 -0
- package/dist/bootstrap/render.d.ts +27 -0
- package/dist/bootstrap/render.d.ts.map +1 -0
- package/dist/bootstrap/render.js +53 -0
- package/dist/bootstrap/render.js.map +1 -0
- package/dist/bootstrap/report.d.ts +34 -0
- package/dist/bootstrap/report.d.ts.map +1 -0
- package/dist/bootstrap/report.js +47 -0
- package/dist/bootstrap/report.js.map +1 -0
- package/dist/bootstrap/run-plane.d.ts +105 -0
- package/dist/bootstrap/run-plane.d.ts.map +1 -0
- package/dist/bootstrap/run-plane.js +190 -0
- package/dist/bootstrap/run-plane.js.map +1 -0
- package/dist/bootstrap/scope-access.d.ts +68 -0
- package/dist/bootstrap/scope-access.d.ts.map +1 -0
- package/dist/bootstrap/scope-access.js +115 -0
- package/dist/bootstrap/scope-access.js.map +1 -0
- package/dist/bootstrap/state-seams.d.ts +14 -0
- package/dist/bootstrap/state-seams.d.ts.map +1 -0
- package/dist/bootstrap/state-seams.js +26 -0
- package/dist/bootstrap/state-seams.js.map +1 -0
- package/dist/bootstrap/tool-lifecycle.d.ts +102 -0
- package/dist/bootstrap/tool-lifecycle.d.ts.map +1 -0
- package/dist/bootstrap/tool-lifecycle.js +103 -0
- package/dist/bootstrap/tool-lifecycle.js.map +1 -0
- package/dist/bootstrap/tool-trust.d.ts +49 -0
- package/dist/bootstrap/tool-trust.d.ts.map +1 -0
- package/dist/bootstrap/tool-trust.js +65 -0
- package/dist/bootstrap/tool-trust.js.map +1 -0
- package/dist/bootstrap/validate-tool.d.ts +22 -0
- package/dist/bootstrap/validate-tool.d.ts.map +1 -0
- package/dist/bootstrap/validate-tool.js +38 -0
- package/dist/bootstrap/validate-tool.js.map +1 -0
- package/dist/cli-context.d.ts +38 -0
- package/dist/cli-context.d.ts.map +1 -0
- package/dist/cli-context.js +134 -0
- package/dist/cli-context.js.map +1 -0
- package/dist/commands/agent-catalog.d.ts +45 -0
- package/dist/commands/agent-catalog.d.ts.map +1 -0
- package/dist/commands/agent-catalog.js +115 -0
- package/dist/commands/agent-catalog.js.map +1 -0
- package/dist/commands/assemble-outcome.d.ts +69 -0
- package/dist/commands/assemble-outcome.d.ts.map +1 -0
- package/dist/commands/assemble-outcome.js +121 -0
- package/dist/commands/assemble-outcome.js.map +1 -0
- package/dist/commands/clear.d.ts +32 -0
- package/dist/commands/clear.d.ts.map +1 -0
- package/dist/commands/clear.js +73 -0
- package/dist/commands/clear.js.map +1 -0
- package/dist/commands/completion.d.ts +90 -0
- package/dist/commands/completion.d.ts.map +1 -0
- package/dist/commands/completion.js +233 -0
- package/dist/commands/completion.js.map +1 -0
- package/dist/commands/configure.d.ts +32 -0
- package/dist/commands/configure.d.ts.map +1 -0
- package/dist/commands/configure.js +94 -0
- package/dist/commands/configure.js.map +1 -0
- package/dist/commands/history.d.ts +18 -0
- package/dist/commands/history.d.ts.map +1 -0
- package/dist/commands/history.js +48 -0
- package/dist/commands/history.js.map +1 -0
- package/dist/commands/host-command-specs.d.ts +49 -0
- package/dist/commands/host-command-specs.d.ts.map +1 -0
- package/dist/commands/host-command-specs.js +331 -0
- package/dist/commands/host-command-specs.js.map +1 -0
- package/dist/commands/host-subcommand-groups.d.ts +69 -0
- package/dist/commands/host-subcommand-groups.d.ts.map +1 -0
- package/dist/commands/host-subcommand-groups.js +374 -0
- package/dist/commands/host-subcommand-groups.js.map +1 -0
- package/dist/commands/index.d.ts +36 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +36 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/init/config-templates.d.ts +16 -0
- package/dist/commands/init/config-templates.d.ts.map +1 -0
- package/dist/commands/init/config-templates.js +108 -0
- package/dist/commands/init/config-templates.js.map +1 -0
- package/dist/commands/init/file-classifier.d.ts +40 -0
- package/dist/commands/init/file-classifier.d.ts.map +1 -0
- package/dist/commands/init/file-classifier.js +155 -0
- package/dist/commands/init/file-classifier.js.map +1 -0
- package/dist/commands/init/language-detection.d.ts +44 -0
- package/dist/commands/init/language-detection.d.ts.map +1 -0
- package/dist/commands/init/language-detection.js +124 -0
- package/dist/commands/init/language-detection.js.map +1 -0
- package/dist/commands/init/scaffold-writer.d.ts +26 -0
- package/dist/commands/init/scaffold-writer.d.ts.map +1 -0
- package/dist/commands/init/scaffold-writer.js +102 -0
- package/dist/commands/init/scaffold-writer.js.map +1 -0
- package/dist/commands/init/state-machine.d.ts +32 -0
- package/dist/commands/init/state-machine.d.ts.map +1 -0
- package/dist/commands/init/state-machine.js +105 -0
- package/dist/commands/init/state-machine.js.map +1 -0
- package/dist/commands/init.d.ts +95 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +209 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/mount-command-spec.d.ts +106 -0
- package/dist/commands/mount-command-spec.d.ts.map +1 -0
- package/dist/commands/mount-command-spec.js +313 -0
- package/dist/commands/mount-command-spec.js.map +1 -0
- package/dist/commands/mount-result-command.d.ts +71 -0
- package/dist/commands/mount-result-command.d.ts.map +1 -0
- package/dist/commands/mount-result-command.js +76 -0
- package/dist/commands/mount-result-command.js.map +1 -0
- package/dist/commands/plugin/config-edit.d.ts +20 -0
- package/dist/commands/plugin/config-edit.d.ts.map +1 -0
- package/dist/commands/plugin/config-edit.js +102 -0
- package/dist/commands/plugin/config-edit.js.map +1 -0
- package/dist/commands/plugin/domain-resolution.d.ts +38 -0
- package/dist/commands/plugin/domain-resolution.d.ts.map +1 -0
- package/dist/commands/plugin/domain-resolution.js +98 -0
- package/dist/commands/plugin/domain-resolution.js.map +1 -0
- package/dist/commands/plugin/host-dir.d.ts +42 -0
- package/dist/commands/plugin/host-dir.d.ts.map +1 -0
- package/dist/commands/plugin/host-dir.js +168 -0
- package/dist/commands/plugin/host-dir.js.map +1 -0
- package/dist/commands/plugin-host-ops.d.ts +41 -0
- package/dist/commands/plugin-host-ops.d.ts.map +1 -0
- package/dist/commands/plugin-host-ops.js +114 -0
- package/dist/commands/plugin-host-ops.js.map +1 -0
- package/dist/commands/plugin.d.ts +81 -0
- package/dist/commands/plugin.d.ts.map +1 -0
- package/dist/commands/plugin.js +287 -0
- package/dist/commands/plugin.js.map +1 -0
- package/dist/commands/render-outcome.d.ts +52 -0
- package/dist/commands/render-outcome.d.ts.map +1 -0
- package/dist/commands/render-outcome.js +55 -0
- package/dist/commands/render-outcome.js.map +1 -0
- package/dist/commands/session-show.d.ts +27 -0
- package/dist/commands/session-show.d.ts.map +1 -0
- package/dist/commands/session-show.js +166 -0
- package/dist/commands/session-show.js.map +1 -0
- package/dist/commands/shared.d.ts +107 -0
- package/dist/commands/shared.d.ts.map +1 -0
- package/dist/commands/shared.js +13 -0
- package/dist/commands/shared.js.map +1 -0
- package/dist/commands/tools/data-purge.d.ts +20 -0
- package/dist/commands/tools/data-purge.d.ts.map +1 -0
- package/dist/commands/tools/data-purge.js +59 -0
- package/dist/commands/tools/data-purge.js.map +1 -0
- package/dist/commands/tools/index.d.ts +16 -0
- package/dist/commands/tools/index.d.ts.map +1 -0
- package/dist/commands/tools/index.js +213 -0
- package/dist/commands/tools/index.js.map +1 -0
- package/dist/commands/tools/install.d.ts +24 -0
- package/dist/commands/tools/install.d.ts.map +1 -0
- package/dist/commands/tools/install.js +83 -0
- package/dist/commands/tools/install.js.map +1 -0
- package/dist/commands/tools/list.d.ts +41 -0
- package/dist/commands/tools/list.d.ts.map +1 -0
- package/dist/commands/tools/list.js +103 -0
- package/dist/commands/tools/list.js.map +1 -0
- package/dist/commands/tools/runtime-probe-entry.d.ts +14 -0
- package/dist/commands/tools/runtime-probe-entry.d.ts.map +1 -0
- package/dist/commands/tools/runtime-probe-entry.js +36 -0
- package/dist/commands/tools/runtime-probe-entry.js.map +1 -0
- package/dist/commands/tools/runtime-probe.d.ts +29 -0
- package/dist/commands/tools/runtime-probe.d.ts.map +1 -0
- package/dist/commands/tools/runtime-probe.js +66 -0
- package/dist/commands/tools/runtime-probe.js.map +1 -0
- package/dist/commands/tools/storage-contract-checks.d.ts +37 -0
- package/dist/commands/tools/storage-contract-checks.d.ts.map +1 -0
- package/dist/commands/tools/storage-contract-checks.js +91 -0
- package/dist/commands/tools/storage-contract-checks.js.map +1 -0
- package/dist/commands/tools/uninstall.d.ts +29 -0
- package/dist/commands/tools/uninstall.d.ts.map +1 -0
- package/dist/commands/tools/uninstall.js +77 -0
- package/dist/commands/tools/uninstall.js.map +1 -0
- package/dist/commands/tools/validate.d.ts +44 -0
- package/dist/commands/tools/validate.d.ts.map +1 -0
- package/dist/commands/tools/validate.js +202 -0
- package/dist/commands/tools/validate.js.map +1 -0
- package/dist/commands/uninstall/targets.d.ts +53 -0
- package/dist/commands/uninstall/targets.d.ts.map +1 -0
- package/dist/commands/uninstall/targets.js +205 -0
- package/dist/commands/uninstall/targets.js.map +1 -0
- package/dist/commands/uninstall.d.ts +88 -0
- package/dist/commands/uninstall.d.ts.map +1 -0
- package/dist/commands/uninstall.js +184 -0
- package/dist/commands/uninstall.js.map +1 -0
- package/dist/env/host-env-specs.d.ts +52 -0
- package/dist/env/host-env-specs.d.ts.map +1 -0
- package/dist/env/host-env-specs.js +129 -0
- package/dist/env/host-env-specs.js.map +1 -0
- package/dist/error-handler.d.ts +64 -0
- package/dist/error-handler.d.ts.map +1 -0
- package/dist/error-handler.js +180 -0
- package/dist/error-handler.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +154 -0
- package/dist/index.js.map +1 -0
- package/dist/open-report.d.ts +40 -0
- package/dist/open-report.d.ts.map +1 -0
- package/dist/open-report.js +54 -0
- package/dist/open-report.js.map +1 -0
- package/dist/report-compose.d.ts +35 -0
- package/dist/report-compose.d.ts.map +1 -0
- package/dist/report-compose.js +103 -0
- package/dist/report-compose.js.map +1 -0
- package/dist/session-replay-registry.d.ts +20 -0
- package/dist/session-replay-registry.d.ts.map +1 -0
- package/dist/session-replay-registry.js +38 -0
- package/dist/session-replay-registry.js.map +1 -0
- package/dist/telemetry/profiling.d.ts +42 -0
- package/dist/telemetry/profiling.d.ts.map +1 -0
- package/dist/telemetry/profiling.js +160 -0
- package/dist/telemetry/profiling.js.map +1 -0
- package/dist/telemetry/sdk-init.d.ts +87 -0
- package/dist/telemetry/sdk-init.d.ts.map +1 -0
- package/dist/telemetry/sdk-init.js +235 -0
- package/dist/telemetry/sdk-init.js.map +1 -0
- package/dist/ui/App.d.ts +32 -0
- package/dist/ui/App.d.ts.map +1 -0
- package/dist/ui/App.js +35 -0
- package/dist/ui/App.js.map +1 -0
- package/dist/ui/render.d.ts +15 -0
- package/dist/ui/render.d.ts.map +1 -0
- package/dist/ui/render.js +21 -0
- package/dist/ui/render.js.map +1 -0
- package/dist/ui/result-to-view.d.ts +40 -0
- package/dist/ui/result-to-view.d.ts.map +1 -0
- package/dist/ui/result-to-view.js +389 -0
- package/dist/ui/result-to-view.js.map +1 -0
- package/dist/ui/views/init-view.d.ts +9 -0
- package/dist/ui/views/init-view.d.ts.map +1 -0
- package/dist/ui/views/init-view.js +119 -0
- package/dist/ui/views/init-view.js.map +1 -0
- package/dist/ui/views/misc-views.d.ts +18 -0
- package/dist/ui/views/misc-views.d.ts.map +1 -0
- package/dist/ui/views/misc-views.js +244 -0
- package/dist/ui/views/misc-views.js.map +1 -0
- package/dist/ui/views/plugin-view.d.ts +8 -0
- package/dist/ui/views/plugin-view.d.ts.map +1 -0
- package/dist/ui/views/plugin-view.js +135 -0
- package/dist/ui/views/plugin-view.js.map +1 -0
- package/dist/ui/views/tools-views.d.ts +12 -0
- package/dist/ui/views/tools-views.d.ts.map +1 -0
- package/dist/ui/views/tools-views.js +152 -0
- package/dist/ui/views/tools-views.js.map +1 -0
- package/dist/update-notifier.d.ts +108 -0
- package/dist/update-notifier.d.ts.map +1 -0
- package/dist/update-notifier.js +188 -0
- package/dist/update-notifier.js.map +1 -0
- package/dist/update-state.d.ts +40 -0
- package/dist/update-state.d.ts.map +1 -0
- package/dist/update-state.js +81 -0
- package/dist/update-state.js.map +1 -0
- package/dist/welcome.d.ts +53 -0
- package/dist/welcome.d.ts.map +1 -0
- package/dist/welcome.js +89 -0
- package/dist/welcome.js.map +1 -0
- package/package.json +100 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Profiling support — optional and severable (see observability-hardening plan).
|
|
3
|
+
*
|
|
4
|
+
* Gate (per ADR-0049 and plan):
|
|
5
|
+
* - Recommended: OPENSIP_PROFILING=1 AND OTEL_EXPORTER_OTLP_ENDPOINT set.
|
|
6
|
+
* - Supported alternative (kept for flexibility): profiling can be tied to
|
|
7
|
+
* OTEL_EXPORTER_OTLP_ENDPOINT alone (documented with cost warnings).
|
|
8
|
+
*
|
|
9
|
+
* Implementation uses Node's built-in `inspector` module for real CPU profiles
|
|
10
|
+
* (no extra published dependency for the optional profiling path). This gives
|
|
11
|
+
* actionable .cpuprofile files for short-lived CLI runs, with runId/command
|
|
12
|
+
* labels embedded in the filename and a sidecar JSON.
|
|
13
|
+
*
|
|
14
|
+
* When a full OTel profiles signal / Pyroscope client is desired, the start/stop
|
|
15
|
+
* hooks here are the single place to swap in that implementation while reusing
|
|
16
|
+
* the same resource attributes, runId, and shutdown discipline.
|
|
17
|
+
*
|
|
18
|
+
* The seam is intentionally thin in core; all heavy lifting and any future
|
|
19
|
+
* SDK bits stay in the CLI root.
|
|
20
|
+
*/
|
|
21
|
+
import { mkdirSync, writeFileSync } from 'node:fs';
|
|
22
|
+
import { Session } from 'node:inspector';
|
|
23
|
+
import { join } from 'node:path';
|
|
24
|
+
import { logger } from '@opensip-cli/core';
|
|
25
|
+
import { hostEnv } from '../env/host-env-specs.js';
|
|
26
|
+
let session = null;
|
|
27
|
+
let isProfiling = false;
|
|
28
|
+
let profilePath = null;
|
|
29
|
+
let labelsPath = null;
|
|
30
|
+
/** Module tag for structured logging (also dedupes the sonarjs/no-duplicate-string occurrences). */
|
|
31
|
+
const MODULE = 'cli:telemetry';
|
|
32
|
+
/** Returns true if the recommended or OTEL-only profiling gate is satisfied. */
|
|
33
|
+
export function isProfilingEnabled() {
|
|
34
|
+
const endpoint = hostEnv.get('OTEL_EXPORTER_OTLP_ENDPOINT');
|
|
35
|
+
if (!endpoint)
|
|
36
|
+
return false;
|
|
37
|
+
const explicit = hostEnv.get('OPENSIP_PROFILING');
|
|
38
|
+
if (explicit === '1' || explicit === 'true')
|
|
39
|
+
return true;
|
|
40
|
+
// Supported "just the OTEL endpoint" alternative (with warnings in docs/ADR-0049).
|
|
41
|
+
// Many teams prefer one knob; we honor it but log at warn so cost is visible.
|
|
42
|
+
// Operators who do not want this can set OPENSIP_PROFILING=0 explicitly.
|
|
43
|
+
return true; // OTEL endpoint present ⇒ profiling on in this fallback mode
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Start CPU profiling for this invocation (if gate is open).
|
|
47
|
+
* Must be called after RunScope is entered (so runId is available).
|
|
48
|
+
* Safe to call multiple times (idempotent).
|
|
49
|
+
*/
|
|
50
|
+
export function startProfiling(scope, command) {
|
|
51
|
+
if (isProfiling)
|
|
52
|
+
return;
|
|
53
|
+
try {
|
|
54
|
+
if (!isProfilingEnabled())
|
|
55
|
+
return;
|
|
56
|
+
session = new Session();
|
|
57
|
+
session.connect();
|
|
58
|
+
session.post('Profiler.enable', (_err, _res) => {
|
|
59
|
+
if (!session)
|
|
60
|
+
return;
|
|
61
|
+
session.post('Profiler.start', (_err2, _res2) => {
|
|
62
|
+
isProfiling = true;
|
|
63
|
+
const runId = scope?.runId ?? 'unknown';
|
|
64
|
+
const safeCommand = (command ?? 'cli').replace(/[^a-z0-9_-]/gi, '_');
|
|
65
|
+
const ts = new Date().toISOString().replace(/[:.]/g, '-');
|
|
66
|
+
// Place profiles under project logs dir if available, else cwd/.runtime/profiles
|
|
67
|
+
const baseDir = scope?.projectContext?.scope === 'project'
|
|
68
|
+
? join(scope.projectContext.projectRoot, 'opensip-cli/.runtime/profiles')
|
|
69
|
+
: join(process.cwd(), 'opensip-cli/.runtime/profiles');
|
|
70
|
+
mkdirSync(baseDir, { recursive: true });
|
|
71
|
+
profilePath = join(baseDir, `${ts}-${safeCommand}-${runId}.cpuprofile`);
|
|
72
|
+
labelsPath = join(baseDir, `${ts}-${safeCommand}-${runId}.labels.json`);
|
|
73
|
+
const labels = {
|
|
74
|
+
runId,
|
|
75
|
+
command: command ?? 'unknown',
|
|
76
|
+
service: 'opensip-cli',
|
|
77
|
+
// Add any OTEL_RESOURCE_ATTRIBUTES derived labels here if needed in future
|
|
78
|
+
};
|
|
79
|
+
writeFileSync(labelsPath, JSON.stringify(labels, null, 2));
|
|
80
|
+
logger.info({
|
|
81
|
+
evt: 'cli.profiling.started',
|
|
82
|
+
module: MODULE,
|
|
83
|
+
runId,
|
|
84
|
+
command,
|
|
85
|
+
profilePath,
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
catch (error) {
|
|
91
|
+
logger.warn({
|
|
92
|
+
evt: 'cli.profiling.start_failed',
|
|
93
|
+
module: MODULE,
|
|
94
|
+
error: error instanceof Error ? error.message : String(error),
|
|
95
|
+
});
|
|
96
|
+
// Best effort — profiling failure must never break the run
|
|
97
|
+
cleanup();
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Stop profiling and flush the .cpuprofile + labels sidecar.
|
|
102
|
+
* Safe and idempotent.
|
|
103
|
+
*/
|
|
104
|
+
export function stopProfiling() {
|
|
105
|
+
if (!isProfiling || !session) {
|
|
106
|
+
cleanup();
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
// node:inspector post callbacks for Profiler.* use structural types not fully declared
|
|
111
|
+
// in the ambient types we consume; the result object (incl. profile) arrives as any.
|
|
112
|
+
// We narrow locally for the write path; the disable is scoped to the wire call.
|
|
113
|
+
session.post('Profiler.stop', (err, result = {}) => {
|
|
114
|
+
if (err) {
|
|
115
|
+
logger.warn({
|
|
116
|
+
evt: 'cli.profiling.stop_failed',
|
|
117
|
+
module: MODULE,
|
|
118
|
+
error: err.message || String(err),
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
else if (result.profile && profilePath) {
|
|
122
|
+
writeFileSync(profilePath, JSON.stringify(result.profile));
|
|
123
|
+
logger.info({
|
|
124
|
+
evt: 'cli.profiling.stopped',
|
|
125
|
+
module: MODULE,
|
|
126
|
+
profilePath,
|
|
127
|
+
labelsPath,
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
cleanup();
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
logger.warn({
|
|
135
|
+
evt: 'cli.profiling.stop_failed',
|
|
136
|
+
module: MODULE,
|
|
137
|
+
error: error instanceof Error ? error.message : String(error),
|
|
138
|
+
});
|
|
139
|
+
cleanup();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
function cleanup() {
|
|
143
|
+
if (session) {
|
|
144
|
+
try {
|
|
145
|
+
session.disconnect();
|
|
146
|
+
}
|
|
147
|
+
catch {
|
|
148
|
+
// @swallow-ok best-effort inspector session disconnect during profiling teardown
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
session = null;
|
|
152
|
+
isProfiling = false;
|
|
153
|
+
profilePath = null;
|
|
154
|
+
labelsPath = null;
|
|
155
|
+
}
|
|
156
|
+
/** Exposed for tests / shutdown. */
|
|
157
|
+
export function resetProfilingForTests() {
|
|
158
|
+
cleanup();
|
|
159
|
+
}
|
|
160
|
+
//# sourceMappingURL=profiling.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profiling.js","sourceRoot":"","sources":["../../src/telemetry/profiling.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenTelemetry SDK init — the application half of the OTel library/application
|
|
3
|
+
* split, wired ONLY at the CLI composition root.
|
|
4
|
+
*
|
|
5
|
+
* ## Opt-in gate (the load-bearing contract)
|
|
6
|
+
*
|
|
7
|
+
* The entire SDK (traces + metrics + optional profiling) is gated on
|
|
8
|
+
* `OTEL_EXPORTER_OTLP_ENDPOINT` (see ADR-0049 and observability plan for details).
|
|
9
|
+
*
|
|
10
|
+
* - **Set** ⇒ register NodeTracerProvider + (Phase 2) MeterProvider.
|
|
11
|
+
* `core`'s `getTracer`/`withSpan`/`getMeter` resolve to real implementations.
|
|
12
|
+
* - Profiling uses the same endpoint (recommended with dedicated
|
|
13
|
+
* `OPENSIP_PROFILING=1` flag for cost control; "OTEL endpoint alone" mode
|
|
14
|
+
* is supported with warnings).
|
|
15
|
+
* - **Unset** ⇒ hard no-op for everything. Standalone CLI pays nothing.
|
|
16
|
+
*
|
|
17
|
+
* ## Layering
|
|
18
|
+
*
|
|
19
|
+
* The heavy SDK packages (`@opentelemetry/sdk-trace-node`, the OTLP exporter,
|
|
20
|
+
* `context-async-hooks`, `core`'s propagator, `resources`) are imported HERE
|
|
21
|
+
* and nowhere else. `core` and the tool packages depend on `@opentelemetry/api`
|
|
22
|
+
* only — dependency-cruiser enforces that an `@opentelemetry/sdk-*` import never
|
|
23
|
+
* leaks into the kernel or a tool.
|
|
24
|
+
*
|
|
25
|
+
* ## Parent-trace nesting
|
|
26
|
+
*
|
|
27
|
+
* An embedding consumer spawns the binary with a `TRACEPARENT` env var. We
|
|
28
|
+
* extract it via the W3C propagator and expose it as a parent context
|
|
29
|
+
* ({@link parentTelemetryContext} / {@link runWithTelemetryContext}) so the
|
|
30
|
+
* command dispatch — and therefore graph's stage spans — nests under the
|
|
31
|
+
* consumer's trace. When `TRACEPARENT` is unset the spans form their own trace,
|
|
32
|
+
* which is still valid.
|
|
33
|
+
*
|
|
34
|
+
* ## Fail-safe shutdown
|
|
35
|
+
*
|
|
36
|
+
* Telemetry must never crash OR hang the primary CLI run: a dead/slow collector
|
|
37
|
+
* has to degrade to "spans dropped," not a multi-second stall on exit (amplified
|
|
38
|
+
* on the sharded path, where every shard-worker subprocess also flushes). Two
|
|
39
|
+
* bounds enforce this: each export attempt is capped at {@link SHUTDOWN_TIMEOUT_MS}
|
|
40
|
+
* (`OTLPTraceExporter({ timeoutMillis })`), and {@link shutdownTelemetry} races the
|
|
41
|
+
* final flush against the same deadline and swallows any failure.
|
|
42
|
+
*/
|
|
43
|
+
import { type Context } from '@opentelemetry/api';
|
|
44
|
+
/**
|
|
45
|
+
* Initialize OpenTelemetry tracing, gated on `OTEL_EXPORTER_OTLP_ENDPOINT`.
|
|
46
|
+
*
|
|
47
|
+
* No-op when the endpoint env var is falsy, or when already started (idempotent
|
|
48
|
+
* and safe to call from multiple entry points). Sets the GLOBAL tracer provider
|
|
49
|
+
* so `core`'s `getTracer` resolves to real tracers process-wide.
|
|
50
|
+
*
|
|
51
|
+
* @param cliEntryUrl `import.meta.url` of the CLI entry, used to read the CLI
|
|
52
|
+
* package version for the `service.version` resource attribute.
|
|
53
|
+
*/
|
|
54
|
+
export declare function initTelemetry(cliEntryUrl: string): void;
|
|
55
|
+
/**
|
|
56
|
+
* The parent context extracted from `TRACEPARENT`, or undefined. Exposed for
|
|
57
|
+
* tests; production code should prefer {@link runWithTelemetryContext}.
|
|
58
|
+
*/
|
|
59
|
+
export declare function parentTelemetryContext(): Context | undefined;
|
|
60
|
+
/**
|
|
61
|
+
* Run `fn` with the extracted parent context active (when present), so spans
|
|
62
|
+
* created inside nest under the consumer's trace. A plain pass-through when no
|
|
63
|
+
* parent context was extracted (or telemetry is disabled), so standalone runs
|
|
64
|
+
* pay nothing.
|
|
65
|
+
*/
|
|
66
|
+
export declare function runWithTelemetryContext<T>(fn: () => T): T;
|
|
67
|
+
/**
|
|
68
|
+
* Flush and shut down the tracer + meter providers (and stop profiling if active)
|
|
69
|
+
* so batched data export before the short-lived process exits.
|
|
70
|
+
* No-op when telemetry was never started. Swallows shutdown errors — a dead
|
|
71
|
+
* collector must not crash the CLI on the way out.
|
|
72
|
+
*/
|
|
73
|
+
export declare function shutdownTelemetry(): Promise<void>;
|
|
74
|
+
/**
|
|
75
|
+
* Resolve when `work` settles, or reject with {@link TimeoutError} after `ms` —
|
|
76
|
+
* whichever comes first. The deadline timer is `unref`'d so it never keeps the
|
|
77
|
+
* (short-lived CLI) event loop alive when the work wins, and cleared on the
|
|
78
|
+
* happy path. Exported for tests.
|
|
79
|
+
*/
|
|
80
|
+
export declare function raceWithTimeout(work: Promise<void>, ms: number): Promise<void>;
|
|
81
|
+
/**
|
|
82
|
+
* Test-only reset of module state. Production never calls this — the CLI is a
|
|
83
|
+
* one-shot process. Tests use it to exercise the gate across env permutations
|
|
84
|
+
* without a fresh module each time.
|
|
85
|
+
*/
|
|
86
|
+
export declare function resetTelemetryForTests(): void;
|
|
87
|
+
//# sourceMappingURL=sdk-init.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdk-init.d.ts","sourceRoot":"","sources":["../../src/telemetry/sdk-init.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG;AAGH,OAAO,EAML,KAAK,OAAO,EACb,MAAM,oBAAoB,CAAC;AA8C5B;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI,CAgEvD;AAED;;;GAGG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,GAAG,SAAS,CAE5D;AAED;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,CAAC,CAEzD;AAED;;;;;GAKG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,IAAI,CAAC,CAyCvD;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,IAAI,EAAE,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBpF;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,IAAI,IAAI,CAG7C"}
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenTelemetry SDK init — the application half of the OTel library/application
|
|
3
|
+
* split, wired ONLY at the CLI composition root.
|
|
4
|
+
*
|
|
5
|
+
* ## Opt-in gate (the load-bearing contract)
|
|
6
|
+
*
|
|
7
|
+
* The entire SDK (traces + metrics + optional profiling) is gated on
|
|
8
|
+
* `OTEL_EXPORTER_OTLP_ENDPOINT` (see ADR-0049 and observability plan for details).
|
|
9
|
+
*
|
|
10
|
+
* - **Set** ⇒ register NodeTracerProvider + (Phase 2) MeterProvider.
|
|
11
|
+
* `core`'s `getTracer`/`withSpan`/`getMeter` resolve to real implementations.
|
|
12
|
+
* - Profiling uses the same endpoint (recommended with dedicated
|
|
13
|
+
* `OPENSIP_PROFILING=1` flag for cost control; "OTEL endpoint alone" mode
|
|
14
|
+
* is supported with warnings).
|
|
15
|
+
* - **Unset** ⇒ hard no-op for everything. Standalone CLI pays nothing.
|
|
16
|
+
*
|
|
17
|
+
* ## Layering
|
|
18
|
+
*
|
|
19
|
+
* The heavy SDK packages (`@opentelemetry/sdk-trace-node`, the OTLP exporter,
|
|
20
|
+
* `context-async-hooks`, `core`'s propagator, `resources`) are imported HERE
|
|
21
|
+
* and nowhere else. `core` and the tool packages depend on `@opentelemetry/api`
|
|
22
|
+
* only — dependency-cruiser enforces that an `@opentelemetry/sdk-*` import never
|
|
23
|
+
* leaks into the kernel or a tool.
|
|
24
|
+
*
|
|
25
|
+
* ## Parent-trace nesting
|
|
26
|
+
*
|
|
27
|
+
* An embedding consumer spawns the binary with a `TRACEPARENT` env var. We
|
|
28
|
+
* extract it via the W3C propagator and expose it as a parent context
|
|
29
|
+
* ({@link parentTelemetryContext} / {@link runWithTelemetryContext}) so the
|
|
30
|
+
* command dispatch — and therefore graph's stage spans — nests under the
|
|
31
|
+
* consumer's trace. When `TRACEPARENT` is unset the spans form their own trace,
|
|
32
|
+
* which is still valid.
|
|
33
|
+
*
|
|
34
|
+
* ## Fail-safe shutdown
|
|
35
|
+
*
|
|
36
|
+
* Telemetry must never crash OR hang the primary CLI run: a dead/slow collector
|
|
37
|
+
* has to degrade to "spans dropped," not a multi-second stall on exit (amplified
|
|
38
|
+
* on the sharded path, where every shard-worker subprocess also flushes). Two
|
|
39
|
+
* bounds enforce this: each export attempt is capped at {@link SHUTDOWN_TIMEOUT_MS}
|
|
40
|
+
* (`OTLPTraceExporter({ timeoutMillis })`), and {@link shutdownTelemetry} races the
|
|
41
|
+
* final flush against the same deadline and swallows any failure.
|
|
42
|
+
*/
|
|
43
|
+
import { logger, readPackageVersion, TimeoutError } from '@opensip-cli/core';
|
|
44
|
+
import { ROOT_CONTEXT, context as otelContext, defaultTextMapGetter, metrics, trace, } from '@opentelemetry/api';
|
|
45
|
+
import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks';
|
|
46
|
+
import { W3CTraceContextPropagator } from '@opentelemetry/core';
|
|
47
|
+
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http';
|
|
48
|
+
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
|
|
49
|
+
import { resourceFromAttributes, detectResources, envDetector } from '@opentelemetry/resources';
|
|
50
|
+
import { MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';
|
|
51
|
+
import { BatchSpanProcessor, NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
|
|
52
|
+
import { ATTR_SERVICE_NAME, ATTR_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';
|
|
53
|
+
import { hostEnv } from '../env/host-env-specs.js';
|
|
54
|
+
import { stopProfiling, resetProfilingForTests } from './profiling.js';
|
|
55
|
+
/**
|
|
56
|
+
* Hard ceiling (ms) on the final span flush AND on each individual export
|
|
57
|
+
* attempt. A dead/slow collector degrades to "spans dropped," never a hang on
|
|
58
|
+
* CLI or shard-worker exit. Kept short — a few seconds of lost batched spans is
|
|
59
|
+
* a fair trade for never stalling the primary run.
|
|
60
|
+
*/
|
|
61
|
+
const SHUTDOWN_TIMEOUT_MS = 2000;
|
|
62
|
+
/** Idempotency guard — provider registration is process-wide and one-shot. */
|
|
63
|
+
let started = false;
|
|
64
|
+
/** Internal shared reset of the process-wide telemetry state. */
|
|
65
|
+
function resetTelemetryState() {
|
|
66
|
+
started = false;
|
|
67
|
+
provider = undefined;
|
|
68
|
+
meterProvider = undefined;
|
|
69
|
+
parentContext = undefined;
|
|
70
|
+
}
|
|
71
|
+
/** The active tracer provider, retained so {@link shutdownTelemetry} can flush it. */
|
|
72
|
+
let provider;
|
|
73
|
+
/** The active meter provider (Phase 2). */
|
|
74
|
+
let meterProvider;
|
|
75
|
+
/**
|
|
76
|
+
* Parent context extracted from `TRACEPARENT`, or undefined when the var is
|
|
77
|
+
* absent (or telemetry is disabled). {@link runWithTelemetryContext} activates
|
|
78
|
+
* it so spans created during the run nest under the consumer's trace.
|
|
79
|
+
*/
|
|
80
|
+
let parentContext;
|
|
81
|
+
/**
|
|
82
|
+
* Initialize OpenTelemetry tracing, gated on `OTEL_EXPORTER_OTLP_ENDPOINT`.
|
|
83
|
+
*
|
|
84
|
+
* No-op when the endpoint env var is falsy, or when already started (idempotent
|
|
85
|
+
* and safe to call from multiple entry points). Sets the GLOBAL tracer provider
|
|
86
|
+
* so `core`'s `getTracer` resolves to real tracers process-wide.
|
|
87
|
+
*
|
|
88
|
+
* @param cliEntryUrl `import.meta.url` of the CLI entry, used to read the CLI
|
|
89
|
+
* package version for the `service.version` resource attribute.
|
|
90
|
+
*/
|
|
91
|
+
export function initTelemetry(cliEntryUrl) {
|
|
92
|
+
if (started)
|
|
93
|
+
return;
|
|
94
|
+
const endpoint = hostEnv.get('OTEL_EXPORTER_OTLP_ENDPOINT');
|
|
95
|
+
if (!endpoint)
|
|
96
|
+
return;
|
|
97
|
+
// Resource: explicit service identity merged with consumer-supplied
|
|
98
|
+
// attributes from OTEL_RESOURCE_ATTRIBUTES (via the env detector), e.g.
|
|
99
|
+
// `tenant_id=...,run_id=...`.
|
|
100
|
+
const resource = detectResources({ detectors: [envDetector] }).merge(resourceFromAttributes({
|
|
101
|
+
[ATTR_SERVICE_NAME]: 'opensip-cli',
|
|
102
|
+
[ATTR_SERVICE_VERSION]: readPackageVersion(cliEntryUrl),
|
|
103
|
+
}));
|
|
104
|
+
provider = new NodeTracerProvider({
|
|
105
|
+
resource,
|
|
106
|
+
// Bound each export attempt so a slow/dead collector fails fast instead of
|
|
107
|
+
// letting the batch processor block the final flush — see SHUTDOWN_TIMEOUT_MS.
|
|
108
|
+
spanProcessors: [
|
|
109
|
+
new BatchSpanProcessor(new OTLPTraceExporter({ timeoutMillis: SHUTDOWN_TIMEOUT_MS })),
|
|
110
|
+
],
|
|
111
|
+
});
|
|
112
|
+
// register() installs the GLOBAL provider + propagator + context manager,
|
|
113
|
+
// so core's getTracer (Phase 0) resolves to real tracers everywhere.
|
|
114
|
+
provider.register({
|
|
115
|
+
contextManager: new AsyncLocalStorageContextManager().enable(),
|
|
116
|
+
propagator: new W3CTraceContextPropagator(),
|
|
117
|
+
});
|
|
118
|
+
// Phase 2: MeterProvider for metrics (same resource + gate).
|
|
119
|
+
// Uses PeriodicExportingMetricReader; for short-lived CLI the interesting
|
|
120
|
+
// export happens on shutdownTelemetry. No-op when gate closed.
|
|
121
|
+
meterProvider = new MeterProvider({
|
|
122
|
+
resource,
|
|
123
|
+
readers: [
|
|
124
|
+
new PeriodicExportingMetricReader({
|
|
125
|
+
exporter: new OTLPMetricExporter({ timeoutMillis: SHUTDOWN_TIMEOUT_MS }),
|
|
126
|
+
exportIntervalMillis: 5000, // short for CLI; shutdown forces final export
|
|
127
|
+
}),
|
|
128
|
+
],
|
|
129
|
+
});
|
|
130
|
+
// Register globally so that core's getMeter() (and any tool code using
|
|
131
|
+
// @opentelemetry/api metrics.getMeter) resolves to this provider's meters.
|
|
132
|
+
// Without this, all CLI + tool metrics are silently no-op even when the
|
|
133
|
+
// OTLP endpoint is configured (the reader/exporter wiring is useless if
|
|
134
|
+
// nothing ever creates instruments against the provider).
|
|
135
|
+
metrics.setGlobalMeterProvider(meterProvider);
|
|
136
|
+
// Parent-trace nesting: extract the W3C traceparent the consumer passed via
|
|
137
|
+
// env so the run's spans attach under the parent trace. Unset ⇒ own trace.
|
|
138
|
+
const traceparent = hostEnv.get('TRACEPARENT');
|
|
139
|
+
if (traceparent) {
|
|
140
|
+
parentContext = new W3CTraceContextPropagator().extract(ROOT_CONTEXT, { traceparent }, defaultTextMapGetter);
|
|
141
|
+
// Only keep it if it actually yielded a valid span context.
|
|
142
|
+
if (!trace.getSpanContext(parentContext))
|
|
143
|
+
parentContext = undefined;
|
|
144
|
+
}
|
|
145
|
+
started = true;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* The parent context extracted from `TRACEPARENT`, or undefined. Exposed for
|
|
149
|
+
* tests; production code should prefer {@link runWithTelemetryContext}.
|
|
150
|
+
*/
|
|
151
|
+
export function parentTelemetryContext() {
|
|
152
|
+
return parentContext;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Run `fn` with the extracted parent context active (when present), so spans
|
|
156
|
+
* created inside nest under the consumer's trace. A plain pass-through when no
|
|
157
|
+
* parent context was extracted (or telemetry is disabled), so standalone runs
|
|
158
|
+
* pay nothing.
|
|
159
|
+
*/
|
|
160
|
+
export function runWithTelemetryContext(fn) {
|
|
161
|
+
return parentContext ? otelContext.with(parentContext, fn) : fn();
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Flush and shut down the tracer + meter providers (and stop profiling if active)
|
|
165
|
+
* so batched data export before the short-lived process exits.
|
|
166
|
+
* No-op when telemetry was never started. Swallows shutdown errors — a dead
|
|
167
|
+
* collector must not crash the CLI on the way out.
|
|
168
|
+
*/
|
|
169
|
+
export async function shutdownTelemetry() {
|
|
170
|
+
if (!started)
|
|
171
|
+
return;
|
|
172
|
+
// Stop profiling first (it may write files and is cheap).
|
|
173
|
+
try {
|
|
174
|
+
void stopProfiling();
|
|
175
|
+
}
|
|
176
|
+
catch {
|
|
177
|
+
// @swallow-ok best-effort profiling stop during SDK shutdown
|
|
178
|
+
}
|
|
179
|
+
const shutdowns = [];
|
|
180
|
+
if (provider) {
|
|
181
|
+
shutdowns.push(raceWithTimeout(provider.shutdown(), SHUTDOWN_TIMEOUT_MS).catch((error) => {
|
|
182
|
+
logger.warn('telemetry.shutdown.failed', {
|
|
183
|
+
evt: 'telemetry.shutdown.failed',
|
|
184
|
+
module: 'cli:telemetry',
|
|
185
|
+
kind: 'tracer',
|
|
186
|
+
err: error instanceof Error ? error.message : String(error),
|
|
187
|
+
});
|
|
188
|
+
}));
|
|
189
|
+
}
|
|
190
|
+
if (meterProvider) {
|
|
191
|
+
shutdowns.push(raceWithTimeout(meterProvider.shutdown(), SHUTDOWN_TIMEOUT_MS).catch((error) => {
|
|
192
|
+
logger.warn('telemetry.shutdown.failed', {
|
|
193
|
+
evt: 'telemetry.shutdown.failed',
|
|
194
|
+
module: 'cli:telemetry',
|
|
195
|
+
kind: 'meter',
|
|
196
|
+
err: error instanceof Error ? error.message : String(error),
|
|
197
|
+
});
|
|
198
|
+
}));
|
|
199
|
+
}
|
|
200
|
+
if (shutdowns.length > 0) {
|
|
201
|
+
await Promise.all(shutdowns);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* Resolve when `work` settles, or reject with {@link TimeoutError} after `ms` —
|
|
206
|
+
* whichever comes first. The deadline timer is `unref`'d so it never keeps the
|
|
207
|
+
* (short-lived CLI) event loop alive when the work wins, and cleared on the
|
|
208
|
+
* happy path. Exported for tests.
|
|
209
|
+
*/
|
|
210
|
+
export async function raceWithTimeout(work, ms) {
|
|
211
|
+
let timer;
|
|
212
|
+
try {
|
|
213
|
+
await Promise.race([
|
|
214
|
+
work,
|
|
215
|
+
new Promise((_, reject) => {
|
|
216
|
+
timer = setTimeout(() => reject(new TimeoutError(`telemetry shutdown exceeded ${String(ms)}ms`)), ms);
|
|
217
|
+
timer.unref();
|
|
218
|
+
}),
|
|
219
|
+
]);
|
|
220
|
+
}
|
|
221
|
+
finally {
|
|
222
|
+
if (timer)
|
|
223
|
+
clearTimeout(timer);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Test-only reset of module state. Production never calls this — the CLI is a
|
|
228
|
+
* one-shot process. Tests use it to exercise the gate across env permutations
|
|
229
|
+
* without a fresh module each time.
|
|
230
|
+
*/
|
|
231
|
+
export function resetTelemetryForTests() {
|
|
232
|
+
resetTelemetryState();
|
|
233
|
+
resetProfilingForTests();
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=sdk-init.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"sdk-init.js","sourceRoot":"","sources":["../../src/telemetry/sdk-init.ts"],"names":[],"mappings":"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"}
|
package/dist/ui/App.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* App — top-level Ink shell. Renders the banner + project line, then the
|
|
3
|
+
* result body via the shared view-model (`resultToView` → `renderToInk`).
|
|
4
|
+
*
|
|
5
|
+
* There is no per-result-type rendering here anymore: every CommandResult
|
|
6
|
+
* is expressed once as a ViewNode by `resultToView`, and the same node is
|
|
7
|
+
* rendered to plain text on the non-TTY path (bootstrap/render.ts). This
|
|
8
|
+
* shell owns only the chrome the plain-text path intentionally omits — the
|
|
9
|
+
* banner and the `ℹ Project:` line.
|
|
10
|
+
*/
|
|
11
|
+
import React from 'react';
|
|
12
|
+
import type { CommandResult } from '@opensip-cli/contracts';
|
|
13
|
+
import type { UiContext } from '@opensip-cli/core';
|
|
14
|
+
/** Project location for the shell's `ℹ Project:` line. */
|
|
15
|
+
export interface ProjectHeaderProps {
|
|
16
|
+
readonly root: string;
|
|
17
|
+
readonly walkedUp: number;
|
|
18
|
+
}
|
|
19
|
+
export interface AppProps {
|
|
20
|
+
readonly result: CommandResult;
|
|
21
|
+
/** Omitted for project-agnostic commands (init/configure/completion) and scopeless error paths. */
|
|
22
|
+
readonly projectHeader?: ProjectHeaderProps;
|
|
23
|
+
/** Presentation settings (banner size + version). Omitted on scopeless paths. */
|
|
24
|
+
readonly ui?: UiContext;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* App shell — the single source of truth for banner visibility. Renders
|
|
28
|
+
* the banner once for every human-facing command, then the result body
|
|
29
|
+
* through the shared view-model.
|
|
30
|
+
*/
|
|
31
|
+
export declare function App({ result, projectHeader, ui }: AppProps): React.ReactElement;
|
|
32
|
+
//# sourceMappingURL=App.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../src/ui/App.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAUH,OAAO,KAAK,MAAM,OAAO,CAAC;AAI1B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,0DAA0D;AAC1D,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,mGAAmG;IACnG,QAAQ,CAAC,aAAa,CAAC,EAAE,kBAAkB,CAAC;IAC5C,iFAAiF;IACjF,QAAQ,CAAC,EAAE,CAAC,EAAE,SAAS,CAAC;CACzB;AAUD;;;;GAIG;AACH,wBAAgB,GAAG,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,EAAE,EAAE,EAAE,QAAQ,GAAG,KAAK,CAAC,YAAY,CAwB/E"}
|
package/dist/ui/App.js
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* App — top-level Ink shell. Renders the banner + project line, then the
|
|
4
|
+
* result body via the shared view-model (`resultToView` → `renderToInk`).
|
|
5
|
+
*
|
|
6
|
+
* There is no per-result-type rendering here anymore: every CommandResult
|
|
7
|
+
* is expressed once as a ViewNode by `resultToView`, and the same node is
|
|
8
|
+
* rendered to plain text on the non-TTY path (bootstrap/render.ts). This
|
|
9
|
+
* shell owns only the chrome the plain-text path intentionally omits — the
|
|
10
|
+
* banner and the `ℹ Project:` line.
|
|
11
|
+
*/
|
|
12
|
+
import { Banner, UpdateHint, normalizeBannerSize, ProjectHeader, renderToInk, } from '@opensip-cli/cli-ui';
|
|
13
|
+
import { Box } from 'ink';
|
|
14
|
+
import { resultToView } from './result-to-view.js';
|
|
15
|
+
/**
|
|
16
|
+
* Result types that render WITHOUT the banner. `error` stays terse — a
|
|
17
|
+
* bare `✗` line reads better in CI logs and above a stack of error text.
|
|
18
|
+
* `--json` and `completion` never reach this component (they bypass the
|
|
19
|
+
* Ink render seam entirely), so they need no entry here.
|
|
20
|
+
*/
|
|
21
|
+
const BANNERLESS_RESULT_TYPES = new Set(['error']);
|
|
22
|
+
/**
|
|
23
|
+
* App shell — the single source of truth for banner visibility. Renders
|
|
24
|
+
* the banner once for every human-facing command, then the result body
|
|
25
|
+
* through the shared view-model.
|
|
26
|
+
*/
|
|
27
|
+
export function App({ result, projectHeader, ui }) {
|
|
28
|
+
const showBanner = !BANNERLESS_RESULT_TYPES.has(result.type);
|
|
29
|
+
const bannerSize = normalizeBannerSize(ui?.bannerSize);
|
|
30
|
+
// `mini` carries the project path inside its boxed card, so the separate
|
|
31
|
+
// `ℹ Project:` line would duplicate it — suppress ProjectHeader for mini.
|
|
32
|
+
const showProjectHeader = bannerSize !== 'mini' && projectHeader !== undefined;
|
|
33
|
+
return (_jsxs(Box, { flexDirection: "column", children: [showBanner && (_jsx(Banner, { size: bannerSize, version: ui?.version, projectPath: projectHeader?.root, walkedUp: projectHeader?.walkedUp, update: ui?.update })), showBanner && bannerSize === 'mini' && ui?.update !== undefined && _jsx(UpdateHint, {}), showBanner && showProjectHeader && (_jsx(ProjectHeader, { root: projectHeader.root, walkedUp: projectHeader.walkedUp })), renderToInk(resultToView(result))] }));
|
|
34
|
+
}
|
|
35
|
+
//# sourceMappingURL=App.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"App.js","sourceRoot":"","sources":["../../src/ui/App.tsx"],"names":[],"mappings":";AAAA;;;;;;;;;GASG;AAEH,OAAO,EACL,MAAM,EACN,UAAU,EACV,mBAAmB,EACnB,aAAa,EACb,WAAW,GACZ,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAG1B,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAmBnD;;;;;GAKG;AACH,MAAM,uBAAuB,GAAuC,IAAI,GAAG,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;AAEvF;;;;GAIG;AACH,MAAM,UAAU,GAAG,CAAC,EAAE,MAAM,EAAE,aAAa,EAAE,EAAE,EAAY;IACzD,MAAM,UAAU,GAAG,CAAC,uBAAuB,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAC7D,MAAM,UAAU,GAAG,mBAAmB,CAAC,EAAE,EAAE,UAAU,CAAC,CAAC;IACvD,yEAAyE;IACzE,0EAA0E;IAC1E,MAAM,iBAAiB,GAAG,UAAU,KAAK,MAAM,IAAI,aAAa,KAAK,SAAS,CAAC;IAC/E,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACxB,UAAU,IAAI,CACb,KAAC,MAAM,IACL,IAAI,EAAE,UAAU,EAChB,OAAO,EAAE,EAAE,EAAE,OAAO,EACpB,WAAW,EAAE,aAAa,EAAE,IAAI,EAChC,QAAQ,EAAE,aAAa,EAAE,QAAQ,EACjC,MAAM,EAAE,EAAE,EAAE,MAAM,GAClB,CACH,EACA,UAAU,IAAI,UAAU,KAAK,MAAM,IAAI,EAAE,EAAE,MAAM,KAAK,SAAS,IAAI,KAAC,UAAU,KAAG,EACjF,UAAU,IAAI,iBAAiB,IAAI,CAClC,KAAC,aAAa,IAAC,IAAI,EAAE,aAAa,CAAC,IAAI,EAAE,QAAQ,EAAE,aAAa,CAAC,QAAQ,GAAI,CAC9E,EACA,WAAW,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,IAC9B,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* render — entry point for the CLI's static Ink rendering.
|
|
3
|
+
*
|
|
4
|
+
* Single responsibility: render a completed `CommandResult` through the
|
|
5
|
+
* tool-agnostic `App` component. Tools that drive a stateful live view
|
|
6
|
+
* (fitness, graph) own their own renderers in their packages and
|
|
7
|
+
* register them via `cli.registerLiveView`; this file no longer mounts
|
|
8
|
+
* any tool-specific component. Layer 5 Phase 3 (audit 2026-05-23 F3).
|
|
9
|
+
*/
|
|
10
|
+
import { type ProjectHeaderProps } from './App.js';
|
|
11
|
+
import type { CommandResult } from '@opensip-cli/contracts';
|
|
12
|
+
import type { UiContext } from '@opensip-cli/core';
|
|
13
|
+
/** Render a static command result. */
|
|
14
|
+
export declare function renderApp(result: CommandResult, projectHeader?: ProjectHeaderProps, ui?: UiContext): Promise<void>;
|
|
15
|
+
//# sourceMappingURL=render.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../src/ui/render.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAKH,OAAO,EAAO,KAAK,kBAAkB,EAAE,MAAM,UAAU,CAAC;AAExD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AAEnD,sCAAsC;AACtC,wBAAsB,SAAS,CAC7B,MAAM,EAAE,aAAa,EACrB,aAAa,CAAC,EAAE,kBAAkB,EAClC,EAAE,CAAC,EAAE,SAAS,GACb,OAAO,CAAC,IAAI,CAAC,CAYf"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* render — entry point for the CLI's static Ink rendering.
|
|
4
|
+
*
|
|
5
|
+
* Single responsibility: render a completed `CommandResult` through the
|
|
6
|
+
* tool-agnostic `App` component. Tools that drive a stateful live view
|
|
7
|
+
* (fitness, graph) own their own renderers in their packages and
|
|
8
|
+
* register them via `cli.registerLiveView`; this file no longer mounts
|
|
9
|
+
* any tool-specific component. Layer 5 Phase 3 (audit 2026-05-23 F3).
|
|
10
|
+
*/
|
|
11
|
+
import { ThemeProvider } from '@opensip-cli/cli-ui';
|
|
12
|
+
import { App } from './App.js';
|
|
13
|
+
/** Render a static command result. */
|
|
14
|
+
export async function renderApp(result, projectHeader, ui) {
|
|
15
|
+
const { render } = await import('ink');
|
|
16
|
+
const app = render(_jsx(ThemeProvider, { children: _jsx(App, { result: result, projectHeader: projectHeader, ui: ui }) }));
|
|
17
|
+
app.unmount();
|
|
18
|
+
// Trailing newline so shell prompt starts on a new line
|
|
19
|
+
process.stdout.write('\n');
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=render.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/ui/render.tsx"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAGpD,OAAO,EAAE,GAAG,EAA2B,MAAM,UAAU,CAAC;AAKxD,sCAAsC;AACtC,MAAM,CAAC,KAAK,UAAU,SAAS,CAC7B,MAAqB,EACrB,aAAkC,EAClC,EAAc;IAEd,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,MAAM,CAAC,KAAK,CAAC,CAAC;IAEvC,MAAM,GAAG,GAAG,MAAM,CAChB,KAAC,aAAa,cACZ,KAAC,GAAG,IAAC,MAAM,EAAE,MAAM,EAAE,aAAa,EAAE,aAAa,EAAE,EAAE,EAAE,EAAE,GAAI,GAC/C,CACjB,CAAC;IAEF,GAAG,CAAC,OAAO,EAAE,CAAC;IACd,wDAAwD;IACxD,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AAC7B,CAAC"}
|