about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--users/Profpatsch/alacritty-change-color-scheme/alacritty-change-color-scheme.js521
-rw-r--r--users/Profpatsch/alacritty-change-color-scheme/package-lock.json296
-rw-r--r--users/Profpatsch/alacritty-change-color-scheme/package.json5
3 files changed, 755 insertions, 67 deletions
diff --git a/users/Profpatsch/alacritty-change-color-scheme/alacritty-change-color-scheme.js b/users/Profpatsch/alacritty-change-color-scheme/alacritty-change-color-scheme.js
index 7d3d961d3928..ca2e86a2b798 100644
--- a/users/Profpatsch/alacritty-change-color-scheme/alacritty-change-color-scheme.js
+++ b/users/Profpatsch/alacritty-change-color-scheme/alacritty-change-color-scheme.js
@@ -1,7 +1,22 @@
+//@ts-check
+
 // Step 3: Create the script
 const dbus = require('dbus-native');
 const fs = require('fs');
 const assert = require('assert');
+const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
+const {
+  BasicTracerProvider,
+  BatchSpanProcessor,
+  ConsoleSpanExporter,
+  SimpleSpanProcessor,
+} = require('@opentelemetry/sdk-trace-base');
+const opentelemetry = require('@opentelemetry/api');
+const { hrTime } = require('@opentelemetry/core');
+const { AsyncHooksContextManager } = require('@opentelemetry/context-async-hooks');
+const { EventEmitter } = require('stream');
+const { setTimeout } = require('node:timers/promises');
+const { promisify } = require('util');
 
 // NB: this code is like 80% copilot generated, and seriously missing error handling.
 // It might break at any time, but for now it seems to work lol.
@@ -21,8 +36,148 @@ console.log(`Dark theme: ${darkTheme}`);
 console.log(`Light theme: ${lightTheme}`);
 
 // Connect to the user session bus
+/** @type any */
 const bus = dbus.sessionBus();
 
