@alisio/plugin-telemetry 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/.agents/skills/telemetry/SKILL.md +52 -0
- package/LICENSE +21 -0
- package/README.md +188 -0
- package/cover.svg +54 -0
- package/dist/commands.d.ts +19 -0
- package/dist/commands.d.ts.map +1 -0
- package/dist/commands.js +298 -0
- package/dist/commands.js.map +1 -0
- package/dist/config.d.ts +145 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +524 -0
- package/dist/config.js.map +1 -0
- package/dist/database.d.ts +16 -0
- package/dist/database.d.ts.map +1 -0
- package/dist/database.js +67 -0
- package/dist/database.js.map +1 -0
- package/dist/events.d.ts +135 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +255 -0
- package/dist/events.js.map +1 -0
- package/dist/format.d.ts +36 -0
- package/dist/format.d.ts.map +1 -0
- package/dist/format.js +49 -0
- package/dist/format.js.map +1 -0
- package/dist/index.d.ts +38 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +78 -0
- package/dist/index.js.map +1 -0
- package/dist/otlp.d.ts +69 -0
- package/dist/otlp.d.ts.map +1 -0
- package/dist/otlp.js +616 -0
- package/dist/otlp.js.map +1 -0
- package/dist/redact.d.ts +36 -0
- package/dist/redact.d.ts.map +1 -0
- package/dist/redact.js +163 -0
- package/dist/redact.js.map +1 -0
- package/dist/runtime.d.ts +80 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.js +290 -0
- package/dist/runtime.js.map +1 -0
- package/dist/store.d.ts +188 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +539 -0
- package/dist/store.js.map +1 -0
- package/dist/tools.d.ts +21 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +292 -0
- package/dist/tools.js.map +1 -0
- package/dist/version.d.ts +3 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +3 -0
- package/dist/version.js.map +1 -0
- package/package.json +54 -0
package/dist/config.js
ADDED
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration, path resolution and safe persistence.
|
|
3
|
+
*
|
|
4
|
+
* PRECEDENCE (highest first): explicit non-secret command/tool argument, then
|
|
5
|
+
* environment, then the config file, then built-in safe defaults. The config
|
|
6
|
+
* file lives at `<configHome>/telemetry/config.json`; the database lives at
|
|
7
|
+
* `ALISIO_TELEMETRY_DB` or `<stateHome>/telemetry/telemetry.sqlite`.
|
|
8
|
+
*
|
|
9
|
+
* CREDENTIALS ARE NEVER PERSISTED. The OTLP token is read from an environment
|
|
10
|
+
* variable at export time only. The config file may reference *which* variable
|
|
11
|
+
* holds it (`otlp.tokenEnv`) but never its value. Secret-shaped keys anywhere in
|
|
12
|
+
* the file are rejected with an actionable message rather than silently kept.
|
|
13
|
+
*
|
|
14
|
+
* FAIL CLOSED. An invalid file or environment produces a structured error, not a
|
|
15
|
+
* throw that could crash the host. The plugin disables telemetry and surfaces the
|
|
16
|
+
* message; the agent run continues.
|
|
17
|
+
*/
|
|
18
|
+
import { chmodSync, closeSync, fsyncSync, mkdirSync, openSync, readFileSync, renameSync, writeSync, } from "node:fs";
|
|
19
|
+
import { homedir } from "node:os";
|
|
20
|
+
import { dirname, join } from "node:path";
|
|
21
|
+
export const ENV_KEYS = {
|
|
22
|
+
db: "ALISIO_TELEMETRY_DB",
|
|
23
|
+
configHome: "ALISIO_CONFIG_HOME",
|
|
24
|
+
stateHome: "ALISIO_STATE_HOME",
|
|
25
|
+
xdgConfigHome: "XDG_CONFIG_HOME",
|
|
26
|
+
xdgStateHome: "XDG_STATE_HOME",
|
|
27
|
+
home: "HOME",
|
|
28
|
+
retentionDays: "ALISIO_TELEMETRY_RETENTION_DAYS",
|
|
29
|
+
otlpEnabled: "ALISIO_TELEMETRY_OTLP_ENABLED",
|
|
30
|
+
otlpEndpoint: "ALISIO_TELEMETRY_OTLP_ENDPOINT",
|
|
31
|
+
otlpTokenEnv: "ALISIO_TELEMETRY_OTLP_TOKEN_ENV",
|
|
32
|
+
otlpInstanceId: "ALISIO_TELEMETRY_OTLP_INSTANCE_ID",
|
|
33
|
+
serviceName: "ALISIO_TELEMETRY_SERVICE_NAME",
|
|
34
|
+
environment: "ALISIO_TELEMETRY_ENVIRONMENT",
|
|
35
|
+
samplingRatio: "ALISIO_TELEMETRY_SAMPLING_RATIO",
|
|
36
|
+
capturePrompts: "ALISIO_TELEMETRY_CAPTURE_PROMPTS",
|
|
37
|
+
captureCompletions: "ALISIO_TELEMETRY_CAPTURE_COMPLETIONS",
|
|
38
|
+
captureToolArguments: "ALISIO_TELEMETRY_CAPTURE_TOOL_ARGUMENTS",
|
|
39
|
+
captureToolResults: "ALISIO_TELEMETRY_CAPTURE_TOOL_RESULTS",
|
|
40
|
+
redactionMode: "ALISIO_TELEMETRY_REDACTION_MODE",
|
|
41
|
+
otlpSignals: "ALISIO_TELEMETRY_OTLP_SIGNALS",
|
|
42
|
+
otlpGzip: "ALISIO_TELEMETRY_OTLP_GZIP",
|
|
43
|
+
batchSize: "ALISIO_TELEMETRY_BATCH_SIZE",
|
|
44
|
+
flushIntervalMs: "ALISIO_TELEMETRY_FLUSH_INTERVAL_MS",
|
|
45
|
+
maxQueue: "ALISIO_TELEMETRY_MAX_QUEUE",
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* The credential itself is intentionally NOT listed above: it is read directly
|
|
49
|
+
* from this name at export time and never flows through configuration.
|
|
50
|
+
*/
|
|
51
|
+
export const OTLP_TOKEN_ENV = "ALISIO_TELEMETRY_OTLP_TOKEN";
|
|
52
|
+
export class ConfigError extends Error {
|
|
53
|
+
constructor(message) {
|
|
54
|
+
super(message);
|
|
55
|
+
this.name = "ConfigError";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
function fail(message) {
|
|
59
|
+
throw new ConfigError(message);
|
|
60
|
+
}
|
|
61
|
+
export function createDefaultConfig() {
|
|
62
|
+
return {
|
|
63
|
+
version: 1,
|
|
64
|
+
retentionDays: 30,
|
|
65
|
+
capture: { prompts: false, completions: false, toolArguments: false, toolResults: false },
|
|
66
|
+
redaction: { mode: "strict" },
|
|
67
|
+
otlp: {
|
|
68
|
+
enabled: false,
|
|
69
|
+
endpoint: null,
|
|
70
|
+
headers: {},
|
|
71
|
+
tokenEnv: OTLP_TOKEN_ENV,
|
|
72
|
+
instanceId: "",
|
|
73
|
+
serviceName: "alisio",
|
|
74
|
+
environment: "",
|
|
75
|
+
samplingRatio: 1,
|
|
76
|
+
signals: { traces: true, logs: true, metrics: false },
|
|
77
|
+
gzip: false,
|
|
78
|
+
maxAttempts: 5,
|
|
79
|
+
timeoutMs: 10_000,
|
|
80
|
+
batchSize: 200,
|
|
81
|
+
},
|
|
82
|
+
batch: { batchSize: 50, flushIntervalMs: 5_000, maxQueue: 10_000 },
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
/** Resolve config/state homes with the documented precedence. */
|
|
86
|
+
export function resolveTelemetryPaths(env = process.env, overrides = {}) {
|
|
87
|
+
const home = env[ENV_KEYS.home]?.trim() || homedir();
|
|
88
|
+
const xdgConfig = env[ENV_KEYS.xdgConfigHome]?.trim();
|
|
89
|
+
const xdgState = env[ENV_KEYS.xdgStateHome]?.trim();
|
|
90
|
+
const configHome = env[ENV_KEYS.configHome]?.trim() ||
|
|
91
|
+
(xdgConfig ? join(xdgConfig, "alisio") : join(home, ".config", "alisio"));
|
|
92
|
+
const stateHome = env[ENV_KEYS.stateHome]?.trim() ||
|
|
93
|
+
(xdgState ? join(xdgState, "alisio") : join(home, ".local", "state", "alisio"));
|
|
94
|
+
const database = overrides.database?.trim() ||
|
|
95
|
+
env[ENV_KEYS.db]?.trim() ||
|
|
96
|
+
join(stateHome, "telemetry", "telemetry.sqlite");
|
|
97
|
+
const configFile = overrides.configFile?.trim() || join(configHome, "telemetry", "config.json");
|
|
98
|
+
return { configFile, database };
|
|
99
|
+
}
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
// Primitive parsers
|
|
102
|
+
// ---------------------------------------------------------------------------
|
|
103
|
+
function isPlainObject(value) {
|
|
104
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
105
|
+
}
|
|
106
|
+
const SECRET_KEY = /(?:secret|password|passwd|credential|authorization|bearer|cookie|private[_-]?key|api[_-]?key|apikey)/i;
|
|
107
|
+
const TOKEN_KEY = /(?:^|[_-])token(?:$|[_-])/i;
|
|
108
|
+
/** A key that must never appear in the persisted configuration. */
|
|
109
|
+
function isSecretKey(key) {
|
|
110
|
+
if (key === "tokenEnv")
|
|
111
|
+
return false;
|
|
112
|
+
return SECRET_KEY.test(key) || key.toLowerCase() === "token" || TOKEN_KEY.test(key);
|
|
113
|
+
}
|
|
114
|
+
/** Reject unknown and secret-shaped keys, with an actionable message. */
|
|
115
|
+
function assertKeys(object, allowed, where) {
|
|
116
|
+
for (const key of Object.keys(object)) {
|
|
117
|
+
if (allowed.includes(key))
|
|
118
|
+
continue;
|
|
119
|
+
if (isSecretKey(key))
|
|
120
|
+
fail(`${where}: field "${key}" is not allowed. Credentials are never persisted; set the token environment variable instead and reference its name with "otlp.tokenEnv".`);
|
|
121
|
+
fail(`${where}: unknown field "${key}" (allowed: ${allowed.join(", ")})`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
function readBoolean(value, where) {
|
|
125
|
+
if (typeof value === "boolean")
|
|
126
|
+
return value;
|
|
127
|
+
if (typeof value === "string") {
|
|
128
|
+
const normalized = value.trim().toLowerCase();
|
|
129
|
+
if (["1", "true", "yes", "on"].includes(normalized))
|
|
130
|
+
return true;
|
|
131
|
+
if (["0", "false", "no", "off"].includes(normalized))
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
fail(`${where}: expected a boolean, got ${JSON.stringify(value)}`);
|
|
135
|
+
}
|
|
136
|
+
function readInteger(value, where, min, max) {
|
|
137
|
+
const numeric = typeof value === "number"
|
|
138
|
+
? value
|
|
139
|
+
: typeof value === "string" && value.trim() !== ""
|
|
140
|
+
? Number(value)
|
|
141
|
+
: Number.NaN;
|
|
142
|
+
if (!Number.isInteger(numeric))
|
|
143
|
+
fail(`${where}: expected an integer, got ${JSON.stringify(value)}`);
|
|
144
|
+
if (numeric < min || numeric > max)
|
|
145
|
+
fail(`${where}: expected an integer between ${min} and ${max}, got ${numeric}`);
|
|
146
|
+
return numeric;
|
|
147
|
+
}
|
|
148
|
+
function readRatio(value, where) {
|
|
149
|
+
const numeric = typeof value === "number"
|
|
150
|
+
? value
|
|
151
|
+
: typeof value === "string" && value.trim() !== ""
|
|
152
|
+
? Number(value)
|
|
153
|
+
: Number.NaN;
|
|
154
|
+
if (!Number.isFinite(numeric) || numeric < 0 || numeric > 1)
|
|
155
|
+
fail(`${where}: expected a number between 0 and 1, got ${JSON.stringify(value)}`);
|
|
156
|
+
return numeric;
|
|
157
|
+
}
|
|
158
|
+
function readBoundedString(value, where, max, allowEmpty = false) {
|
|
159
|
+
if (typeof value !== "string")
|
|
160
|
+
fail(`${where}: expected a string, got ${JSON.stringify(value)}`);
|
|
161
|
+
const trimmed = value.trim();
|
|
162
|
+
if (!allowEmpty && trimmed === "")
|
|
163
|
+
fail(`${where}: must not be empty`);
|
|
164
|
+
if (trimmed.length > max)
|
|
165
|
+
fail(`${where}: must be at most ${max} characters`);
|
|
166
|
+
return trimmed;
|
|
167
|
+
}
|
|
168
|
+
/** Validate and normalize an OTLP base endpoint. Credentials in the URL are refused. */
|
|
169
|
+
export function parseEndpoint(value, where = "otlp.endpoint") {
|
|
170
|
+
let url;
|
|
171
|
+
try {
|
|
172
|
+
url = new URL(value.trim());
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
fail(`${where}: not a valid absolute URL`);
|
|
176
|
+
}
|
|
177
|
+
if (url.protocol !== "https:" && url.protocol !== "http:")
|
|
178
|
+
fail(`${where}: must use http or https`);
|
|
179
|
+
if (url.username !== "" || url.password !== "")
|
|
180
|
+
fail(`${where}: must not embed credentials`);
|
|
181
|
+
if (url.search !== "")
|
|
182
|
+
fail(`${where}: must not include a query string`);
|
|
183
|
+
if (url.hash !== "")
|
|
184
|
+
fail(`${where}: must not include a fragment`);
|
|
185
|
+
const base = `${url.origin}${url.pathname}`.replace(/\/+$/, "");
|
|
186
|
+
return base;
|
|
187
|
+
}
|
|
188
|
+
function readHeaders(value, where) {
|
|
189
|
+
if (!isPlainObject(value))
|
|
190
|
+
fail(`${where}: expected an object of string headers`);
|
|
191
|
+
const headers = {};
|
|
192
|
+
for (const [key, raw] of Object.entries(value)) {
|
|
193
|
+
if (isSecretKey(key))
|
|
194
|
+
fail(`${where}: header "${key}" looks like a credential and is refused. Only non-secret headers are persisted; authorization is injected at export time from the token environment variable.`);
|
|
195
|
+
if (typeof raw !== "string")
|
|
196
|
+
fail(`${where}.${key}: header value must be a string`);
|
|
197
|
+
if (raw.length > 1000)
|
|
198
|
+
fail(`${where}.${key}: header value is too long`);
|
|
199
|
+
headers[key] = raw;
|
|
200
|
+
}
|
|
201
|
+
return headers;
|
|
202
|
+
}
|
|
203
|
+
function readSignals(value, where) {
|
|
204
|
+
if (isPlainObject(value))
|
|
205
|
+
assertKeys(value, ["traces", "logs", "metrics"], where);
|
|
206
|
+
else
|
|
207
|
+
fail(`${where}: expected an object of booleans`);
|
|
208
|
+
const object = value;
|
|
209
|
+
return {
|
|
210
|
+
traces: object.traces === undefined ? true : readBoolean(object.traces, `${where}.traces`),
|
|
211
|
+
logs: object.logs === undefined ? true : readBoolean(object.logs, `${where}.logs`),
|
|
212
|
+
metrics: object.metrics === undefined ? false : readBoolean(object.metrics, `${where}.metrics`),
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
// ---------------------------------------------------------------------------
|
|
216
|
+
// Config-file validation and merging
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
/** Parse a raw config object into a partial config, rejecting unknown/secret keys. */
|
|
219
|
+
export function parsePartialConfig(raw, where = "config") {
|
|
220
|
+
if (!isPlainObject(raw))
|
|
221
|
+
fail(`${where}: expected a JSON object`);
|
|
222
|
+
assertKeys(raw, ["version", "retentionDays", "capture", "redaction", "otlp", "batch"], where);
|
|
223
|
+
const partial = {};
|
|
224
|
+
if (raw.version !== undefined && raw.version !== 1)
|
|
225
|
+
fail(`${where}.version: unsupported version ${JSON.stringify(raw.version)}`);
|
|
226
|
+
if (raw.retentionDays !== undefined)
|
|
227
|
+
partial.retentionDays = readInteger(raw.retentionDays, `${where}.retentionDays`, 1, 3650);
|
|
228
|
+
if (raw.capture !== undefined) {
|
|
229
|
+
if (!isPlainObject(raw.capture))
|
|
230
|
+
fail(`${where}.capture: expected an object`);
|
|
231
|
+
assertKeys(raw.capture, ["prompts", "completions", "toolArguments", "toolResults"], `${where}.capture`);
|
|
232
|
+
const capture = {};
|
|
233
|
+
for (const key of ["prompts", "completions", "toolArguments", "toolResults"]) {
|
|
234
|
+
if (raw.capture[key] !== undefined)
|
|
235
|
+
capture[key] = readBoolean(raw.capture[key], `${where}.capture.${key}`);
|
|
236
|
+
}
|
|
237
|
+
partial.capture = capture;
|
|
238
|
+
}
|
|
239
|
+
if (raw.redaction !== undefined) {
|
|
240
|
+
if (!isPlainObject(raw.redaction))
|
|
241
|
+
fail(`${where}.redaction: expected an object`);
|
|
242
|
+
assertKeys(raw.redaction, ["mode"], `${where}.redaction`);
|
|
243
|
+
if (raw.redaction.mode !== undefined) {
|
|
244
|
+
const mode = raw.redaction.mode;
|
|
245
|
+
if (mode !== "strict" && mode !== "standard")
|
|
246
|
+
fail(`${where}.redaction.mode: expected "strict" or "standard", got ${JSON.stringify(mode)}`);
|
|
247
|
+
partial.redaction = { mode };
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (raw.otlp !== undefined) {
|
|
251
|
+
if (!isPlainObject(raw.otlp))
|
|
252
|
+
fail(`${where}.otlp: expected an object`);
|
|
253
|
+
assertKeys(raw.otlp, [
|
|
254
|
+
"enabled",
|
|
255
|
+
"endpoint",
|
|
256
|
+
"headers",
|
|
257
|
+
"tokenEnv",
|
|
258
|
+
"instanceId",
|
|
259
|
+
"serviceName",
|
|
260
|
+
"environment",
|
|
261
|
+
"samplingRatio",
|
|
262
|
+
"signals",
|
|
263
|
+
"gzip",
|
|
264
|
+
"maxAttempts",
|
|
265
|
+
"timeoutMs",
|
|
266
|
+
"batchSize",
|
|
267
|
+
], `${where}.otlp`);
|
|
268
|
+
const otlp = {};
|
|
269
|
+
if (raw.otlp.enabled !== undefined)
|
|
270
|
+
otlp.enabled = readBoolean(raw.otlp.enabled, `${where}.otlp.enabled`);
|
|
271
|
+
if (raw.otlp.endpoint !== undefined) {
|
|
272
|
+
if (raw.otlp.endpoint === null || raw.otlp.endpoint === "")
|
|
273
|
+
otlp.endpoint = null;
|
|
274
|
+
else
|
|
275
|
+
otlp.endpoint = parseEndpoint(readBoundedString(raw.otlp.endpoint, `${where}.otlp.endpoint`, 2048), `${where}.otlp.endpoint`);
|
|
276
|
+
}
|
|
277
|
+
if (raw.otlp.headers !== undefined)
|
|
278
|
+
otlp.headers = readHeaders(raw.otlp.headers, `${where}.otlp.headers`);
|
|
279
|
+
if (raw.otlp.tokenEnv !== undefined) {
|
|
280
|
+
const name = readBoundedString(raw.otlp.tokenEnv, `${where}.otlp.tokenEnv`, 200);
|
|
281
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(name))
|
|
282
|
+
fail(`${where}.otlp.tokenEnv: must be a valid environment variable name`);
|
|
283
|
+
otlp.tokenEnv = name;
|
|
284
|
+
}
|
|
285
|
+
if (raw.otlp.instanceId !== undefined)
|
|
286
|
+
otlp.instanceId = readBoundedString(raw.otlp.instanceId, `${where}.otlp.instanceId`, 200, true);
|
|
287
|
+
if (raw.otlp.serviceName !== undefined)
|
|
288
|
+
otlp.serviceName = readBoundedString(raw.otlp.serviceName, `${where}.otlp.serviceName`, 200);
|
|
289
|
+
if (raw.otlp.environment !== undefined)
|
|
290
|
+
otlp.environment = readBoundedString(raw.otlp.environment, `${where}.otlp.environment`, 100, true);
|
|
291
|
+
if (raw.otlp.samplingRatio !== undefined)
|
|
292
|
+
otlp.samplingRatio = readRatio(raw.otlp.samplingRatio, `${where}.otlp.samplingRatio`);
|
|
293
|
+
if (raw.otlp.signals !== undefined)
|
|
294
|
+
otlp.signals = readSignals(raw.otlp.signals, `${where}.otlp.signals`);
|
|
295
|
+
if (raw.otlp.gzip !== undefined)
|
|
296
|
+
otlp.gzip = readBoolean(raw.otlp.gzip, `${where}.otlp.gzip`);
|
|
297
|
+
if (raw.otlp.maxAttempts !== undefined)
|
|
298
|
+
otlp.maxAttempts = readInteger(raw.otlp.maxAttempts, `${where}.otlp.maxAttempts`, 1, 10);
|
|
299
|
+
if (raw.otlp.timeoutMs !== undefined)
|
|
300
|
+
otlp.timeoutMs = readInteger(raw.otlp.timeoutMs, `${where}.otlp.timeoutMs`, 1_000, 120_000);
|
|
301
|
+
if (raw.otlp.batchSize !== undefined)
|
|
302
|
+
otlp.batchSize = readInteger(raw.otlp.batchSize, `${where}.otlp.batchSize`, 1, 10_000);
|
|
303
|
+
partial.otlp = otlp;
|
|
304
|
+
}
|
|
305
|
+
if (raw.batch !== undefined) {
|
|
306
|
+
if (!isPlainObject(raw.batch))
|
|
307
|
+
fail(`${where}.batch: expected an object`);
|
|
308
|
+
assertKeys(raw.batch, ["batchSize", "flushIntervalMs", "maxQueue"], `${where}.batch`);
|
|
309
|
+
const batch = {};
|
|
310
|
+
if (raw.batch.batchSize !== undefined)
|
|
311
|
+
batch.batchSize = readInteger(raw.batch.batchSize, `${where}.batch.batchSize`, 1, 10_000);
|
|
312
|
+
if (raw.batch.flushIntervalMs !== undefined)
|
|
313
|
+
batch.flushIntervalMs = readInteger(raw.batch.flushIntervalMs, `${where}.batch.flushIntervalMs`, 250, 600_000);
|
|
314
|
+
if (raw.batch.maxQueue !== undefined)
|
|
315
|
+
batch.maxQueue = readInteger(raw.batch.maxQueue, `${where}.batch.maxQueue`, 100, 1_000_000);
|
|
316
|
+
partial.batch = batch;
|
|
317
|
+
}
|
|
318
|
+
return partial;
|
|
319
|
+
}
|
|
320
|
+
function merge(base, patch) {
|
|
321
|
+
return {
|
|
322
|
+
version: 1,
|
|
323
|
+
retentionDays: patch.retentionDays ?? base.retentionDays,
|
|
324
|
+
capture: { ...base.capture, ...(patch.capture ?? {}) },
|
|
325
|
+
redaction: { ...base.redaction, ...(patch.redaction ?? {}) },
|
|
326
|
+
otlp: {
|
|
327
|
+
...base.otlp,
|
|
328
|
+
...(patch.otlp ?? {}),
|
|
329
|
+
signals: { ...base.otlp.signals, ...(patch.otlp?.signals ?? {}) },
|
|
330
|
+
},
|
|
331
|
+
batch: { ...base.batch, ...(patch.batch ?? {}) },
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
/** Environment overrides parsed into the same partial shape as the config file. */
|
|
335
|
+
export function environmentOverrides(env) {
|
|
336
|
+
const raw = {};
|
|
337
|
+
const put = (key, value) => {
|
|
338
|
+
const [head, tail] = key.split(".");
|
|
339
|
+
if (tail === undefined) {
|
|
340
|
+
raw[head] = value;
|
|
341
|
+
return;
|
|
342
|
+
}
|
|
343
|
+
const nested = raw[head] ?? {};
|
|
344
|
+
nested[tail] = value;
|
|
345
|
+
raw[head] = nested;
|
|
346
|
+
};
|
|
347
|
+
if (env[ENV_KEYS.retentionDays]?.trim())
|
|
348
|
+
put("retentionDays", env[ENV_KEYS.retentionDays]);
|
|
349
|
+
if (env[ENV_KEYS.otlpEnabled] !== undefined)
|
|
350
|
+
put("otlp.enabled", env[ENV_KEYS.otlpEnabled]);
|
|
351
|
+
const endpointEnv = env[ENV_KEYS.otlpEndpoint];
|
|
352
|
+
if (endpointEnv !== undefined && endpointEnv.trim() !== "")
|
|
353
|
+
put("otlp.endpoint", endpointEnv);
|
|
354
|
+
if (env[ENV_KEYS.otlpTokenEnv]?.trim())
|
|
355
|
+
put("otlp.tokenEnv", env[ENV_KEYS.otlpTokenEnv]);
|
|
356
|
+
if (env[ENV_KEYS.otlpInstanceId] !== undefined)
|
|
357
|
+
put("otlp.instanceId", env[ENV_KEYS.otlpInstanceId]);
|
|
358
|
+
if (env[ENV_KEYS.serviceName]?.trim())
|
|
359
|
+
put("otlp.serviceName", env[ENV_KEYS.serviceName]);
|
|
360
|
+
if (env[ENV_KEYS.environment] !== undefined)
|
|
361
|
+
put("otlp.environment", env[ENV_KEYS.environment]);
|
|
362
|
+
if (env[ENV_KEYS.samplingRatio]?.trim())
|
|
363
|
+
put("otlp.samplingRatio", env[ENV_KEYS.samplingRatio]);
|
|
364
|
+
if (env[ENV_KEYS.otlpGzip] !== undefined)
|
|
365
|
+
put("otlp.gzip", env[ENV_KEYS.otlpGzip]);
|
|
366
|
+
if (env[ENV_KEYS.batchSize]?.trim())
|
|
367
|
+
put("otlp.batchSize", env[ENV_KEYS.batchSize]);
|
|
368
|
+
if (env[ENV_KEYS.capturePrompts] !== undefined)
|
|
369
|
+
put("capture.prompts", env[ENV_KEYS.capturePrompts]);
|
|
370
|
+
if (env[ENV_KEYS.captureCompletions] !== undefined)
|
|
371
|
+
put("capture.completions", env[ENV_KEYS.captureCompletions]);
|
|
372
|
+
if (env[ENV_KEYS.captureToolArguments] !== undefined)
|
|
373
|
+
put("capture.toolArguments", env[ENV_KEYS.captureToolArguments]);
|
|
374
|
+
if (env[ENV_KEYS.captureToolResults] !== undefined)
|
|
375
|
+
put("capture.toolResults", env[ENV_KEYS.captureToolResults]);
|
|
376
|
+
if (env[ENV_KEYS.redactionMode]?.trim())
|
|
377
|
+
put("redaction.mode", env[ENV_KEYS.redactionMode]);
|
|
378
|
+
const signalsEnv = env[ENV_KEYS.otlpSignals];
|
|
379
|
+
if (signalsEnv?.trim()) {
|
|
380
|
+
const enabled = new Set(signalsEnv.split(",").map((signal) => signal.trim().toLowerCase()));
|
|
381
|
+
put("otlp.signals", {
|
|
382
|
+
traces: enabled.has("traces"),
|
|
383
|
+
logs: enabled.has("logs"),
|
|
384
|
+
metrics: enabled.has("metrics"),
|
|
385
|
+
});
|
|
386
|
+
}
|
|
387
|
+
if (env[ENV_KEYS.flushIntervalMs]?.trim())
|
|
388
|
+
put("batch.flushIntervalMs", env[ENV_KEYS.flushIntervalMs]);
|
|
389
|
+
if (env[ENV_KEYS.maxQueue]?.trim())
|
|
390
|
+
put("batch.maxQueue", env[ENV_KEYS.maxQueue]);
|
|
391
|
+
return parsePartialConfig(raw, "environment");
|
|
392
|
+
}
|
|
393
|
+
function readConfigFile(path) {
|
|
394
|
+
try {
|
|
395
|
+
const text = readFileSync(path, "utf8");
|
|
396
|
+
return { raw: JSON.parse(text), missing: false };
|
|
397
|
+
}
|
|
398
|
+
catch (error) {
|
|
399
|
+
const code = error.code;
|
|
400
|
+
if (code === "ENOENT")
|
|
401
|
+
return { raw: null, missing: true };
|
|
402
|
+
if (error instanceof SyntaxError)
|
|
403
|
+
fail(`config file ${path} is not valid JSON: ${error.message}`);
|
|
404
|
+
fail(`cannot read config file ${path}`);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Load, merge and validate the effective configuration. Returns a structured
|
|
409
|
+
* result; it never throws, so an invalid configuration can never crash a host.
|
|
410
|
+
*/
|
|
411
|
+
export function loadTelemetryConfig(options = {}) {
|
|
412
|
+
const env = options.env ?? process.env;
|
|
413
|
+
const paths = options.paths ?? resolveTelemetryPaths(env);
|
|
414
|
+
try {
|
|
415
|
+
let fromFile = {};
|
|
416
|
+
if (!options.skipFile) {
|
|
417
|
+
const { raw, missing } = options.file !== undefined
|
|
418
|
+
? { raw: options.file, missing: false }
|
|
419
|
+
: readConfigFile(paths.configFile);
|
|
420
|
+
if (!missing && raw !== null && raw !== undefined)
|
|
421
|
+
fromFile = parsePartialConfig(raw, "config file");
|
|
422
|
+
}
|
|
423
|
+
const fromEnv = environmentOverrides(env);
|
|
424
|
+
const merged = merge(merge(merge(createDefaultConfig(), fromFile), fromEnv), options.overrides ?? {});
|
|
425
|
+
// Re-parse the fully merged object so every value is re-validated in
|
|
426
|
+
// context (a partial that was valid alone must stay valid combined).
|
|
427
|
+
const validated = parsePartialConfig(merged, "configuration");
|
|
428
|
+
const config = merge(createDefaultConfig(), validated);
|
|
429
|
+
return { ok: true, config, paths };
|
|
430
|
+
}
|
|
431
|
+
catch (error) {
|
|
432
|
+
const message = error instanceof ConfigError
|
|
433
|
+
? error.message
|
|
434
|
+
: error instanceof Error
|
|
435
|
+
? error.message
|
|
436
|
+
: String(error);
|
|
437
|
+
return { ok: false, error: message, paths };
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
// ---------------------------------------------------------------------------
|
|
441
|
+
// Persistence
|
|
442
|
+
// ---------------------------------------------------------------------------
|
|
443
|
+
/** Serialize a config for storage. The shape contains no credential by construction. */
|
|
444
|
+
export function serializeConfig(config) {
|
|
445
|
+
return `${JSON.stringify(config, null, 2)}\n`;
|
|
446
|
+
}
|
|
447
|
+
/**
|
|
448
|
+
* Apply validated overrides on top of a base configuration and return the
|
|
449
|
+
* effective result. Used by the setup command; throws `ConfigError` on invalid
|
|
450
|
+
* input so the caller can report an actionable message.
|
|
451
|
+
*/
|
|
452
|
+
export function updateConfig(base, overrides) {
|
|
453
|
+
const merged = merge(base, overrides);
|
|
454
|
+
const validated = parsePartialConfig(merged, "configuration");
|
|
455
|
+
return merge(createDefaultConfig(), validated);
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Atomically write the config file with 0600 file mode and 0700 parent. Write
|
|
459
|
+
* to a sibling temp file, fsync, rename over the target.
|
|
460
|
+
*/
|
|
461
|
+
export function writeConfigFile(path, config) {
|
|
462
|
+
const directory = dirname(path);
|
|
463
|
+
mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
464
|
+
try {
|
|
465
|
+
chmodSync(directory, 0o700);
|
|
466
|
+
}
|
|
467
|
+
catch {
|
|
468
|
+
// Hardening only.
|
|
469
|
+
}
|
|
470
|
+
const temp = `${path}.${process.pid}.tmp`;
|
|
471
|
+
const text = serializeConfig(config);
|
|
472
|
+
const descriptor = openSync(temp, "w", 0o600);
|
|
473
|
+
try {
|
|
474
|
+
writeSync(descriptor, text, 0, "utf8");
|
|
475
|
+
fsyncSync(descriptor);
|
|
476
|
+
}
|
|
477
|
+
finally {
|
|
478
|
+
closeSync(descriptor);
|
|
479
|
+
}
|
|
480
|
+
try {
|
|
481
|
+
chmodSync(temp, 0o600);
|
|
482
|
+
}
|
|
483
|
+
catch {
|
|
484
|
+
// Hardening only.
|
|
485
|
+
}
|
|
486
|
+
renameSync(temp, path);
|
|
487
|
+
try {
|
|
488
|
+
chmodSync(path, 0o600);
|
|
489
|
+
}
|
|
490
|
+
catch {
|
|
491
|
+
// Hardening only.
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* A display projection of the configuration. It contains no credential (there
|
|
496
|
+
* is none) and reports whether the token environment variable is present
|
|
497
|
+
* without ever reading or echoing its value.
|
|
498
|
+
*/
|
|
499
|
+
export function describeConfig(config, env = process.env) {
|
|
500
|
+
return {
|
|
501
|
+
version: config.version,
|
|
502
|
+
retentionDays: config.retentionDays,
|
|
503
|
+
capture: { ...config.capture },
|
|
504
|
+
redaction: { ...config.redaction },
|
|
505
|
+
otlp: {
|
|
506
|
+
enabled: config.otlp.enabled,
|
|
507
|
+
endpoint: config.otlp.endpoint,
|
|
508
|
+
headers: Object.keys(config.otlp.headers),
|
|
509
|
+
tokenEnv: config.otlp.tokenEnv,
|
|
510
|
+
tokenConfigured: Boolean(env[config.otlp.tokenEnv]?.trim()),
|
|
511
|
+
instanceId: config.otlp.instanceId,
|
|
512
|
+
serviceName: config.otlp.serviceName,
|
|
513
|
+
environment: config.otlp.environment,
|
|
514
|
+
samplingRatio: config.otlp.samplingRatio,
|
|
515
|
+
signals: { ...config.otlp.signals },
|
|
516
|
+
gzip: config.otlp.gzip,
|
|
517
|
+
maxAttempts: config.otlp.maxAttempts,
|
|
518
|
+
timeoutMs: config.otlp.timeoutMs,
|
|
519
|
+
batchSize: config.otlp.batchSize,
|
|
520
|
+
},
|
|
521
|
+
batch: { ...config.batch },
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { SqlDatabase } from "@alisio/sdk";
|
|
2
|
+
export type { SqlDatabase, SqlRow, SqlStatement, SqlValue } from "@alisio/sdk";
|
|
3
|
+
export interface ConfigureDatabaseOptions {
|
|
4
|
+
/** Milliseconds a locked database waits before failing. Defaults to 5000. */
|
|
5
|
+
busyTimeoutMs?: number;
|
|
6
|
+
/** Skip the WAL/busy_timeout/foreign_keys pragmas (used only by focused tests). */
|
|
7
|
+
minimal?: boolean;
|
|
8
|
+
}
|
|
9
|
+
/** File mode bits of `path`, or null when it does not exist. Exposed for tests. */
|
|
10
|
+
export declare function fileMode(path: string): number | null;
|
|
11
|
+
/**
|
|
12
|
+
* Harden an already-open host port with the telemetry pragmas and file
|
|
13
|
+
* permissions. The port is returned unchanged so callers can chain it.
|
|
14
|
+
*/
|
|
15
|
+
export declare function configureDatabase(database: SqlDatabase, path: string, options?: ConfigureDatabaseOptions): SqlDatabase;
|
|
16
|
+
//# sourceMappingURL=database.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.d.ts","sourceRoot":"","sources":["../src/database.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAE/C,YAAY,EAAE,WAAW,EAAE,MAAM,EAAE,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE/E,MAAM,WAAW,wBAAwB;IACvC,6EAA6E;IAC7E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,mFAAmF;IACnF,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAID,mFAAmF;AACnF,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAMpD;AAuBD;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,WAAW,EACrB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,wBAA6B,GACrC,WAAW,CAiBb"}
|
package/dist/database.js
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The host-provided SQLite storage port.
|
|
3
|
+
*
|
|
4
|
+
* The database is opened by the host through `api.storage.sqlite(path)`, so the
|
|
5
|
+
* plugin never depends on a runtime SQLite driver. The host port applies its
|
|
6
|
+
* file permissions only when it creates the file and sets no pragmas, so this
|
|
7
|
+
* module hardens the port immediately after it is opened: it enables WAL, a
|
|
8
|
+
* `busy_timeout`, foreign keys and NORMAL synchronous mode, and defensively
|
|
9
|
+
* tightens the database file to 0600 and its parent directory to 0700.
|
|
10
|
+
* `PRAGMA optimize` runs periodically from the store.
|
|
11
|
+
*
|
|
12
|
+
* The types are re-exported from `@alisio/sdk` so the rest of the plugin stays
|
|
13
|
+
* driver-agnostic. Tests must run without a host; they inject an equivalent
|
|
14
|
+
* `SqlDatabase` from `test/sqlite-driver.ts`, which is never shipped.
|
|
15
|
+
*/
|
|
16
|
+
import { chmodSync, mkdirSync, statSync } from "node:fs";
|
|
17
|
+
import { dirname } from "node:path";
|
|
18
|
+
const DEFAULT_BUSY_TIMEOUT_MS = 5_000;
|
|
19
|
+
/** File mode bits of `path`, or null when it does not exist. Exposed for tests. */
|
|
20
|
+
export function fileMode(path) {
|
|
21
|
+
try {
|
|
22
|
+
return statSync(path).mode & 0o777;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
/** Create (if needed) and tighten the parent directory to 0700. */
|
|
29
|
+
function ensureParent(path) {
|
|
30
|
+
const parent = dirname(path);
|
|
31
|
+
mkdirSync(parent, { recursive: true, mode: 0o700 });
|
|
32
|
+
// `mkdirSync`'s mode is masked by the process umask, so tighten explicitly.
|
|
33
|
+
try {
|
|
34
|
+
chmodSync(parent, 0o700);
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
// A read-only or foreign filesystem is not a reason to fail telemetry.
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/** Best-effort 0600 on the database file; permissions are hardening, not a gate. */
|
|
41
|
+
function tightenFile(path) {
|
|
42
|
+
try {
|
|
43
|
+
chmodSync(path, 0o600);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
// Ignore: some filesystems reject chmod.
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Harden an already-open host port with the telemetry pragmas and file
|
|
51
|
+
* permissions. The port is returned unchanged so callers can chain it.
|
|
52
|
+
*/
|
|
53
|
+
export function configureDatabase(database, path, options = {}) {
|
|
54
|
+
const inMemory = path === ":memory:" || path === "";
|
|
55
|
+
if (!inMemory) {
|
|
56
|
+
ensureParent(path);
|
|
57
|
+
tightenFile(path);
|
|
58
|
+
}
|
|
59
|
+
if (!options.minimal) {
|
|
60
|
+
database.exec("PRAGMA journal_mode = WAL");
|
|
61
|
+
database.exec(`PRAGMA busy_timeout = ${Math.max(0, options.busyTimeoutMs ?? DEFAULT_BUSY_TIMEOUT_MS)}`);
|
|
62
|
+
database.exec("PRAGMA foreign_keys = ON");
|
|
63
|
+
database.exec("PRAGMA synchronous = NORMAL");
|
|
64
|
+
}
|
|
65
|
+
return database;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=database.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"database.js","sourceRoot":"","sources":["../src/database.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACzD,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAYpC,MAAM,uBAAuB,GAAG,KAAK,CAAC;AAEtC,mFAAmF;AACnF,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,IAAI,CAAC;QACH,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,KAAK,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,mEAAmE;AACnE,SAAS,YAAY,CAAC,IAAY;IAChC,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACpD,4EAA4E;IAC5E,IAAI,CAAC;QACH,SAAS,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAC3B,CAAC;IAAC,MAAM,CAAC;QACP,uEAAuE;IACzE,CAAC;AACH,CAAC;AAED,oFAAoF;AACpF,SAAS,WAAW,CAAC,IAAY;IAC/B,IAAI,CAAC;QACH,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACzB,CAAC;IAAC,MAAM,CAAC;QACP,yCAAyC;IAC3C,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,QAAqB,EACrB,IAAY,EACZ,OAAO,GAA6B,EAAE;IAEtC,MAAM,QAAQ,GAAG,IAAI,KAAK,UAAU,IAAI,IAAI,KAAK,EAAE,CAAC;IACpD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,YAAY,CAAC,IAAI,CAAC,CAAC;QACnB,WAAW,CAAC,IAAI,CAAC,CAAC;IACpB,CAAC;IAED,IAAI,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;QACrB,QAAQ,CAAC,IAAI,CAAC,2BAA2B,CAAC,CAAC;QAC3C,QAAQ,CAAC,IAAI,CACX,yBAAyB,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,OAAO,CAAC,aAAa,IAAI,uBAAuB,CAAC,EAAE,CACzF,CAAC;QACF,QAAQ,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;QAC1C,QAAQ,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC;IAC/C,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|