@lorekit/cli 1.24.0 → 1.25.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/package.json +1 -1
- package/src/mcp.mjs +20 -0
- package/src/telemetry.mjs +48 -6
package/package.json
CHANGED
package/src/mcp.mjs
CHANGED
|
@@ -124,16 +124,36 @@ export function mcpToRestBase(mcpEndpointUrl) {
|
|
|
124
124
|
* @param {number} [opts.timeoutMs=10000]
|
|
125
125
|
* @param {string} [opts.traceparent] - W3C traceparent header value
|
|
126
126
|
*/
|
|
127
|
+
/**
|
|
128
|
+
* Normalise a client-supplied usage correlation id (a PR ref, session id, or CI
|
|
129
|
+
* job id). Bounded + charset-restricted to match the server's `parseCorrelationId`
|
|
130
|
+
* (supabase/functions/_shared/usage-stats.ts); returns null for empty/over-long/
|
|
131
|
+
* out-of-charset input so a bad value is simply not sent. Zero-dep (the CLI does
|
|
132
|
+
* not import mcp-core), so the small regex is duplicated intentionally.
|
|
133
|
+
*/
|
|
134
|
+
export function normalizeCorrelationId(raw) {
|
|
135
|
+
if (typeof raw !== 'string') return null;
|
|
136
|
+
const t = raw.trim();
|
|
137
|
+
if (!t || t.length > 200) return null;
|
|
138
|
+
return /^[A-Za-z0-9_\-./:#@]+$/.test(t) ? t : null;
|
|
139
|
+
}
|
|
140
|
+
|
|
127
141
|
export async function restFetch(baseUrl, token, path, { method = 'GET', body, timeoutMs = 10000, traceparent } = {}) {
|
|
128
142
|
const controller = new AbortController();
|
|
129
143
|
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
130
144
|
try {
|
|
131
145
|
const url = `${baseUrl}${path}`;
|
|
146
|
+
// Opt-in usage correlation: when LOREKIT_CORRELATION_ID is set (e.g. by a CI
|
|
147
|
+
// job or a hook to a PR/session id), tag every REST call so GET
|
|
148
|
+
// /memories/usage?correlation_id=… can report "usage for this PR". Absent env
|
|
149
|
+
// ⇒ no header ⇒ existing behaviour unchanged.
|
|
150
|
+
const correlationId = normalizeCorrelationId(process.env.LOREKIT_CORRELATION_ID);
|
|
132
151
|
const headers = {
|
|
133
152
|
accept: 'application/json',
|
|
134
153
|
...(body !== undefined ? { 'content-type': 'application/json' } : {}),
|
|
135
154
|
...(token ? { authorization: `Bearer ${token}` } : {}),
|
|
136
155
|
...(traceparent ? { traceparent } : {}),
|
|
156
|
+
...(correlationId ? { 'x-lorekit-correlation-id': correlationId } : {}),
|
|
137
157
|
};
|
|
138
158
|
const res = await fetch(url, {
|
|
139
159
|
method,
|
package/src/telemetry.mjs
CHANGED
|
@@ -165,16 +165,58 @@ function toOtlpAttributes(attributes) {
|
|
|
165
165
|
return Object.entries(attributes).map(([key, value]) => ({ key, value: toOtlpValue(value) }));
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
-
|
|
169
|
-
|
|
168
|
+
// OTel `os.type` / `host.arch` use their own bounded enum vocabularies, which
|
|
169
|
+
// are NOT identical to Node's `process.platform` / `process.arch` spellings.
|
|
170
|
+
// Node's `win32` / `sunos` and `x64` / `ia32` / `arm` are the ones that differ;
|
|
171
|
+
// emitting them verbatim produces off-registry attribute values that a Dash0 /
|
|
172
|
+
// OTel-native backend can't group with telemetry from other SDKs. Map the known
|
|
173
|
+
// divergences and pass anything already-canonical (or unknown) through.
|
|
174
|
+
// os.type: https://opentelemetry.io/docs/specs/semconv/registry/attributes/os/
|
|
175
|
+
// host.arch: https://opentelemetry.io/docs/specs/semconv/registry/attributes/host/
|
|
176
|
+
const OS_TYPE_BY_PLATFORM = { win32: 'windows', sunos: 'solaris' };
|
|
177
|
+
// Node's `process.arch` reports `ppc` for 32-bit PowerPC; the OTel `host.arch`
|
|
178
|
+
// registry value for it is `ppc32` (its `ppc64` spelling already matches Node).
|
|
179
|
+
const HOST_ARCH_BY_PROCESS_ARCH = { x64: 'amd64', ia32: 'x86', arm: 'arm32', ppc: 'ppc32' };
|
|
180
|
+
|
|
181
|
+
/** Map a Node `process.platform` value to an OTel `os.type` registry value. */
|
|
182
|
+
export function normalizeOsType(platform) {
|
|
183
|
+
return OS_TYPE_BY_PLATFORM[platform] ?? platform;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/** Map a Node `process.arch` value to an OTel `host.arch` registry value. */
|
|
187
|
+
export function normalizeHostArch(arch) {
|
|
188
|
+
return HOST_ARCH_BY_PROCESS_ARCH[arch] ?? arch;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Resolve the `deployment.environment.name` resource value, or `undefined` when
|
|
193
|
+
* none is set. The CLI runs on end-users' machines, so — unlike the edge/web/
|
|
194
|
+
* mcp-node deployments — it has no ambient environment and deliberately OMITS
|
|
195
|
+
* the attribute by default. It is emitted ONLY when explicitly overridden via
|
|
196
|
+
* `DEPLOYMENT_ENVIRONMENT` (falling back to `OTEL_DEPLOYMENT_ENVIRONMENT`) — the
|
|
197
|
+
* same single, env-driven knob the edge honours, which the correlated-trace
|
|
198
|
+
* harness (`scripts/emit-correlated-trace.mts`) uses to stamp `test`.
|
|
199
|
+
* @param {object} [env] defaults to process.env
|
|
200
|
+
*/
|
|
201
|
+
export function resolveDeploymentEnvironment(env = process.env) {
|
|
202
|
+
const raw = env.DEPLOYMENT_ENVIRONMENT ?? env.OTEL_DEPLOYMENT_ENVIRONMENT;
|
|
203
|
+
const value = raw !== undefined ? String(raw).trim() : '';
|
|
204
|
+
return value || undefined;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
function resourceAttributes(version, env = process.env) {
|
|
208
|
+
const attrs = [
|
|
170
209
|
{ key: 'service.name', value: { stringValue: 'cli' } },
|
|
171
210
|
{ key: 'service.namespace', value: { stringValue: 'lorekit' } },
|
|
172
211
|
{ key: 'service.version', value: { stringValue: String(version) } },
|
|
173
212
|
{ key: 'process.runtime.name', value: { stringValue: 'nodejs' } },
|
|
174
213
|
{ key: 'process.runtime.version', value: { stringValue: process.versions.node } },
|
|
175
|
-
{ key: 'os.type', value: { stringValue: process.platform } },
|
|
176
|
-
{ key: 'host.arch', value: { stringValue: process.arch } },
|
|
214
|
+
{ key: 'os.type', value: { stringValue: normalizeOsType(process.platform) } },
|
|
215
|
+
{ key: 'host.arch', value: { stringValue: normalizeHostArch(process.arch) } },
|
|
177
216
|
];
|
|
217
|
+
const deploymentEnv = resolveDeploymentEnvironment(env);
|
|
218
|
+
if (deploymentEnv) attrs.push({ key: 'deployment.environment.name', value: { stringValue: deploymentEnv } });
|
|
219
|
+
return attrs;
|
|
178
220
|
}
|
|
179
221
|
|
|
180
222
|
// ── Payload builders (pure — unit-tested) ─────────────────────────────────────
|
|
@@ -200,7 +242,7 @@ export function buildTracePayload({ version, name, attributes, startMs, endMs, s
|
|
|
200
242
|
resource: { attributes: resourceAttributes(version) },
|
|
201
243
|
scopeSpans: [
|
|
202
244
|
{
|
|
203
|
-
scope: { name: '
|
|
245
|
+
scope: { name: 'cli', version: String(version) },
|
|
204
246
|
spans: [
|
|
205
247
|
{
|
|
206
248
|
traceId: traceId ?? randHex(16),
|
|
@@ -230,7 +272,7 @@ export function buildMetricsPayload({ version, attributes, startMs, endMs }) {
|
|
|
230
272
|
resource: { attributes: resourceAttributes(version) },
|
|
231
273
|
scopeMetrics: [
|
|
232
274
|
{
|
|
233
|
-
scope: { name: '
|
|
275
|
+
scope: { name: 'cli', version: String(version) },
|
|
234
276
|
metrics: [
|
|
235
277
|
{
|
|
236
278
|
name: 'lorekit.cli.invocations',
|