+opentelemetry.diag.setLogger({ ...console, verbose: console.log });
+
+const exporter = new OTLPTraceExporter();
+const consoleExporter = new ConsoleSpanExporter();
+const provider = new BasicTracerProvider({
+  //@ts-ignore
+  resource: {
+    attributes: {
+      'service.name': 'alacritty-change-color-scheme',
+      // 'service.namespace': 'default',
+      // 'service.instance.id': 'alacritty-change-color-scheme-01',
+      // 'service.version': '0.0.1',
+    },
+  },
+  spanProcessors: [
+    // new BatchSpanProcessor(exporter, {
+    //   maxQueueSize: 100,
+    //   scheduledDelayMillis: 5000,
+    // }),
+    new SimpleSpanProcessor(exporter),
+    new SimpleSpanProcessor(consoleExporter),
+  ],
+});
+provider.register({
+  contextManager: new AsyncHooksContextManager().enable(),
+});
+
+const dbusSpanEmitter = new EventEmitter();
+
+dbusSpanEmitter.on('new-root-span', onNewRootSpan);
+
+/** @typedef {{spanId: string, name: string, parentId?: string, startTime?: opentelemetry.TimeInput, attributes?: opentelemetry.Attributes}} StartSpan */
+/** @typedef {{spanId: string, endTime?: opentelemetry.TimeInput}} EndSpan */
+
+/** @param {opentelemetry.Tracer} tracer,
+ *  @param {StartSpan} spanData */
+function emitNewRootSpanEvent(tracer, spanData) {
+  dbusSpanEmitter.emit('new-root-span', tracer, spanData);
+}
+
+/** @param {StartSpan} childSpanData */
+function emitNewChildSpanEvent(parentSpanId, childSpanData) {
+  dbusSpanEmitter.emit(`new-child-span-for/${parentSpanId}`, childSpanData);
+}
+
+/** @param {EndSpan} endSpanData */
+function emitEndSpanEvent(endSpanData) {
+  dbusSpanEmitter.emit(`end-span-for/${endSpanData.spanId}`, endSpanData);
+}
+
+/** @param {opentelemetry.Tracer} tracer
+ *  @param {StartSpan} spanData */
+function onNewRootSpan(tracer, spanData) {
+  console.log(`New span: ${spanData.spanId}`);
+  setupActiveSpan(tracer, spanData);
+}
+
+/** @param {opentelemetry.Tracer} tracer
+ *  @param {StartSpan} spanData */
+function setupActiveSpan(tracer, spanData) {
+  const SPAN_TIMEOUT = 1_000_000; // 1000 seconds
+  const SPAN_TIMEOUT_SHORT = 10_000; // 10 seconds
+  if (typeof spanData.startTime === 'number') {
+    console.warn(
+      'startTime is a number, not a hrTime tuple. This would use perfomance.now() in the wrong context, so we are ignoring it.',
+    );
+    spanData.startTime = undefined;
+  }
+  tracer.startActiveSpan(
+    spanData.name,
+    { startTime: spanData.startTime, attributes: spanData.attributes },
+    span => {
+      let activeContext = opentelemetry.context.active();
+
+      /** @param {{spanId: string, name: string}} childSpanData */
+      function onNewChildSpan(childSpanData) {
+        opentelemetry.context.with(activeContext, () => {
+          console.log(`New child span: ${childSpanData.spanId}`);
+          setupActiveSpan(tracer, childSpanData);
+        });
+      }
+      dbusSpanEmitter.on(`new-child-span-for/${spanData.spanId}`, onNewChildSpan);
+
+      const removeTimeoutOnEnd = new AbortController();
+
+      /** @param {{endTime?: opentelemetry.TimeInput}} endSpanData */
+      function onEndSpan(endSpanData) {
+        opentelemetry.context.with(activeContext, () => {
+          console.log(`End span: ${spanData.spanId}`);
+          if (typeof endSpanData.endTime === 'number') {
+            console.warn(
+              'endTime is a number, not a hrTime tuple. This would use perfomance.now() in the wrong context, so we are ignoring it.',
+            );
+            endSpanData.endTime = undefined;
+          }
+          span.end(endSpanData.endTime);
+          // we don’t remove the child span listener here, because in theory child spans don’t have to be inside parent spans.
+          // However, we only want to keep the child span listener around for a little more, so let’s set a shorter timeout here
+          setTimeout(SPAN_TIMEOUT_SHORT).then(() => {
+            dbusSpanEmitter.off(`new-child-span-for/${spanData.spanId}`, onNewChildSpan);
+          });
+          // remove the general long timeout belove
+          removeTimeoutOnEnd.abort();
+        });
+      }
+      dbusSpanEmitter.once(`end-span-for/${spanData.spanId}`, onEndSpan);
+
+      // don't keep spans contexts open forever if we never get a second message
+      // but we don’t end the spans, just remove the listeners.
+      setTimeout(SPAN_TIMEOUT, undefined, { signal: removeTimeoutOnEnd.signal })
+        .then(() => {
+          console.warn(
+            `Timeout for span ${spanData.spanId}, removing all event listeners`,
+          );
+          dbusSpanEmitter.off(`new-child-span-for/${spanData.spanId}`, onNewChildSpan);
+          dbusSpanEmitter.off(`end-span-for/${spanData.spanId}`, onEndSpan);
+        })
+        .catch(err => {
+          if (err.name === 'AbortError') {
+            // console.log(`Timeout for span ${spanData.spanId} was aborted`);
+          } else {
+            throw err;
+          }
+        });
+    },
+  );
+}
+
+// tracer.startActiveSpan('222', span => {
+//   span.setAttribute('blabla', 'alacritty-change-color-scheme');
+//   tracer.startActiveSpan('333', span => {
+//     span.setAttribute('foo', 'bar');
+//     span.end();
+//   });
+//   // span.setAttribute('service.name', 'alacritty-change-color-scheme');
+//   // span.setAttribute('service.version', '0.0.1');
+//   span.end();
+// });
+
 // set XDG_CONFIG_HOME if it's not set
 if (!process.env.XDG_CONFIG_HOME) {
   process.env.XDG_CONFIG_HOME = process.env.HOME + '/.config';
@@ -35,8 +190,12 @@ function getThemePathSync(theme) {
   return absolutePath;
 }
 
-// write new color scheme
-function writeAlacrittyColorConfig(cs, theme) {
+/** write new color scheme
+ *
+ * @param {'prefer-dark' | 'prefer-light'} cs
+ */
+function writeAlacrittyColorConfig(cs) {
+  const theme = cs === 'prefer-dark' ? darkTheme : lightTheme;
   console.log(`Writing color scheme ${cs} with theme ${theme}`);
   fs.writeFileSync(
     process.env.XDG_CONFIG_HOME + '/alacritty/alacritty-colors-autogen.toml',
@@ -46,35 +205,41 @@ general.import = ["${theme}"]`,
   );
 }
 
-// get the current value of the color scheme from dbus
-function getColorScheme() {
-  return new Promise((resolve, reject) => {
-    bus
-      .getService('org.freedesktop.portal.Desktop')
-      .getInterface(
-        '/org/freedesktop/portal/desktop',
-        'org.freedesktop.portal.Settings',
-        (err, iface) => {
-          if (err) {
-            reject(`error getting interface: ${err}`);
-            return;
-          }
+/** get the current value of the color scheme from dbus
+ *
+ * @returns {Promise<'prefer-dark' | 'prefer-light'>}
+ */
+async function getColorScheme() {
+  let service = bus.getService('org.freedesktop.portal.Desktop');
+  let iface = await promisifyMethodAnnotate(
+    service,
+    service.getInterface,
+    'Error getting interface',
+    '/org/freedesktop/portal/desktop',
+    'org.freedesktop.portal.Settings',
+  );
 
-          iface.ReadOne(
-            'org.gnome.desktop.interface',
-            'color-scheme',
-            (err, [_, [value]]) => {
-              if (err) {
-                reject(err);
-                return;
-              }
-
-              resolve(value);
-            },
-          );
-        },
-      );
-  });
+  const [_, [value]] = await promisifyMethodAnnotate(
+    iface,
+    iface.ReadOne,
+    'Error reading color scheme',
+    'org.gnome.desktop.interface',
+    'color-scheme',
+  );
+  assert(value === 'prefer-dark' || value === 'prefer-light');
+  return value;
+}
+
+/** promisify an object method and annotate any errors that get thrown
+ * @template {Function} A
+ * @param {object} obj
+ * @param {A} method
+ * @param {string} msg
+ * @param  {...any} args
+ * @returns {Promise<ReturnType<A>>}
+ */
+function promisifyMethodAnnotate(obj, method, msg, ...args) {
+  return promisify(method.bind(obj))(...args).catch(annotateErr(msg));
 }
 
 /** write respective alacritty config if the colorscheme changes.
@@ -90,40 +255,33 @@ function writeAlacrittyColorConfigIfDifferent(cs) {
   previous = cs;
 
   console.log(`Color scheme changed to ${cs}`);
-  writeAlacrittyColorConfig(cs, cs === 'prefer-dark' ? darkTheme : lightTheme);
+  writeAlacrittyColorConfig(cs);
 }
 
 /** Listen on the freedesktop SettingChanged dbus interface for the color-scheme setting to change. */
-function listenForColorschemeChange() {
-  return new Promise((resolve, reject) => {
-    bus
-      .getService('org.freedesktop.portal.Desktop')
-      .getInterface(
-        '/org/freedesktop/portal/desktop',
-        'org.freedesktop.portal.Settings',
-        (err, iface) => {
-          if (err) {
-            reject(`error getting interface: ${err}`);
-          }
+async function listenForColorschemeChange() {
+  const service = bus.getService('org.freedesktop.portal.Desktop');
 
-          // Listen for SettingChanged signals
-          iface.on('SettingChanged', (interfaceName, key, [_, [newValue]]) => {
-            if (
-              interfaceName === 'org.gnome.desktop.interface' &&
-              key == 'color-scheme'
-            ) {
-              writeAlacrittyColorConfigIfDifferent(newValue);
-            }
-          });
+  const iface = await promisifyMethodAnnotate(
+    service,
+    service.getInterface,
+    'Error getting interface',
+    '/org/freedesktop/portal/desktop',
+    'org.freedesktop.portal.Settings',
+  );
 
-          console.log('Listening for color scheme changes...');
-        },
-      );
+  // Listen for SettingChanged signals
+  iface.on('SettingChanged', (interfaceName, key, [_, [newValue]]) => {
+    if (interfaceName === 'org.gnome.desktop.interface' && key == 'color-scheme') {
+      writeAlacrittyColorConfigIfDifferent(newValue);
+    }
   });
+
+  console.log('Listening for color scheme changes...');
 }
 
 /** Create a dbus service that binds against the interface de.profpatsch.alacritty.ColorScheme and implements the method SetColorScheme */
-function exportColorSchemeDbusInterface() {
+async function exportColorSchemeDbusInterface() {
   console.log('Exporting color scheme interface de.profpatsch.alacritty.ColorScheme');
   const ifaceName = 'de.profpatsch.alacritty.ColorScheme';
   const iface = {
@@ -141,18 +299,18 @@ function exportColorSchemeDbusInterface() {
   };
 
   try {
-    bus.requestName(ifaceName, 0, (err, retCode) => {
-      if (err) {
-        console.error(
-          'Error requesting name for interface de.profpatsch.alacritty.ColorScheme',
-        );
-        console.error(err);
-      }
-      console.log(
-        `Request name returned ${retCode} for interface de.profpatsch.alacritty.ColorScheme`,
-      );
-    });
+    const retCode = await promisifyMethodAnnotate(
+      bus,
+      bus.requestName,
+      'Error requesting name for interface de.profpatsch.alacritty.ColorScheme',
+      ifaceName,
+      0,
+    );
+    console.log(
+      `Request name returned ${retCode} for interface de.profpatsch.alacritty.ColorScheme`,
+    );
     bus.exportInterface(ifaceImpl, '/de/profpatsch/alacritty/ColorScheme', iface);
+    bus.exportInterface(ifaceImpl, '/de/profpatsch/alacritty/ColorScheme2', iface);
     console.log('Exported interface de.profpatsch.alacritty.ColorScheme');
   } catch (err) {
     console.log('Error exporting interface de.profpatsch.alacritty.ColorScheme');
@@ -160,10 +318,239 @@ function exportColorSchemeDbusInterface() {
   }
 }
 
+/** Annotate an error as a promise .catch handler (rethrows the annotated error)
+ * @param {string} msg
+ * @returns {function(Error): void}
+ */
+function annotateErr(msg) {
+  return err => {
+    msg = err.message ? `${msg}: ${err.message}` : msg;
+    err.message = msg;
+    throw err;
+  };
+}
+
+/** @type any */
+const bus2 = dbus.sessionBus();
+async function exportOtelInterface() {
+  console.log('Exporting OpenTelemetry interface');
+
+  try {
+    const retCode = await promisifyMethodAnnotate(
+      bus2,
+      bus2.requestName,
+      'Error requesting name for interface de.profpatsch.otel.Tracer',
+
+      'de.profpatsch.otel.Tracer',
+      0,
+    );
+    console.log(
+      `Request name returned ${retCode} for interface de.profpatsch.otel.Tracer`,
+    );
+
+    const traceIface = {
+      name: 'de.profpatsch.otel.Tracer',
+      methods: {
+        // These both just take a json string as input
+        StartSpan: ['s', ''],
+        EndSpan: ['s', ''],
+        // An array of [(isStartSpan: bool, span: Span)]
+        // So that you don’t have to call dbus for every span
+        BatchSpans: ['a(bs)', ''],
+      },
+    };
+    /** @type {(arg: {tracer: opentelemetry.Tracer, tracerName: string}) => {StartSpan: (input: string) => void, EndSpan: (input: string) => void, BatchSpans: (input: [boolean, string][]) => void }} */
+    const traceImpl = tracer => ({
+      StartSpan: function (input) {
+        // TODO: actually verify json input
+        /** @type {StartSpan} */
+        const spanArgs = JSON.parse(input);
+        if (spanArgs.parentId === undefined) {
+          console.log(
+            `Tracing root span ${spanArgs.name} with tracer ${tracer.tracerName}`,
+          );
+          emitNewRootSpanEvent(tracer.tracer, spanArgs);
+        } else {
+          console.log(
+            `Tracing child span ${spanArgs.name} with tracer ${tracer.tracerName}`,
+          );
+          emitNewChildSpanEvent(spanArgs.parentId, spanArgs);
+        }
+      },
+      EndSpan: function (input) {
+        // TODO: actually verify json input
+        /** @type {EndSpan} */
+        const spanArgs = JSON.parse(input);
+        console.log(`Ending span ${spanArgs.spanId} with tracer ${tracer.tracerName}`);
+        emitEndSpanEvent(spanArgs);
+      },
+      BatchSpans: function (input) {
+        // lol
+        for (const [isStartSpan, span] of input) {
+          if (isStartSpan) {
+            traceImpl(tracer).StartSpan(span);
+          } else {
+            traceImpl(tracer).EndSpan(span);
+          }
+        }
+      },
+    });
+    bus2.exportInterface(
+      {
+        CreateTracer: function (tracerName) {
+          console.log(`Creating tracer with name ${tracerName}`);
+          const tracer = opentelemetry.trace.getTracer(tracerName, '0.0.1');
+          bus2.exportInterface(
+            traceImpl({ tracer, tracerName }),
+            `/de/profpatsch/otel/tracers/${tracerName}`,
+            traceIface,
+          );
+          return `/de/profpatsch/otel/tracers/${tracerName}`;
+        },
+      },
+      '/de/profpatsch/otel/TracerFactory',
+      {
+        name: 'de.profpatsch.otel.TracerFactory',
+        methods: {
+          CreateTracer: ['s', 's'],
+        },
+      },
+    );
+    console.log('Exported interface de.profpatsch.otel.TracerFactory');
+  } catch (err) {
+    console.log('Error exporting interface de.profpatsch.alacritty.ColorScheme');
+    console.error(err);
+  }
+}
+
+/** Returns the callsite of the function calling `getParentCallsite`, if any. */
+async function getParentCallsite() {
+  const getCallsites = (await import('callsites')).default;
+  const cs = getCallsites();
+  return cs[2] ?? cs[1] ?? null;
+}
+
+/** Calls the tracer dbus interface, sets up a tracer
+ *
+ * @param {string} tracerName The name of the tracer to set up
+ * @returns {Promise<{
+ *   StartSpan: (spanData: StartSpan) => Promise<void>,
+ *   EndSpan: (spanData: EndSpan) => Promise<void>,
+ *   BatchSpans: (spans: ([true, StartSpan] | [false, EndSpan])[]) => Promise<void>
+ * }>}
+ */
+async function setupTracer(tracerName) {
+  const parentCallsite = await getParentCallsite();
+  console.log(`Setting up tracer ${tracerName} from ${parentCallsite?.getFileName()}`);
+  const service = bus2.getService('de.profpatsch.otel.Tracer');
+  const iface = await promisifyMethodAnnotate(
+    service,
+    service.getInterface,
+    'Error getting interface',
+    '/de/profpatsch/otel/TracerFactory',
+    'de.profpatsch.otel.TracerFactory',
+  );
+  const path = await promisifyMethodAnnotate(
+    iface,
+    iface.CreateTracer,
+    'Error creating tracer',
+    tracerName,
+  );
+  const tracerIface = await promisifyMethodAnnotate(
+    service,
+    service.getInterface,
+    'Error getting interface',
+    path,
+    'de.profpatsch.otel.Tracer',
+  );
+
+  function StartSpan(spanData) {
+    return promisifyMethodAnnotate(
+      tracerIface,
+      tracerIface.StartSpan,
+      'Error starting span',
+      JSON.stringify(spanData),
+    );
+  }
+  function EndSpan(spanData) {
+    return promisifyMethodAnnotate(
+      tracerIface,
+      tracerIface.EndSpan,
+      'Error ending span',
+      JSON.stringify(spanData),
+    );
+  }
+  function BatchSpans(spans) {
+    return promisifyMethodAnnotate(
+      tracerIface,
+      tracerIface.BatchSpans,
+      'Error batching spans',
+      spans.map(([isStartSpan, span]) => [isStartSpan, JSON.stringify(span)]),
+    );
+  }
+  return {
+    StartSpan,
+    EndSpan,
+    BatchSpans,
+  };
+}
+
 async function main() {
+  await exportOtelInterface();
+
+  const tracer = await setupTracer('hello');
+  await tracer.StartSpan({
+    name: 'hello',
+    spanId: 'hello',
+    startTime: hrTime(),
+    attributes: {
+      foo: 'bar',
+    },
+  });
+  await tracer.StartSpan({
+    name: 'world',
+    spanId: 'world',
+    parentId: 'hello',
+    attributes: {
+      bar: 'baz',
+    },
+  });
+  await tracer.EndSpan({
+    spanId: 'world',
+    endTime: hrTime(),
+  });
+  await tracer.EndSpan({
+    spanId: 'hello',
+    endTime: hrTime(),
+  });
+  const t = performance.now();
+  await tracer.BatchSpans([
+    [
+      true,
+      {
+        name: 'batchy',
+        spanId: 'batchy',
+        startTime: t,
+        attributes: { foo: 'bar' },
+      },
+    ],
+    [
+      true,
+      {
+        name: 'worldy',
+        spanId: 'worldy',
+        parentId: 'batchy',
+        startTime: t + 100,
+        attributes: { bar: 'baz' },
+      },
+    ],
+    [false, { spanId: 'worldy', endTime: t + 500 }],
+    [false, { spanId: 'batchy', endTime: t + 1000 }],
+  ]);
+
   // TODO: proper error handling, through proper callback promises for dbus function.
 
-  exportColorSchemeDbusInterface();
+  await exportColorSchemeDbusInterface();
 
   // get the current color scheme
   const currentColorScheme = await getColorScheme();
diff --git a/users/Profpatsch/alacritty-change-color-scheme/package-lock.json b/users/Profpatsch/alacritty-change-color-scheme/package-lock.json
index 38e2b437c432..19f75c42579b 100644
--- a/users/Profpatsch/alacritty-change-color-scheme/package-lock.json
+++ b/users/Profpatsch/alacritty-change-color-scheme/package-lock.json
@@ -9,7 +9,267 @@
       "version": "1.0.0",
       "license": "ISC",
       "dependencies": {
+        "@opentelemetry/api": "^1.9.0",
+        "@opentelemetry/context-async-hooks": "^1.29.0",
+        "@opentelemetry/core": "^1.29.0",
+        "@opentelemetry/exporter-trace-otlp-http": "^0.56.0",
+        "@opentelemetry/sdk-trace-base": "^1.29.0",
         "dbus-native": "^0.4.0"
+      },
+      "bin": {
+        "alacritty-change-color-scheme": "alacritty-change-color-scheme.js"
+      }
+    },
+    "node_modules/@opentelemetry/api": {
+      "version": "1.9.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz",
+      "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=8.0.0"
+      }
+    },
+    "node_modules/@opentelemetry/api-logs": {
+      "version": "0.56.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.56.0.tgz",
+      "integrity": "sha512-Wr39+94UNNG3Ei9nv3pHd4AJ63gq5nSemMRpCd8fPwDL9rN3vK26lzxfH27mw16XzOSO+TpyQwBAMaLxaPWG0g==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      },
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@opentelemetry/context-async-hooks": {
+      "version": "1.29.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/context-async-hooks/-/context-async-hooks-1.29.0.tgz",
+      "integrity": "sha512-TKT91jcFXgHyIDF1lgJF3BHGIakn6x0Xp7Tq3zoS3TMPzT9IlP0xEavWP8C1zGjU9UmZP2VR1tJhW9Az1A3w8Q==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": ">=1.0.0 <1.10.0"
+      }
+    },
+    "node_modules/@opentelemetry/core": {
+      "version": "1.29.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-1.29.0.tgz",
+      "integrity": "sha512-gmT7vAreXl0DTHD2rVZcw3+l2g84+5XiHIqdBUxXbExymPCvSsGOpiwMmn8nkiJur28STV31wnhIDrzWDPzjfA==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/semantic-conventions": "1.28.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": ">=1.0.0 <1.10.0"
+      }
+    },
+    "node_modules/@opentelemetry/exporter-trace-otlp-http": {
+      "version": "0.56.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.56.0.tgz",
+      "integrity": "sha512-vqVuJvcwameA0r0cNrRzrZqPLB0otS+95g0XkZdiKOXUo81wYdY6r4kyrwz4nSChqTBEFm0lqi/H2OWGboOa6g==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "1.29.0",
+        "@opentelemetry/otlp-exporter-base": "0.56.0",
+        "@opentelemetry/otlp-transformer": "0.56.0",
+        "@opentelemetry/resources": "1.29.0",
+        "@opentelemetry/sdk-trace-base": "1.29.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@opentelemetry/otlp-exporter-base": {
+      "version": "0.56.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.56.0.tgz",
+      "integrity": "sha512-eURvv0fcmBE+KE1McUeRo+u0n18ZnUeSc7lDlW/dzlqFYasEbsztTK4v0Qf8C4vEY+aMTjPKUxBG0NX2Te3Pmw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "1.29.0",
+        "@opentelemetry/otlp-transformer": "0.56.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@opentelemetry/otlp-transformer": {
+      "version": "0.56.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.56.0.tgz",
+      "integrity": "sha512-kVkH/W2W7EpgWWpyU5VnnjIdSD7Y7FljQYObAQSKdRcejiwMj2glypZtUdfq1LTJcv4ht0jyTrw1D3CCxssNtQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/api-logs": "0.56.0",
+        "@opentelemetry/core": "1.29.0",
+        "@opentelemetry/resources": "1.29.0",
+        "@opentelemetry/sdk-logs": "0.56.0",
+        "@opentelemetry/sdk-metrics": "1.29.0",
+        "@opentelemetry/sdk-trace-base": "1.29.0",
+        "protobufjs": "^7.3.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "^1.3.0"
+      }
+    },
+    "node_modules/@opentelemetry/resources": {
+      "version": "1.29.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-1.29.0.tgz",
+      "integrity": "sha512-s7mLXuHZE7RQr1wwweGcaRp3Q4UJJ0wazeGlc/N5/XSe6UyXfsh1UQGMADYeg7YwD+cEdMtU1yJAUXdnFzYzyQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "1.29.0",
+        "@opentelemetry/semantic-conventions": "1.28.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": ">=1.0.0 <1.10.0"
+      }
+    },
+    "node_modules/@opentelemetry/sdk-logs": {
+      "version": "0.56.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.56.0.tgz",
+      "integrity": "sha512-OS0WPBJF++R/cSl+terUjQH5PebloidB1Jbbecgg2rnCmQbTST9xsRes23bLfDQVRvmegmHqDh884h0aRdJyLw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/api-logs": "0.56.0",
+        "@opentelemetry/core": "1.29.0",
+        "@opentelemetry/resources": "1.29.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": ">=1.4.0 <1.10.0"
+      }
+    },
+    "node_modules/@opentelemetry/sdk-metrics": {
+      "version": "1.29.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-1.29.0.tgz",
+      "integrity": "sha512-MkVtuzDjXZaUJSuJlHn6BSXjcQlMvHcsDV7LjY4P6AJeffMa4+kIGDjzsCf6DkAh6Vqlwag5EWEam3KZOX5Drw==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "1.29.0",
+        "@opentelemetry/resources": "1.29.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": ">=1.3.0 <1.10.0"
+      }
+    },
+    "node_modules/@opentelemetry/sdk-trace-base": {
+      "version": "1.29.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-1.29.0.tgz",
+      "integrity": "sha512-hEOpAYLKXF3wGJpXOtWsxEtqBgde0SCv+w+jvr3/UusR4ll3QrENEGnSl1WDCyRrpqOQ5NCNOvZch9UFVa7MnQ==",
+      "license": "Apache-2.0",
+      "dependencies": {
+        "@opentelemetry/core": "1.29.0",
+        "@opentelemetry/resources": "1.29.0",
+        "@opentelemetry/semantic-conventions": "1.28.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": ">=1.0.0 <1.10.0"
+      }
+    },
+    "node_modules/@opentelemetry/semantic-conventions": {
+      "version": "1.28.0",
+      "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.28.0.tgz",
+      "integrity": "sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==",
+      "license": "Apache-2.0",
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/@protobufjs/aspromise": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
+      "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@protobufjs/base64": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
+      "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@protobufjs/codegen": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
+      "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@protobufjs/eventemitter": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
+      "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@protobufjs/fetch": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
+      "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@protobufjs/aspromise": "^1.1.1",
+        "@protobufjs/inquire": "^1.1.0"
+      }
+    },
+    "node_modules/@protobufjs/float": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
+      "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@protobufjs/inquire": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
+      "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@protobufjs/path": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
+      "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@protobufjs/pool": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
+      "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@protobufjs/utf8": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
+      "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==",
+      "license": "BSD-3-Clause"
+    },
+    "node_modules/@types/node": {
+      "version": "22.10.1",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.1.tgz",
+      "integrity": "sha512-qKgsUwfHZV2WCWLAnVP1JqnpE6Im6h3Y0+fYgMTasNQ7V++CBX5OT1as0g0f+OyubbFqhf6XVNIsmN4IIhEgGQ==",
+      "license": "MIT",
+      "dependencies": {
+        "undici-types": "~6.20.0"
       }
     },
     "node_modules/abstract-socket": {
@@ -152,6 +412,36 @@
         "through": "~2.3"
       }
     },
+    "node_modules/protobufjs": {
+      "version": "7.4.0",
+      "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz",
+      "integrity": "sha512-mRUWCc3KUU4w1jU8sGxICXH/gNS94DvI1gxqDvBzhj1JpcsimQkYiOJfwsPUykUI5ZaspFbSgmBLER8IrQ3tqw==",
+      "hasInstallScript": true,
+      "license": "BSD-3-Clause",
+      "dependencies": {
+        "@protobufjs/aspromise": "^1.1.2",
+        "@protobufjs/base64": "^1.1.2",
+        "@protobufjs/codegen": "^2.0.4",
+        "@protobufjs/eventemitter": "^1.1.0",
+        "@protobufjs/fetch": "^1.1.0",
+        "@protobufjs/float": "^1.0.2",
+        "@protobufjs/inquire": "^1.1.0",
+        "@protobufjs/path": "^1.1.2",
+        "@protobufjs/pool": "^1.1.0",
+        "@protobufjs/utf8": "^1.1.0",
+        "@types/node": ">=13.7.0",
+        "long": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=12.0.0"
+      }
+    },
+    "node_modules/protobufjs/node_modules/long": {
+      "version": "5.2.3",
+      "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz",
+      "integrity": "sha512-lcHwpNoggQTObv5apGNCTdJrO69eHOZMi4BNC+rTLER8iHAqGrUVeLh/irVIM7zTw2bOXA8T6uNPeujwOLg/2Q==",
+      "license": "Apache-2.0"
+    },
     "node_modules/put": {
       "version": "0.0.6",
       "resolved": "https://registry.npmjs.org/put/-/put-0.0.6.tgz",
@@ -215,6 +505,12 @@
       "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
       "license": "MIT"
     },
+    "node_modules/undici-types": {
+      "version": "6.20.0",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
+      "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
+      "license": "MIT"
+    },
     "node_modules/wordwrap": {
       "version": "0.0.3",
       "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz",
diff --git a/users/Profpatsch/alacritty-change-color-scheme/package.json b/users/Profpatsch/alacritty-change-color-scheme/package.json
index 720ee4b15657..e1b4af0c01b2 100644
--- a/users/Profpatsch/alacritty-change-color-scheme/package.json
+++ b/users/Profpatsch/alacritty-change-color-scheme/package.json
@@ -10,6 +10,11 @@
   "license": "ISC",
   "description": "",
   "dependencies": {
+    "@opentelemetry/api": "^1.9.0",
+    "@opentelemetry/context-async-hooks": "^1.29.0",
+    "@opentelemetry/core": "^1.29.0",
+    "@opentelemetry/exporter-trace-otlp-http": "^0.56.0",
+    "@opentelemetry/sdk-trace-base": "^1.29.0",
     "dbus-native": "^0.4.0"
   }
 }