@klarkxy/dsh-plugin-kit 0.6.1 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/docs/README.zh-CN.md +1 -1
- package/lib/auxiliary-model-client.d.ts +4 -1
- package/lib/auxiliary-model-client.js +72 -33
- package/lib/auxiliary-model-client.js.map +1 -1
- package/lib/auxiliary-model.js +26 -2
- package/lib/auxiliary-model.js.map +1 -1
- package/lib/cli-media.js +1 -1
- package/lib/cli-process.d.ts +2 -0
- package/lib/cli-process.js +36 -2
- package/lib/cli-process.js.map +1 -1
- package/lib/client-logger.d.ts +19 -0
- package/lib/client-logger.js +153 -0
- package/lib/client-logger.js.map +1 -0
- package/lib/client-utils.d.ts +2 -1
- package/lib/client-utils.js +9 -3
- package/lib/client-utils.js.map +1 -1
- package/lib/domain.d.ts +12 -4
- package/lib/domain.js +32 -6
- package/lib/domain.js.map +1 -1
- package/lib/host-rpc.d.ts +2 -0
- package/lib/host-rpc.js +71 -2
- package/lib/host-rpc.js.map +1 -1
- package/lib/index.d.ts +3 -1
- package/lib/index.js +2 -1
- package/lib/llm-call.d.ts +5 -1
- package/lib/llm-call.js +79 -46
- package/lib/llm-call.js.map +1 -1
- package/lib/log-shared.d.ts +2 -0
- package/lib/log-shared.js +169 -0
- package/lib/log-shared.js.map +1 -0
- package/lib/log-shared2.d.ts +72 -0
- package/lib/logger.d.ts +87 -0
- package/lib/logger.js +572 -0
- package/lib/logger.js.map +1 -0
- package/lib/model-picker.d.ts +34 -0
- package/lib/model-picker.js +96 -0
- package/lib/model-picker.js.map +1 -0
- package/lib/official-ui.d.ts +1 -1
- package/lib/official-ui.js +3 -1
- package/lib/official-ui.js.map +1 -1
- package/lib/zod.js +79 -79
- package/lib/zod.js.map +1 -1
- package/package.json +34 -14
package/lib/logger.js
ADDED
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
import { DEFAULT_PLUGIN_LOG_LEVEL, PLUGIN_LOG_LEVELS, PLUGIN_LOG_MAX_CONTEXT_CHARS, PLUGIN_LOG_MAX_MESSAGE_CHARS, buildPluginLogEntry, createChildPluginLogger, isPluginLogLevel, mergePluginLogContext, normalizePluginLogEvent, normalizePluginLogSource, parsePluginLogLevel, pluginLogLevelEnabled, sanitizePluginLogContext, sanitizePluginLogError, sanitizePluginLogText, truncateLogText } from "./log-shared.js";
|
|
2
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
3
|
+
import { appendFileSync, closeSync, existsSync, lstatSync, mkdirSync, openSync, readSync, readdirSync, realpathSync, renameSync, unlinkSync } from "node:fs";
|
|
4
|
+
import { homedir } from "node:os";
|
|
5
|
+
import { basename, dirname, join, resolve, sep } from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
//#region src/logger.ts
|
|
8
|
+
const PLUGIN_LOG_MAX_AGE_MS = 6048e5;
|
|
9
|
+
const PLUGIN_LOG_MAX_FILE_BYTES = 104857600;
|
|
10
|
+
const PLUGIN_LOG_MAX_ARCHIVE_BYTES = 104857600;
|
|
11
|
+
const PLUGIN_LOG_RECENT_LIMIT = 200;
|
|
12
|
+
const PROFILE_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,63}$/;
|
|
13
|
+
const ARCHIVE_RE = /^[a-z][a-z0-9-]{0,63}\.\d{4}-\d{2}-\d{2}T\d{2}-\d{2}-\d{2}\.\d{3}Z\.\d+\.\d+\.jsonl$/;
|
|
14
|
+
const correlationStorage = new AsyncLocalStorage();
|
|
15
|
+
const processFileLoggers = /* @__PURE__ */ new Map();
|
|
16
|
+
let operationSequence = 0;
|
|
17
|
+
function safeProfileName(profile) {
|
|
18
|
+
const trimmed = String(profile ?? "").trim();
|
|
19
|
+
if (PROFILE_RE.test(trimmed)) return trimmed.toLowerCase();
|
|
20
|
+
const cleaned = trimmed.toLowerCase().replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
|
|
21
|
+
return cleaned && PROFILE_RE.test(cleaned) ? cleaned : "default";
|
|
22
|
+
}
|
|
23
|
+
function pluginLogDirectory(home, profile = "default") {
|
|
24
|
+
return join(resolve(home), "logs", "plugins", safeProfileName(profile));
|
|
25
|
+
}
|
|
26
|
+
function resolveHostLogTarget(ctx, options = {}) {
|
|
27
|
+
const env = options.env ?? process.env;
|
|
28
|
+
if (options.directory?.trim()) {
|
|
29
|
+
const dir = resolve(options.directory.trim());
|
|
30
|
+
return {
|
|
31
|
+
directory: dir,
|
|
32
|
+
anchor: dir,
|
|
33
|
+
...options.profile ? { profile: safeProfileName(options.profile) } : {}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (options.home?.trim()) {
|
|
37
|
+
const home = resolve(options.home.trim());
|
|
38
|
+
const profile = safeProfileName(options.profile);
|
|
39
|
+
return {
|
|
40
|
+
directory: pluginLogDirectory(home, profile),
|
|
41
|
+
anchor: home,
|
|
42
|
+
profile
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
if (env.DSH_PLUGIN_LOG_DIR?.trim()) {
|
|
46
|
+
const dir = resolve(env.DSH_PLUGIN_LOG_DIR.trim());
|
|
47
|
+
return {
|
|
48
|
+
directory: dir,
|
|
49
|
+
anchor: dir,
|
|
50
|
+
...options.profile ? { profile: safeProfileName(options.profile) } : {}
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const host = ctx && typeof ctx === "object" ? ctx : void 0;
|
|
54
|
+
let profileCtx;
|
|
55
|
+
try {
|
|
56
|
+
profileCtx = host?.get?.("profileContext", false);
|
|
57
|
+
} catch {
|
|
58
|
+
try {
|
|
59
|
+
profileCtx = host?.get?.("profileContext");
|
|
60
|
+
} catch {
|
|
61
|
+
profileCtx = void 0;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (typeof profileCtx?.home === "string" && profileCtx.home.trim() && typeof profileCtx.dir === "string" && profileCtx.dir.trim()) {
|
|
65
|
+
const home = resolve(profileCtx.home.trim());
|
|
66
|
+
const dir = resolve(profileCtx.dir.trim());
|
|
67
|
+
if (isInside(home, dir)) {
|
|
68
|
+
const profile = safeProfileName(typeof profileCtx.name === "string" ? profileCtx.name : basename(dir));
|
|
69
|
+
return {
|
|
70
|
+
directory: pluginLogDirectory(home, profile),
|
|
71
|
+
anchor: home,
|
|
72
|
+
profile
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
if (!Boolean(env.VITEST || env.NODE_TEST_CONTEXT) && typeof host?.baseUrl === "string" && host.baseUrl.trim()) {
|
|
77
|
+
const basePath = pathFromBaseUrl(host.baseUrl);
|
|
78
|
+
const configuredHome = resolve((env.DSH_HOME?.trim() || join(homedir(), ".dsh")).trim());
|
|
79
|
+
const profilesRoot = join(configuredHome, "profiles");
|
|
80
|
+
if (basePath && isInside(profilesRoot, basePath) && !samePath(profilesRoot, basePath)) {
|
|
81
|
+
const firstSegment = basePath.slice(profilesRoot.length).replace(/^[/\\]+/, "").split(/[/\\]/)[0];
|
|
82
|
+
if (firstSegment) {
|
|
83
|
+
const profile = safeProfileName(firstSegment);
|
|
84
|
+
return {
|
|
85
|
+
directory: pluginLogDirectory(configuredHome, profile),
|
|
86
|
+
anchor: configuredHome,
|
|
87
|
+
profile
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return {
|
|
93
|
+
directory: null,
|
|
94
|
+
anchor: null,
|
|
95
|
+
...options.profile ? { profile: safeProfileName(options.profile) } : {}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Synchronous, append-only rotating JSONL logger modeled on DSH Spaces
|
|
100
|
+
* diagnostics: 7-day or 100 MiB active rotation, 7-day / 100 MiB archive
|
|
101
|
+
* retention, secret and path redaction, and non-throwing write resilience.
|
|
102
|
+
*/
|
|
103
|
+
var RotatingPluginLogger = class {
|
|
104
|
+
source;
|
|
105
|
+
file;
|
|
106
|
+
lastError = null;
|
|
107
|
+
size = 0;
|
|
108
|
+
openedAt = 0;
|
|
109
|
+
initialized = false;
|
|
110
|
+
writing = false;
|
|
111
|
+
sequence = 0;
|
|
112
|
+
loggedWriteFailure = false;
|
|
113
|
+
recent = [];
|
|
114
|
+
directory;
|
|
115
|
+
anchor;
|
|
116
|
+
profile;
|
|
117
|
+
minimum;
|
|
118
|
+
now;
|
|
119
|
+
pid;
|
|
120
|
+
maxFileBytes;
|
|
121
|
+
maxArchiveBytes;
|
|
122
|
+
maxAgeMs;
|
|
123
|
+
recentLimit;
|
|
124
|
+
constructor(source, options = {}) {
|
|
125
|
+
this.source = normalizePluginLogSource(source);
|
|
126
|
+
const env = options.env ?? process.env;
|
|
127
|
+
const resolved = options.directory !== void 0 || options.home !== void 0 ? resolveHostLogTarget(void 0, options) : {
|
|
128
|
+
directory: null,
|
|
129
|
+
anchor: options.anchor ?? null,
|
|
130
|
+
profile: options.profile
|
|
131
|
+
};
|
|
132
|
+
this.directory = resolved.directory;
|
|
133
|
+
this.anchor = options.anchor ?? resolved.anchor;
|
|
134
|
+
this.profile = options.profile ?? resolved.profile;
|
|
135
|
+
this.file = this.directory ? join(this.directory, `${this.source}.jsonl`) : null;
|
|
136
|
+
this.minimum = options.minimumLevel ?? parsePluginLogLevel(env.DSH_PLUGIN_LOG_LEVEL, this.source, "info");
|
|
137
|
+
this.now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
138
|
+
this.pid = options.pid ?? process.pid;
|
|
139
|
+
this.maxFileBytes = options.maxFileBytes ?? 104857600;
|
|
140
|
+
this.maxArchiveBytes = options.maxArchiveBytes ?? 104857600;
|
|
141
|
+
this.maxAgeMs = options.maxAgeMs ?? 6048e5;
|
|
142
|
+
this.recentLimit = Math.max(1, options.recentLimit ?? 200);
|
|
143
|
+
}
|
|
144
|
+
get lastWriteError() {
|
|
145
|
+
return this.lastError;
|
|
146
|
+
}
|
|
147
|
+
recentEntries(limit = this.recentLimit) {
|
|
148
|
+
return this.recent.slice(-Math.max(0, limit));
|
|
149
|
+
}
|
|
150
|
+
debug(event, message, context) {
|
|
151
|
+
return this.write({
|
|
152
|
+
level: "debug",
|
|
153
|
+
event,
|
|
154
|
+
message,
|
|
155
|
+
...context ? { context } : {}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
info(event, message, context) {
|
|
159
|
+
return this.write({
|
|
160
|
+
level: "info",
|
|
161
|
+
event,
|
|
162
|
+
message,
|
|
163
|
+
...context ? { context } : {}
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
warn(event, message, error, context) {
|
|
167
|
+
return this.write({
|
|
168
|
+
level: "warn",
|
|
169
|
+
event,
|
|
170
|
+
message,
|
|
171
|
+
...error !== void 0 ? { error } : {},
|
|
172
|
+
...context ? { context } : {}
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
error(event, message, error, context) {
|
|
176
|
+
return this.write({
|
|
177
|
+
level: "error",
|
|
178
|
+
event,
|
|
179
|
+
message,
|
|
180
|
+
...error !== void 0 ? { error } : {},
|
|
181
|
+
...context ? { context } : {}
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
fault(event, message, error, context) {
|
|
185
|
+
return this.write({
|
|
186
|
+
level: "fault",
|
|
187
|
+
event,
|
|
188
|
+
message,
|
|
189
|
+
...error !== void 0 ? { error } : {},
|
|
190
|
+
...context ? { context } : {}
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
child(context) {
|
|
194
|
+
return createChildPluginLogger(this, context);
|
|
195
|
+
}
|
|
196
|
+
write(input) {
|
|
197
|
+
if (!pluginLogLevelEnabled(input.level, this.minimum) || this.writing) return false;
|
|
198
|
+
const at = this.now();
|
|
199
|
+
let entry;
|
|
200
|
+
let line;
|
|
201
|
+
try {
|
|
202
|
+
entry = buildPluginLogEntry(input, {
|
|
203
|
+
at,
|
|
204
|
+
source: this.source,
|
|
205
|
+
surface: "host",
|
|
206
|
+
profile: this.profile,
|
|
207
|
+
pid: this.pid,
|
|
208
|
+
correlation: correlationStorage.getStore()
|
|
209
|
+
});
|
|
210
|
+
line = `${JSON.stringify(entry)}\n`;
|
|
211
|
+
} catch {
|
|
212
|
+
return false;
|
|
213
|
+
}
|
|
214
|
+
this.pushRecent(entry);
|
|
215
|
+
if (!this.directory || !this.file) return true;
|
|
216
|
+
this.writing = true;
|
|
217
|
+
try {
|
|
218
|
+
this.openManagedDirectory();
|
|
219
|
+
this.prepare(at);
|
|
220
|
+
if (this.size > 0 && (this.size + Buffer.byteLength(line) > this.maxFileBytes || at.getTime() - this.openedAt >= this.maxAgeMs)) this.rotate(at);
|
|
221
|
+
appendFileSync(this.file, line, {
|
|
222
|
+
encoding: "utf8",
|
|
223
|
+
mode: 384
|
|
224
|
+
});
|
|
225
|
+
this.size += Buffer.byteLength(line);
|
|
226
|
+
this.prune(at);
|
|
227
|
+
this.lastError = null;
|
|
228
|
+
this.loggedWriteFailure = false;
|
|
229
|
+
return true;
|
|
230
|
+
} catch (error) {
|
|
231
|
+
const detail = sanitizePluginLogText(error instanceof Error ? error.message : String(error), 500);
|
|
232
|
+
this.lastError = `The plugin diagnostic log could not be written: ${detail}`;
|
|
233
|
+
this.initialized = false;
|
|
234
|
+
if (!this.loggedWriteFailure) {
|
|
235
|
+
this.loggedWriteFailure = true;
|
|
236
|
+
try {
|
|
237
|
+
process.stderr.write(`DSH Plugin (${this.source}): ${this.lastError}. The original operation continues unchanged.\n`);
|
|
238
|
+
} catch {}
|
|
239
|
+
}
|
|
240
|
+
return false;
|
|
241
|
+
} finally {
|
|
242
|
+
this.writing = false;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
pushRecent(entry) {
|
|
246
|
+
this.recent.push(entry);
|
|
247
|
+
const overflow = this.recent.length - this.recentLimit;
|
|
248
|
+
if (overflow > 0) this.recent.splice(0, overflow);
|
|
249
|
+
}
|
|
250
|
+
prepare(at) {
|
|
251
|
+
if (this.initialized || !this.file) return;
|
|
252
|
+
this.openManagedDirectory();
|
|
253
|
+
const stat = this.regularFile(this.file);
|
|
254
|
+
this.size = stat?.size ?? 0;
|
|
255
|
+
const stamps = stat ? [stat.birthtimeMs, stat.mtimeMs].filter((value) => value > 0) : [];
|
|
256
|
+
this.openedAt = stamps.length ? Math.min(...stamps) : at.getTime();
|
|
257
|
+
this.initialized = true;
|
|
258
|
+
}
|
|
259
|
+
rotate(at) {
|
|
260
|
+
if (!this.directory || !this.file) return;
|
|
261
|
+
const stamp = at.toISOString().replace(/:/g, "-");
|
|
262
|
+
const name = `${this.source}.${stamp}.${this.pid}.${this.sequence++}.jsonl`;
|
|
263
|
+
if (!ARCHIVE_RE.test(name)) throw new Error("Diagnostic log archive name is invalid.");
|
|
264
|
+
const archive = join(this.directory, name);
|
|
265
|
+
if (lexists(archive)) throw new Error("Diagnostic log archive already exists.");
|
|
266
|
+
try {
|
|
267
|
+
renameSync(this.file, archive);
|
|
268
|
+
} catch (error) {
|
|
269
|
+
if (error.code === "ENOENT") {
|
|
270
|
+
this.size = 0;
|
|
271
|
+
this.openedAt = at.getTime();
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
throw error;
|
|
275
|
+
}
|
|
276
|
+
this.size = 0;
|
|
277
|
+
this.openedAt = at.getTime();
|
|
278
|
+
}
|
|
279
|
+
rotateStaleActive(at) {
|
|
280
|
+
if (!this.file) return;
|
|
281
|
+
const stat = this.regularFile(this.file);
|
|
282
|
+
if (!stat || stat.size === 0) return;
|
|
283
|
+
if (at.getTime() - stat.mtimeMs < this.maxAgeMs) return;
|
|
284
|
+
this.rotate(at);
|
|
285
|
+
}
|
|
286
|
+
prune(at) {
|
|
287
|
+
if (!this.directory) return;
|
|
288
|
+
this.rotateStaleActive(at);
|
|
289
|
+
const archives = [];
|
|
290
|
+
let total = 0;
|
|
291
|
+
for (const name of readdirSync(this.directory)) {
|
|
292
|
+
if (!ARCHIVE_RE.test(name) || !name.startsWith(`${this.source}.`)) continue;
|
|
293
|
+
const file = join(this.directory, name);
|
|
294
|
+
const stat = lstatSync(file);
|
|
295
|
+
if (stat.isSymbolicLink() || !stat.isFile()) throw new Error("Diagnostic log archive is not a regular file.");
|
|
296
|
+
if (at.getTime() - stat.mtimeMs >= this.maxAgeMs) {
|
|
297
|
+
try {
|
|
298
|
+
unlinkSync(file);
|
|
299
|
+
} catch {}
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
archives.push({
|
|
303
|
+
name,
|
|
304
|
+
size: stat.size,
|
|
305
|
+
mtime: stat.mtimeMs
|
|
306
|
+
});
|
|
307
|
+
total += stat.size;
|
|
308
|
+
}
|
|
309
|
+
archives.sort((left, right) => left.mtime - right.mtime || left.name.localeCompare(right.name));
|
|
310
|
+
while (total > this.maxArchiveBytes && archives.length) {
|
|
311
|
+
const oldest = archives.shift();
|
|
312
|
+
try {
|
|
313
|
+
unlinkSync(join(this.directory, oldest.name));
|
|
314
|
+
} catch {}
|
|
315
|
+
total -= oldest.size;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
openManagedDirectory() {
|
|
319
|
+
if (!this.directory) return;
|
|
320
|
+
if (this.anchor) {
|
|
321
|
+
if ((existsSync(this.anchor) ? lstatSync(this.anchor) : void 0)?.isSymbolicLink()) throw new Error("Diagnostic log anchor cannot be a symbolic link.");
|
|
322
|
+
}
|
|
323
|
+
mkdirSync(this.directory, {
|
|
324
|
+
recursive: true,
|
|
325
|
+
mode: 448
|
|
326
|
+
});
|
|
327
|
+
const stat = lstatSync(this.directory);
|
|
328
|
+
if (stat.isSymbolicLink() || !stat.isDirectory()) throw new Error("Diagnostic log directory is not a real directory.");
|
|
329
|
+
const realDir = realpathSync(this.directory);
|
|
330
|
+
if (this.anchor) {
|
|
331
|
+
if (!isInside(realpathSync(this.anchor), realDir)) throw new Error("Diagnostic log directory escaped its managed root.");
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
regularFile(file) {
|
|
335
|
+
try {
|
|
336
|
+
const stat = lstatSync(file);
|
|
337
|
+
if (stat.isSymbolicLink() || !stat.isFile()) throw new Error("Diagnostic log is not a regular file.");
|
|
338
|
+
const expected = join(realpathSync(dirname(file)), basename(file));
|
|
339
|
+
if (!samePath(realpathSync(file), expected)) throw new Error("Diagnostic log path could not be verified.");
|
|
340
|
+
return stat;
|
|
341
|
+
} catch (error) {
|
|
342
|
+
if (error.code === "ENOENT") return void 0;
|
|
343
|
+
throw error;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
};
|
|
347
|
+
function createPluginLogger(ctxOrSource, maybeSource, maybeOptions) {
|
|
348
|
+
const hasCtx = typeof ctxOrSource !== "string";
|
|
349
|
+
const ctx = hasCtx ? ctxOrSource : void 0;
|
|
350
|
+
const source = hasCtx ? String(maybeSource ?? "dsh-plugin") : ctxOrSource;
|
|
351
|
+
const options = (hasCtx ? maybeOptions : maybeSource) ?? {};
|
|
352
|
+
const normalized = normalizePluginLogSource(source);
|
|
353
|
+
const target = resolveHostLogTarget(ctx, options);
|
|
354
|
+
let backing;
|
|
355
|
+
if (Boolean(target.directory && options.minimumLevel === void 0 && options.now === void 0 && options.pid === void 0 && options.maxFileBytes === void 0 && options.maxArchiveBytes === void 0 && options.maxAgeMs === void 0) && target.directory) {
|
|
356
|
+
const key = resolve(target.directory, `${normalized}.jsonl`);
|
|
357
|
+
let shared = processFileLoggers.get(key);
|
|
358
|
+
if (!shared) {
|
|
359
|
+
shared = new RotatingPluginLogger(normalized, {
|
|
360
|
+
...options,
|
|
361
|
+
directory: target.directory,
|
|
362
|
+
anchor: target.anchor,
|
|
363
|
+
profile: target.profile
|
|
364
|
+
});
|
|
365
|
+
processFileLoggers.set(key, shared);
|
|
366
|
+
}
|
|
367
|
+
backing = shared;
|
|
368
|
+
} else backing = new RotatingPluginLogger(normalized, {
|
|
369
|
+
...options,
|
|
370
|
+
...target.directory ? {
|
|
371
|
+
directory: target.directory,
|
|
372
|
+
anchor: target.anchor
|
|
373
|
+
} : {},
|
|
374
|
+
...target.profile ? { profile: target.profile } : {}
|
|
375
|
+
});
|
|
376
|
+
if (options.forwardHostWarnError === false || !ctx || typeof ctx !== "object") return backing;
|
|
377
|
+
return wrapWithHostForwarding(backing, ctx);
|
|
378
|
+
}
|
|
379
|
+
function installPluginLogger(ctx, source, options = {}) {
|
|
380
|
+
const logger = createPluginLogger(ctx, source, options);
|
|
381
|
+
logger.info("plugin.loaded", "Host plugin initialized.", options.loadedContext);
|
|
382
|
+
const host = ctx && typeof ctx === "object" ? ctx : void 0;
|
|
383
|
+
if (typeof host?.effect === "function") try {
|
|
384
|
+
host.effect(() => () => {
|
|
385
|
+
logger.debug("plugin.disposed", "Host plugin disposed.");
|
|
386
|
+
}, `${logger.source}.logger`);
|
|
387
|
+
} catch {}
|
|
388
|
+
return logger;
|
|
389
|
+
}
|
|
390
|
+
function wrapWithHostForwarding(backing, host) {
|
|
391
|
+
const forward = (input) => {
|
|
392
|
+
if (input.level !== "warn" && input.level !== "error" && input.level !== "fault") return;
|
|
393
|
+
try {
|
|
394
|
+
const hostLogger = host.logger;
|
|
395
|
+
if (!hostLogger) return;
|
|
396
|
+
const detail = input.error instanceof Error ? `${input.message} (${input.error.message})` : input.error !== void 0 ? `${input.message} (${String(input.error)})` : input.message;
|
|
397
|
+
const text = sanitizePluginLogText(`${backing.source} [${input.event}]: ${detail}`, 1e3);
|
|
398
|
+
if (input.level === "warn") {
|
|
399
|
+
if (typeof hostLogger.warn === "function") hostLogger.warn(text);
|
|
400
|
+
} else if (typeof hostLogger.error === "function") hostLogger.error(text);
|
|
401
|
+
else if (typeof hostLogger.warn === "function") hostLogger.warn(text);
|
|
402
|
+
} catch {}
|
|
403
|
+
};
|
|
404
|
+
const wrapped = {
|
|
405
|
+
get source() {
|
|
406
|
+
return backing.source;
|
|
407
|
+
},
|
|
408
|
+
get file() {
|
|
409
|
+
return backing.file;
|
|
410
|
+
},
|
|
411
|
+
get lastWriteError() {
|
|
412
|
+
return backing.lastWriteError;
|
|
413
|
+
},
|
|
414
|
+
debug: (event, message, context) => backing.debug(event, message, context),
|
|
415
|
+
info: (event, message, context) => backing.info(event, message, context),
|
|
416
|
+
warn: (event, message, error, context) => {
|
|
417
|
+
const wrote = backing.warn(event, message, error, context);
|
|
418
|
+
forward({
|
|
419
|
+
level: "warn",
|
|
420
|
+
event,
|
|
421
|
+
message,
|
|
422
|
+
error,
|
|
423
|
+
context
|
|
424
|
+
});
|
|
425
|
+
return wrote;
|
|
426
|
+
},
|
|
427
|
+
error: (event, message, error, context) => {
|
|
428
|
+
const wrote = backing.error(event, message, error, context);
|
|
429
|
+
forward({
|
|
430
|
+
level: "error",
|
|
431
|
+
event,
|
|
432
|
+
message,
|
|
433
|
+
error,
|
|
434
|
+
context
|
|
435
|
+
});
|
|
436
|
+
return wrote;
|
|
437
|
+
},
|
|
438
|
+
fault: (event, message, error, context) => {
|
|
439
|
+
const wrote = backing.fault(event, message, error, context);
|
|
440
|
+
forward({
|
|
441
|
+
level: "fault",
|
|
442
|
+
event,
|
|
443
|
+
message,
|
|
444
|
+
error,
|
|
445
|
+
context
|
|
446
|
+
});
|
|
447
|
+
return wrote;
|
|
448
|
+
},
|
|
449
|
+
write: (input) => {
|
|
450
|
+
const wrote = backing.write(input);
|
|
451
|
+
forward(input);
|
|
452
|
+
return wrote;
|
|
453
|
+
},
|
|
454
|
+
child: (context) => createChildPluginLogger(wrapped, context)
|
|
455
|
+
};
|
|
456
|
+
return wrapped;
|
|
457
|
+
}
|
|
458
|
+
function nextPluginOperationId(prefix = "op") {
|
|
459
|
+
return `${normalizePluginLogSource(prefix)}-${process.pid}-${++operationSequence}`;
|
|
460
|
+
}
|
|
461
|
+
function currentPluginLogContext() {
|
|
462
|
+
return correlationStorage.getStore();
|
|
463
|
+
}
|
|
464
|
+
function withPluginLogContext(context, action) {
|
|
465
|
+
const merged = mergePluginLogContext(correlationStorage.getStore(), context) ?? {};
|
|
466
|
+
return correlationStorage.run(merged, action);
|
|
467
|
+
}
|
|
468
|
+
async function pluginLogStep(logger, step, context, action, completedLevel = "debug") {
|
|
469
|
+
const started = Date.now();
|
|
470
|
+
const base = {
|
|
471
|
+
...context,
|
|
472
|
+
step
|
|
473
|
+
};
|
|
474
|
+
logger.debug("step.started", "Operation step started.", base);
|
|
475
|
+
try {
|
|
476
|
+
const result = await action();
|
|
477
|
+
logger.write({
|
|
478
|
+
level: completedLevel,
|
|
479
|
+
event: "step.completed",
|
|
480
|
+
message: "Operation step completed.",
|
|
481
|
+
context: {
|
|
482
|
+
...base,
|
|
483
|
+
durationMs: Date.now() - started
|
|
484
|
+
}
|
|
485
|
+
});
|
|
486
|
+
return result;
|
|
487
|
+
} catch (error) {
|
|
488
|
+
logger.error("step.failed", "Operation step failed.", error, {
|
|
489
|
+
...base,
|
|
490
|
+
durationMs: Date.now() - started
|
|
491
|
+
});
|
|
492
|
+
throw error;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
function pluginLogSyncStep(logger, step, context, action, completedLevel = "debug") {
|
|
496
|
+
const started = Date.now();
|
|
497
|
+
const base = {
|
|
498
|
+
...context,
|
|
499
|
+
step
|
|
500
|
+
};
|
|
501
|
+
logger.debug("step.started", "Operation step started.", base);
|
|
502
|
+
try {
|
|
503
|
+
const result = action();
|
|
504
|
+
logger.write({
|
|
505
|
+
level: completedLevel,
|
|
506
|
+
event: "step.completed",
|
|
507
|
+
message: "Operation step completed.",
|
|
508
|
+
context: {
|
|
509
|
+
...base,
|
|
510
|
+
durationMs: Date.now() - started
|
|
511
|
+
}
|
|
512
|
+
});
|
|
513
|
+
return result;
|
|
514
|
+
} catch (error) {
|
|
515
|
+
logger.error("step.failed", "Operation step failed.", error, {
|
|
516
|
+
...base,
|
|
517
|
+
durationMs: Date.now() - started
|
|
518
|
+
});
|
|
519
|
+
throw error;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
function readPluginLogTail(file, maxBytes = 262144) {
|
|
523
|
+
const stat = lstatSync(file);
|
|
524
|
+
if (stat.isSymbolicLink() || !stat.isFile()) throw new Error("Diagnostic log is not a regular file.");
|
|
525
|
+
if (stat.size <= 0 || maxBytes <= 0) return "";
|
|
526
|
+
const length = Math.min(stat.size, maxBytes);
|
|
527
|
+
const descriptor = openSync(file, "r");
|
|
528
|
+
try {
|
|
529
|
+
const buffer = Buffer.alloc(length);
|
|
530
|
+
const read = readSync(descriptor, buffer, 0, buffer.length, stat.size - length);
|
|
531
|
+
let text = buffer.subarray(0, read).toString("utf8");
|
|
532
|
+
if (stat.size > length) {
|
|
533
|
+
const newline = text.indexOf("\n");
|
|
534
|
+
text = newline === -1 ? "" : text.slice(newline + 1);
|
|
535
|
+
}
|
|
536
|
+
return text;
|
|
537
|
+
} finally {
|
|
538
|
+
closeSync(descriptor);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
function lexists(file) {
|
|
542
|
+
try {
|
|
543
|
+
lstatSync(file);
|
|
544
|
+
return true;
|
|
545
|
+
} catch (error) {
|
|
546
|
+
if (error.code === "ENOENT") return false;
|
|
547
|
+
throw error;
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
function pathFromBaseUrl(baseUrl) {
|
|
551
|
+
try {
|
|
552
|
+
return baseUrl.includes("://") ? resolve(fileURLToPath(baseUrl)) : resolve(baseUrl);
|
|
553
|
+
} catch {
|
|
554
|
+
return null;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
function samePath(left, right) {
|
|
558
|
+
const a = resolve(left);
|
|
559
|
+
const b = resolve(right);
|
|
560
|
+
return process.platform === "win32" ? a.toLowerCase() === b.toLowerCase() : a === b;
|
|
561
|
+
}
|
|
562
|
+
function isInside(root, target) {
|
|
563
|
+
const rootResolved = resolve(root);
|
|
564
|
+
const targetResolved = resolve(target);
|
|
565
|
+
if (samePath(rootResolved, targetResolved)) return true;
|
|
566
|
+
const prefix = rootResolved.endsWith(sep) ? rootResolved : rootResolved + sep;
|
|
567
|
+
return process.platform === "win32" ? targetResolved.toLowerCase().startsWith(prefix.toLowerCase()) : targetResolved.startsWith(prefix);
|
|
568
|
+
}
|
|
569
|
+
//#endregion
|
|
570
|
+
export { DEFAULT_PLUGIN_LOG_LEVEL, PLUGIN_LOG_LEVELS, PLUGIN_LOG_MAX_AGE_MS, PLUGIN_LOG_MAX_ARCHIVE_BYTES, PLUGIN_LOG_MAX_CONTEXT_CHARS, PLUGIN_LOG_MAX_FILE_BYTES, PLUGIN_LOG_MAX_MESSAGE_CHARS, PLUGIN_LOG_RECENT_LIMIT, RotatingPluginLogger, buildPluginLogEntry, createChildPluginLogger, createPluginLogger, currentPluginLogContext, installPluginLogger, isPluginLogLevel, mergePluginLogContext, nextPluginOperationId, normalizePluginLogEvent, normalizePluginLogSource, parsePluginLogLevel, pluginLogDirectory, pluginLogLevelEnabled, pluginLogStep, pluginLogSyncStep, readPluginLogTail, resolveHostLogTarget, safeProfileName, sanitizePluginLogContext, sanitizePluginLogError, sanitizePluginLogText, truncateLogText, withPluginLogContext };
|
|
571
|
+
|
|
572
|
+
//# sourceMappingURL=logger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"logger.js","names":[],"sources":["../src/logger.ts"],"sourcesContent":["import { AsyncLocalStorage } from 'node:async_hooks'\nimport {\n appendFileSync, closeSync, existsSync, lstatSync, mkdirSync, openSync,\n readSync, readdirSync, realpathSync, renameSync, unlinkSync,\n} from 'node:fs'\nimport { homedir } from 'node:os'\nimport { basename, dirname, join, resolve, sep } from 'node:path'\nimport { fileURLToPath } from 'node:url'\nimport {\n DEFAULT_PLUGIN_LOG_LEVEL, buildPluginLogEntry, createChildPluginLogger,\n mergePluginLogContext, normalizePluginLogSource, parsePluginLogLevel,\n pluginLogLevelEnabled, sanitizePluginLogText,\n type PluginLogContext, type PluginLogEntry, type PluginLogInput,\n type PluginLogLevel, type PluginLogger,\n} from './log-shared.ts'\n\nexport * from './log-shared.ts'\n\nexport const PLUGIN_LOG_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000\nexport const PLUGIN_LOG_MAX_FILE_BYTES = 100 * 1024 * 1024\nexport const PLUGIN_LOG_MAX_ARCHIVE_BYTES = 100 * 1024 * 1024\nexport const PLUGIN_LOG_RECENT_LIMIT = 200\n\nconst PROFILE_RE = /^[a-zA-Z0-9][a-zA-Z0-9._-]{0,63}$/\nconst ARCHIVE_RE = /^[a-z][a-z0-9-]{0,63}\\.\\d{4}-\\d{2}-\\d{2}T\\d{2}-\\d{2}-\\d{2}\\.\\d{3}Z\\.\\d+\\.\\d+\\.jsonl$/\n\nconst correlationStorage = new AsyncLocalStorage<PluginLogContext>()\nconst processFileLoggers = new Map<string, RotatingPluginLogger>()\nlet operationSequence = 0\n\nexport interface RotatingPluginLoggerOptions {\n directory?: string\n home?: string\n profile?: string\n minimumLevel?: PluginLogLevel\n env?: NodeJS.ProcessEnv\n now?: () => Date\n pid?: number\n maxFileBytes?: number\n maxArchiveBytes?: number\n maxAgeMs?: number\n recentLimit?: number\n forwardHostWarnError?: boolean\n}\n\nexport interface InstallPluginLoggerOptions extends RotatingPluginLoggerOptions {\n loadedContext?: PluginLogContext\n}\n\ntype HostContextLike = {\n baseUrl?: unknown\n get?: (name: string, ...rest: unknown[]) => unknown\n effect?: (callback: () => () => void | Promise<void>, label?: string) => unknown\n logger?: {\n warn?: (...args: unknown[]) => void\n error?: (...args: unknown[]) => void\n }\n}\n\nexport function safeProfileName(profile: string | undefined | null): string {\n const trimmed = String(profile ?? '').trim()\n if (PROFILE_RE.test(trimmed)) return trimmed.toLowerCase()\n const cleaned = trimmed.toLowerCase().replace(/[^a-z0-9._-]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 64)\n return cleaned && PROFILE_RE.test(cleaned) ? cleaned : 'default'\n}\n\nexport function pluginLogDirectory(home: string, profile = 'default'): string {\n return join(resolve(home), 'logs', 'plugins', safeProfileName(profile))\n}\n\nexport function resolveHostLogTarget(\n ctx: unknown,\n options: Pick<RotatingPluginLoggerOptions, 'directory' | 'home' | 'profile' | 'env'> = {},\n): { directory: string | null; anchor: string | null; profile?: string } {\n const env = options.env ?? process.env\n if (options.directory?.trim()) {\n const dir = resolve(options.directory.trim())\n return { directory: dir, anchor: dir, ...(options.profile ? { profile: safeProfileName(options.profile) } : {}) }\n }\n if (options.home?.trim()) {\n const home = resolve(options.home.trim())\n const profile = safeProfileName(options.profile)\n return { directory: pluginLogDirectory(home, profile), anchor: home, profile }\n }\n if (env.DSH_PLUGIN_LOG_DIR?.trim()) {\n const dir = resolve(env.DSH_PLUGIN_LOG_DIR.trim())\n return { directory: dir, anchor: dir, ...(options.profile ? { profile: safeProfileName(options.profile) } : {}) }\n }\n const host = ctx && typeof ctx === 'object' ? ctx as HostContextLike : undefined\n let profileCtx: { home?: unknown; name?: unknown; dir?: unknown } | undefined\n try {\n profileCtx = host?.get?.('profileContext', false) as typeof profileCtx\n } catch {\n try { profileCtx = host?.get?.('profileContext') as typeof profileCtx } catch { profileCtx = undefined }\n }\n if (\n typeof profileCtx?.home === 'string' && profileCtx.home.trim()\n && typeof profileCtx.dir === 'string' && profileCtx.dir.trim()\n ) {\n const home = resolve(profileCtx.home.trim())\n const dir = resolve(profileCtx.dir.trim())\n if (isInside(home, dir)) {\n const profile = safeProfileName(typeof profileCtx.name === 'string' ? profileCtx.name : basename(dir))\n return { directory: pluginLogDirectory(home, profile), anchor: home, profile }\n }\n }\n const inUnitTest = Boolean(env.VITEST || env.NODE_TEST_CONTEXT)\n if (!inUnitTest && typeof host?.baseUrl === 'string' && host.baseUrl.trim()) {\n const basePath = pathFromBaseUrl(host.baseUrl)\n const configuredHome = resolve((env.DSH_HOME?.trim() || join(homedir(), '.dsh')).trim())\n const profilesRoot = join(configuredHome, 'profiles')\n if (basePath && isInside(profilesRoot, basePath) && !samePath(profilesRoot, basePath)) {\n const rel = basePath.slice(profilesRoot.length).replace(/^[/\\\\]+/, '')\n const firstSegment = rel.split(/[/\\\\]/)[0]\n if (firstSegment) {\n const profile = safeProfileName(firstSegment)\n return { directory: pluginLogDirectory(configuredHome, profile), anchor: configuredHome, profile }\n }\n }\n }\n return { directory: null, anchor: null, ...(options.profile ? { profile: safeProfileName(options.profile) } : {}) }\n}\n\n/**\n * Synchronous, append-only rotating JSONL logger modeled on DSH Spaces\n * diagnostics: 7-day or 100 MiB active rotation, 7-day / 100 MiB archive\n * retention, secret and path redaction, and non-throwing write resilience.\n */\nexport class RotatingPluginLogger implements PluginLogger {\n readonly source: string\n readonly file: string | null\n private lastError: string | null = null\n private size = 0\n private openedAt = 0\n private initialized = false\n private writing = false\n private sequence = 0\n private loggedWriteFailure = false\n private readonly recent: PluginLogEntry[] = []\n private readonly directory: string | null\n private readonly anchor: string | null\n private readonly profile: string | undefined\n private readonly minimum: PluginLogLevel\n private readonly now: () => Date\n private readonly pid: number\n private readonly maxFileBytes: number\n private readonly maxArchiveBytes: number\n private readonly maxAgeMs: number\n private readonly recentLimit: number\n\n constructor(source: string, options: RotatingPluginLoggerOptions & { anchor?: string | null } = {}) {\n this.source = normalizePluginLogSource(source)\n const env = options.env ?? process.env\n const resolved = options.directory !== undefined || options.home !== undefined\n ? resolveHostLogTarget(undefined, options)\n : { directory: null, anchor: options.anchor ?? null, profile: options.profile }\n this.directory = resolved.directory\n this.anchor = options.anchor ?? resolved.anchor\n this.profile = options.profile ?? resolved.profile\n this.file = this.directory ? join(this.directory, `${this.source}.jsonl`) : null\n this.minimum = options.minimumLevel ?? parsePluginLogLevel(env.DSH_PLUGIN_LOG_LEVEL, this.source, DEFAULT_PLUGIN_LOG_LEVEL)\n this.now = options.now ?? (() => new Date())\n this.pid = options.pid ?? process.pid\n this.maxFileBytes = options.maxFileBytes ?? PLUGIN_LOG_MAX_FILE_BYTES\n this.maxArchiveBytes = options.maxArchiveBytes ?? PLUGIN_LOG_MAX_ARCHIVE_BYTES\n this.maxAgeMs = options.maxAgeMs ?? PLUGIN_LOG_MAX_AGE_MS\n this.recentLimit = Math.max(1, options.recentLimit ?? PLUGIN_LOG_RECENT_LIMIT)\n }\n\n get lastWriteError(): string | null {\n return this.lastError\n }\n\n recentEntries(limit = this.recentLimit): PluginLogEntry[] {\n return this.recent.slice(-Math.max(0, limit))\n }\n\n debug(event: string, message: string, context?: PluginLogContext): boolean {\n return this.write({ level: 'debug', event, message, ...(context ? { context } : {}) })\n }\n\n info(event: string, message: string, context?: PluginLogContext): boolean {\n return this.write({ level: 'info', event, message, ...(context ? { context } : {}) })\n }\n\n warn(event: string, message: string, error?: unknown, context?: PluginLogContext): boolean {\n return this.write({\n level: 'warn', event, message,\n ...(error !== undefined ? { error } : {}),\n ...(context ? { context } : {}),\n })\n }\n\n error(event: string, message: string, error?: unknown, context?: PluginLogContext): boolean {\n return this.write({\n level: 'error', event, message,\n ...(error !== undefined ? { error } : {}),\n ...(context ? { context } : {}),\n })\n }\n\n fault(event: string, message: string, error?: unknown, context?: PluginLogContext): boolean {\n return this.write({\n level: 'fault', event, message,\n ...(error !== undefined ? { error } : {}),\n ...(context ? { context } : {}),\n })\n }\n\n child(context: PluginLogContext): PluginLogger {\n return createChildPluginLogger(this, context)\n }\n\n write(input: PluginLogInput): boolean {\n if (!pluginLogLevelEnabled(input.level, this.minimum) || this.writing) return false\n const at = this.now()\n let entry: PluginLogEntry\n let line: string\n try {\n entry = buildPluginLogEntry(input, {\n at,\n source: this.source,\n surface: 'host',\n profile: this.profile,\n pid: this.pid,\n correlation: correlationStorage.getStore(),\n })\n line = `${JSON.stringify(entry)}\\n`\n } catch {\n return false\n }\n this.pushRecent(entry)\n if (!this.directory || !this.file) return true\n this.writing = true\n try {\n this.openManagedDirectory()\n this.prepare(at)\n if (\n this.size > 0\n && (this.size + Buffer.byteLength(line) > this.maxFileBytes || at.getTime() - this.openedAt >= this.maxAgeMs)\n ) {\n this.rotate(at)\n }\n appendFileSync(this.file, line, { encoding: 'utf8', mode: 0o600 })\n this.size += Buffer.byteLength(line)\n this.prune(at)\n this.lastError = null\n this.loggedWriteFailure = false\n return true\n } catch (error) {\n const detail = sanitizePluginLogText(error instanceof Error ? error.message : String(error), 500)\n this.lastError = `The plugin diagnostic log could not be written: ${detail}`\n this.initialized = false\n if (!this.loggedWriteFailure) {\n this.loggedWriteFailure = true\n try {\n process.stderr.write(`DSH Plugin (${this.source}): ${this.lastError}. The original operation continues unchanged.\\n`)\n } catch {\n // Broken stderr must never affect caller behavior.\n }\n }\n return false\n } finally {\n this.writing = false\n }\n }\n\n private pushRecent(entry: PluginLogEntry): void {\n this.recent.push(entry)\n const overflow = this.recent.length - this.recentLimit\n if (overflow > 0) this.recent.splice(0, overflow)\n }\n\n private prepare(at: Date): void {\n if (this.initialized || !this.file) return\n this.openManagedDirectory()\n const stat = this.regularFile(this.file)\n this.size = stat?.size ?? 0\n const stamps = stat ? [stat.birthtimeMs, stat.mtimeMs].filter(value => value > 0) : []\n this.openedAt = stamps.length ? Math.min(...stamps) : at.getTime()\n this.initialized = true\n }\n\n private rotate(at: Date): void {\n if (!this.directory || !this.file) return\n const stamp = at.toISOString().replace(/:/g, '-')\n const name = `${this.source}.${stamp}.${this.pid}.${this.sequence++}.jsonl`\n if (!ARCHIVE_RE.test(name)) throw new Error('Diagnostic log archive name is invalid.')\n const archive = join(this.directory, name)\n if (lexists(archive)) throw new Error('Diagnostic log archive already exists.')\n try {\n renameSync(this.file, archive)\n } catch (error) {\n const code = (error as NodeJS.ErrnoException).code\n if (code === 'ENOENT') {\n this.size = 0\n this.openedAt = at.getTime()\n return\n }\n throw error\n }\n this.size = 0\n this.openedAt = at.getTime()\n }\n\n private rotateStaleActive(at: Date): void {\n if (!this.file) return\n const stat = this.regularFile(this.file)\n if (!stat || stat.size === 0) return\n if (at.getTime() - stat.mtimeMs < this.maxAgeMs) return\n this.rotate(at)\n }\n\n private prune(at: Date): void {\n if (!this.directory) return\n this.rotateStaleActive(at)\n const archives: Array<{ name: string; size: number; mtime: number }> = []\n let total = 0\n for (const name of readdirSync(this.directory)) {\n if (!ARCHIVE_RE.test(name) || !name.startsWith(`${this.source}.`)) continue\n const file = join(this.directory, name)\n const stat = lstatSync(file)\n if (stat.isSymbolicLink() || !stat.isFile()) throw new Error('Diagnostic log archive is not a regular file.')\n if (at.getTime() - stat.mtimeMs >= this.maxAgeMs) {\n try { unlinkSync(file) } catch { /* concurrent prune */ }\n continue\n }\n archives.push({ name, size: stat.size, mtime: stat.mtimeMs })\n total += stat.size\n }\n archives.sort((left, right) => left.mtime - right.mtime || left.name.localeCompare(right.name))\n while (total > this.maxArchiveBytes && archives.length) {\n const oldest = archives.shift()!\n try { unlinkSync(join(this.directory, oldest.name)) } catch { /* concurrent prune */ }\n total -= oldest.size\n }\n }\n\n private openManagedDirectory(): void {\n if (!this.directory) return\n if (this.anchor) {\n const anchorStat = existsSync(this.anchor) ? lstatSync(this.anchor) : undefined\n if (anchorStat?.isSymbolicLink()) throw new Error('Diagnostic log anchor cannot be a symbolic link.')\n }\n mkdirSync(this.directory, { recursive: true, mode: 0o700 })\n const stat = lstatSync(this.directory)\n if (stat.isSymbolicLink() || !stat.isDirectory()) {\n throw new Error('Diagnostic log directory is not a real directory.')\n }\n const realDir = realpathSync(this.directory)\n if (this.anchor) {\n const realAnchor = realpathSync(this.anchor)\n if (!isInside(realAnchor, realDir)) {\n throw new Error('Diagnostic log directory escaped its managed root.')\n }\n }\n }\n\n private regularFile(file: string): { size: number; birthtimeMs: number; mtimeMs: number } | undefined {\n try {\n const stat = lstatSync(file)\n if (stat.isSymbolicLink() || !stat.isFile()) throw new Error('Diagnostic log is not a regular file.')\n const expected = join(realpathSync(dirname(file)), basename(file))\n if (!samePath(realpathSync(file), expected)) throw new Error('Diagnostic log path could not be verified.')\n return stat\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return undefined\n throw error\n }\n }\n}\n\nexport function createPluginLogger(\n ctxOrSource: unknown,\n maybeSource?: string | RotatingPluginLoggerOptions,\n maybeOptions?: RotatingPluginLoggerOptions,\n): PluginLogger {\n const hasCtx = typeof ctxOrSource !== 'string'\n const ctx = hasCtx ? ctxOrSource : undefined\n const source = hasCtx ? String(maybeSource ?? 'dsh-plugin') : ctxOrSource\n const options = (hasCtx ? maybeOptions : maybeSource as RotatingPluginLoggerOptions | undefined) ?? {}\n const normalized = normalizePluginLogSource(source)\n const target = resolveHostLogTarget(ctx, options)\n\n let backing: RotatingPluginLogger\n const canShare = Boolean(\n target.directory\n && options.minimumLevel === undefined\n && options.now === undefined\n && options.pid === undefined\n && options.maxFileBytes === undefined\n && options.maxArchiveBytes === undefined\n && options.maxAgeMs === undefined,\n )\n if (canShare && target.directory) {\n const key = resolve(target.directory, `${normalized}.jsonl`)\n let shared = processFileLoggers.get(key)\n if (!shared) {\n shared = new RotatingPluginLogger(normalized, {\n ...options,\n directory: target.directory,\n anchor: target.anchor,\n profile: target.profile,\n })\n processFileLoggers.set(key, shared)\n }\n backing = shared\n } else {\n backing = new RotatingPluginLogger(normalized, {\n ...options,\n ...(target.directory ? { directory: target.directory, anchor: target.anchor } : {}),\n ...(target.profile ? { profile: target.profile } : {}),\n })\n }\n\n if (options.forwardHostWarnError === false || !ctx || typeof ctx !== 'object') {\n return backing\n }\n return wrapWithHostForwarding(backing, ctx as HostContextLike)\n}\n\nexport function installPluginLogger(\n ctx: unknown,\n source: string,\n options: InstallPluginLoggerOptions = {},\n): PluginLogger {\n const logger = createPluginLogger(ctx, source, options)\n logger.info('plugin.loaded', 'Host plugin initialized.', options.loadedContext)\n const host = ctx && typeof ctx === 'object' ? ctx as HostContextLike : undefined\n if (typeof host?.effect === 'function') {\n try {\n host.effect(() => () => {\n logger.debug('plugin.disposed', 'Host plugin disposed.')\n }, `${logger.source}.logger`)\n } catch {\n // Non-Cordis or mock effect implementation.\n }\n }\n return logger\n}\n\nfunction wrapWithHostForwarding(backing: PluginLogger, host: HostContextLike): PluginLogger {\n const forward = (input: PluginLogInput) => {\n if (input.level !== 'warn' && input.level !== 'error' && input.level !== 'fault') return\n try {\n const hostLogger = host.logger\n if (!hostLogger) return\n const detail = input.error instanceof Error\n ? `${input.message} (${input.error.message})`\n : input.error !== undefined\n ? `${input.message} (${String(input.error)})`\n : input.message\n const text = sanitizePluginLogText(`${backing.source} [${input.event}]: ${detail}`, 1_000)\n if (input.level === 'warn') {\n if (typeof hostLogger.warn === 'function') hostLogger.warn(text)\n } else if (typeof hostLogger.error === 'function') {\n hostLogger.error(text)\n } else if (typeof hostLogger.warn === 'function') {\n hostLogger.warn(text)\n }\n } catch {\n // Host logger forwarding is best-effort.\n }\n }\n const wrapped: PluginLogger = {\n get source() { return backing.source },\n get file() { return backing.file },\n get lastWriteError() { return backing.lastWriteError },\n debug: (event, message, context) => backing.debug(event, message, context),\n info: (event, message, context) => backing.info(event, message, context),\n warn: (event, message, error, context) => {\n const wrote = backing.warn(event, message, error, context)\n forward({ level: 'warn', event, message, error, context })\n return wrote\n },\n error: (event, message, error, context) => {\n const wrote = backing.error(event, message, error, context)\n forward({ level: 'error', event, message, error, context })\n return wrote\n },\n fault: (event, message, error, context) => {\n const wrote = backing.fault(event, message, error, context)\n forward({ level: 'fault', event, message, error, context })\n return wrote\n },\n write: input => {\n const wrote = backing.write(input)\n forward(input)\n return wrote\n },\n child: context => createChildPluginLogger(wrapped, context),\n }\n return wrapped\n}\n\nexport function nextPluginOperationId(prefix = 'op'): string {\n return `${normalizePluginLogSource(prefix)}-${process.pid}-${++operationSequence}`\n}\n\nexport function currentPluginLogContext(): PluginLogContext | undefined {\n return correlationStorage.getStore()\n}\n\nexport function withPluginLogContext<T>(context: PluginLogContext, action: () => T): T {\n const merged = mergePluginLogContext(correlationStorage.getStore(), context) ?? {}\n return correlationStorage.run(merged, action)\n}\n\nexport async function pluginLogStep<T>(\n logger: PluginLogger,\n step: string,\n context: PluginLogContext | undefined,\n action: () => Promise<T>,\n completedLevel: PluginLogLevel = 'debug',\n): Promise<T> {\n const started = Date.now()\n const base = { ...context, step }\n logger.debug('step.started', 'Operation step started.', base)\n try {\n const result = await action()\n logger.write({\n level: completedLevel,\n event: 'step.completed',\n message: 'Operation step completed.',\n context: { ...base, durationMs: Date.now() - started },\n })\n return result\n } catch (error) {\n logger.error('step.failed', 'Operation step failed.', error, {\n ...base,\n durationMs: Date.now() - started,\n })\n throw error\n }\n}\n\nexport function pluginLogSyncStep<T>(\n logger: PluginLogger,\n step: string,\n context: PluginLogContext | undefined,\n action: () => T,\n completedLevel: PluginLogLevel = 'debug',\n): T {\n const started = Date.now()\n const base = { ...context, step }\n logger.debug('step.started', 'Operation step started.', base)\n try {\n const result = action()\n logger.write({\n level: completedLevel,\n event: 'step.completed',\n message: 'Operation step completed.',\n context: { ...base, durationMs: Date.now() - started },\n })\n return result\n } catch (error) {\n logger.error('step.failed', 'Operation step failed.', error, {\n ...base,\n durationMs: Date.now() - started,\n })\n throw error\n }\n}\n\nexport function readPluginLogTail(file: string, maxBytes = 256 * 1024): string {\n const stat = lstatSync(file)\n if (stat.isSymbolicLink() || !stat.isFile()) throw new Error('Diagnostic log is not a regular file.')\n if (stat.size <= 0 || maxBytes <= 0) return ''\n const length = Math.min(stat.size, maxBytes)\n const descriptor = openSync(file, 'r')\n try {\n const buffer = Buffer.alloc(length)\n const read = readSync(descriptor, buffer, 0, buffer.length, stat.size - length)\n let text = buffer.subarray(0, read).toString('utf8')\n if (stat.size > length) {\n const newline = text.indexOf('\\n')\n text = newline === -1 ? '' : text.slice(newline + 1)\n }\n return text\n } finally {\n closeSync(descriptor)\n }\n}\n\nfunction lexists(file: string): boolean {\n try {\n lstatSync(file)\n return true\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return false\n throw error\n }\n}\n\nfunction pathFromBaseUrl(baseUrl: string): string | null {\n try {\n return baseUrl.includes('://') ? resolve(fileURLToPath(baseUrl)) : resolve(baseUrl)\n } catch {\n return null\n }\n}\n\nfunction samePath(left: string, right: string): boolean {\n const a = resolve(left)\n const b = resolve(right)\n return process.platform === 'win32' ? a.toLowerCase() === b.toLowerCase() : a === b\n}\n\nfunction isInside(root: string, target: string): boolean {\n const rootResolved = resolve(root)\n const targetResolved = resolve(target)\n if (samePath(rootResolved, targetResolved)) return true\n const prefix = rootResolved.endsWith(sep) ? rootResolved : rootResolved + sep\n return process.platform === 'win32'\n ? targetResolved.toLowerCase().startsWith(prefix.toLowerCase())\n : targetResolved.startsWith(prefix)\n}\n"],"mappings":";;;;;;;AAkBA,MAAa,wBAAwB;AACrC,MAAa,4BAA4B;AACzC,MAAa,+BAA+B;AAC5C,MAAa,0BAA0B;AAEvC,MAAM,aAAa;AACnB,MAAM,aAAa;AAEnB,MAAM,qBAAqB,IAAI,kBAAoC;AACnE,MAAM,qCAAqB,IAAI,IAAkC;AACjE,IAAI,oBAAoB;AA+BxB,SAAgB,gBAAgB,SAA4C;CAC1E,MAAM,UAAU,OAAO,WAAW,EAAE,CAAC,CAAC,KAAK;CAC3C,IAAI,WAAW,KAAK,OAAO,GAAG,OAAO,QAAQ,YAAY;CACzD,MAAM,UAAU,QAAQ,YAAY,CAAC,CAAC,QAAQ,kBAAkB,GAAG,CAAC,CAAC,QAAQ,YAAY,EAAE,CAAC,CAAC,MAAM,GAAG,EAAE;CACxG,OAAO,WAAW,WAAW,KAAK,OAAO,IAAI,UAAU;AACzD;AAEA,SAAgB,mBAAmB,MAAc,UAAU,WAAmB;CAC5E,OAAO,KAAK,QAAQ,IAAI,GAAG,QAAQ,WAAW,gBAAgB,OAAO,CAAC;AACxE;AAEA,SAAgB,qBACd,KACA,UAAuF,CAAC,GACjB;CACvE,MAAM,MAAM,QAAQ,OAAO,QAAQ;CACnC,IAAI,QAAQ,WAAW,KAAK,GAAG;EAC7B,MAAM,MAAM,QAAQ,QAAQ,UAAU,KAAK,CAAC;EAC5C,OAAO;GAAE,WAAW;GAAK,QAAQ;GAAK,GAAI,QAAQ,UAAU,EAAE,SAAS,gBAAgB,QAAQ,OAAO,EAAE,IAAI,CAAC;EAAG;CAClH;CACA,IAAI,QAAQ,MAAM,KAAK,GAAG;EACxB,MAAM,OAAO,QAAQ,QAAQ,KAAK,KAAK,CAAC;EACxC,MAAM,UAAU,gBAAgB,QAAQ,OAAO;EAC/C,OAAO;GAAE,WAAW,mBAAmB,MAAM,OAAO;GAAG,QAAQ;GAAM;EAAQ;CAC/E;CACA,IAAI,IAAI,oBAAoB,KAAK,GAAG;EAClC,MAAM,MAAM,QAAQ,IAAI,mBAAmB,KAAK,CAAC;EACjD,OAAO;GAAE,WAAW;GAAK,QAAQ;GAAK,GAAI,QAAQ,UAAU,EAAE,SAAS,gBAAgB,QAAQ,OAAO,EAAE,IAAI,CAAC;EAAG;CAClH;CACA,MAAM,OAAO,OAAO,OAAO,QAAQ,WAAW,MAAyB,KAAA;CACvE,IAAI;CACJ,IAAI;EACF,aAAa,MAAM,MAAM,kBAAkB,KAAK;CAClD,QAAQ;EACN,IAAI;GAAE,aAAa,MAAM,MAAM,gBAAgB;EAAuB,QAAQ;GAAE,aAAa,KAAA;EAAU;CACzG;CACA,IACE,OAAO,YAAY,SAAS,YAAY,WAAW,KAAK,KAAK,KAC1D,OAAO,WAAW,QAAQ,YAAY,WAAW,IAAI,KAAK,GAC7D;EACA,MAAM,OAAO,QAAQ,WAAW,KAAK,KAAK,CAAC;EAC3C,MAAM,MAAM,QAAQ,WAAW,IAAI,KAAK,CAAC;EACzC,IAAI,SAAS,MAAM,GAAG,GAAG;GACvB,MAAM,UAAU,gBAAgB,OAAO,WAAW,SAAS,WAAW,WAAW,OAAO,SAAS,GAAG,CAAC;GACrG,OAAO;IAAE,WAAW,mBAAmB,MAAM,OAAO;IAAG,QAAQ;IAAM;GAAQ;EAC/E;CACF;CAEA,IAAI,CADe,QAAQ,IAAI,UAAU,IAAI,iBAC/B,KAAK,OAAO,MAAM,YAAY,YAAY,KAAK,QAAQ,KAAK,GAAG;EAC3E,MAAM,WAAW,gBAAgB,KAAK,OAAO;EAC7C,MAAM,iBAAiB,SAAS,IAAI,UAAU,KAAK,KAAK,KAAK,QAAQ,GAAG,MAAM,EAAA,CAAG,KAAK,CAAC;EACvF,MAAM,eAAe,KAAK,gBAAgB,UAAU;EACpD,IAAI,YAAY,SAAS,cAAc,QAAQ,KAAK,CAAC,SAAS,cAAc,QAAQ,GAAG;GAErF,MAAM,eADM,SAAS,MAAM,aAAa,MAAM,CAAC,CAAC,QAAQ,WAAW,EAC5C,CAAC,CAAC,MAAM,OAAO,CAAC,CAAC;GACxC,IAAI,cAAc;IAChB,MAAM,UAAU,gBAAgB,YAAY;IAC5C,OAAO;KAAE,WAAW,mBAAmB,gBAAgB,OAAO;KAAG,QAAQ;KAAgB;IAAQ;GACnG;EACF;CACF;CACA,OAAO;EAAE,WAAW;EAAM,QAAQ;EAAM,GAAI,QAAQ,UAAU,EAAE,SAAS,gBAAgB,QAAQ,OAAO,EAAE,IAAI,CAAC;CAAG;AACpH;;;;;;AAOA,IAAa,uBAAb,MAA0D;CACxD;CACA;CACA,YAAmC;CACnC,OAAe;CACf,WAAmB;CACnB,cAAsB;CACtB,UAAkB;CAClB,WAAmB;CACnB,qBAA6B;CAC7B,SAA4C,CAAC;CAC7C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA,YAAY,QAAgB,UAAoE,CAAC,GAAG;EAClG,KAAK,SAAS,yBAAyB,MAAM;EAC7C,MAAM,MAAM,QAAQ,OAAO,QAAQ;EACnC,MAAM,WAAW,QAAQ,cAAc,KAAA,KAAa,QAAQ,SAAS,KAAA,IACjE,qBAAqB,KAAA,GAAW,OAAO,IACvC;GAAE,WAAW;GAAM,QAAQ,QAAQ,UAAU;GAAM,SAAS,QAAQ;EAAQ;EAChF,KAAK,YAAY,SAAS;EAC1B,KAAK,SAAS,QAAQ,UAAU,SAAS;EACzC,KAAK,UAAU,QAAQ,WAAW,SAAS;EAC3C,KAAK,OAAO,KAAK,YAAY,KAAK,KAAK,WAAW,GAAG,KAAK,OAAO,OAAO,IAAI;EAC5E,KAAK,UAAU,QAAQ,gBAAgB,oBAAoB,IAAI,sBAAsB,KAAK,QAAA,MAAgC;EAC1H,KAAK,MAAM,QAAQ,8BAAc,IAAI,KAAK;EAC1C,KAAK,MAAM,QAAQ,OAAO,QAAQ;EAClC,KAAK,eAAe,QAAQ,gBAAA;EAC5B,KAAK,kBAAkB,QAAQ,mBAAA;EAC/B,KAAK,WAAW,QAAQ,YAAA;EACxB,KAAK,cAAc,KAAK,IAAI,GAAG,QAAQ,eAAA,GAAsC;CAC/E;CAEA,IAAI,iBAAgC;EAClC,OAAO,KAAK;CACd;CAEA,cAAc,QAAQ,KAAK,aAA+B;EACxD,OAAO,KAAK,OAAO,MAAM,CAAC,KAAK,IAAI,GAAG,KAAK,CAAC;CAC9C;CAEA,MAAM,OAAe,SAAiB,SAAqC;EACzE,OAAO,KAAK,MAAM;GAAE,OAAO;GAAS;GAAO;GAAS,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;EAAG,CAAC;CACvF;CAEA,KAAK,OAAe,SAAiB,SAAqC;EACxE,OAAO,KAAK,MAAM;GAAE,OAAO;GAAQ;GAAO;GAAS,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;EAAG,CAAC;CACtF;CAEA,KAAK,OAAe,SAAiB,OAAiB,SAAqC;EACzF,OAAO,KAAK,MAAM;GAChB,OAAO;GAAQ;GAAO;GACtB,GAAI,UAAU,KAAA,IAAY,EAAE,MAAM,IAAI,CAAC;GACvC,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;EAC/B,CAAC;CACH;CAEA,MAAM,OAAe,SAAiB,OAAiB,SAAqC;EAC1F,OAAO,KAAK,MAAM;GAChB,OAAO;GAAS;GAAO;GACvB,GAAI,UAAU,KAAA,IAAY,EAAE,MAAM,IAAI,CAAC;GACvC,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;EAC/B,CAAC;CACH;CAEA,MAAM,OAAe,SAAiB,OAAiB,SAAqC;EAC1F,OAAO,KAAK,MAAM;GAChB,OAAO;GAAS;GAAO;GACvB,GAAI,UAAU,KAAA,IAAY,EAAE,MAAM,IAAI,CAAC;GACvC,GAAI,UAAU,EAAE,QAAQ,IAAI,CAAC;EAC/B,CAAC;CACH;CAEA,MAAM,SAAyC;EAC7C,OAAO,wBAAwB,MAAM,OAAO;CAC9C;CAEA,MAAM,OAAgC;EACpC,IAAI,CAAC,sBAAsB,MAAM,OAAO,KAAK,OAAO,KAAK,KAAK,SAAS,OAAO;EAC9E,MAAM,KAAK,KAAK,IAAI;EACpB,IAAI;EACJ,IAAI;EACJ,IAAI;GACF,QAAQ,oBAAoB,OAAO;IACjC;IACA,QAAQ,KAAK;IACb,SAAS;IACT,SAAS,KAAK;IACd,KAAK,KAAK;IACV,aAAa,mBAAmB,SAAS;GAC3C,CAAC;GACD,OAAO,GAAG,KAAK,UAAU,KAAK,EAAE;EAClC,QAAQ;GACN,OAAO;EACT;EACA,KAAK,WAAW,KAAK;EACrB,IAAI,CAAC,KAAK,aAAa,CAAC,KAAK,MAAM,OAAO;EAC1C,KAAK,UAAU;EACf,IAAI;GACF,KAAK,qBAAqB;GAC1B,KAAK,QAAQ,EAAE;GACf,IACE,KAAK,OAAO,MACR,KAAK,OAAO,OAAO,WAAW,IAAI,IAAI,KAAK,gBAAgB,GAAG,QAAQ,IAAI,KAAK,YAAY,KAAK,WAEpG,KAAK,OAAO,EAAE;GAEhB,eAAe,KAAK,MAAM,MAAM;IAAE,UAAU;IAAQ,MAAM;GAAM,CAAC;GACjE,KAAK,QAAQ,OAAO,WAAW,IAAI;GACnC,KAAK,MAAM,EAAE;GACb,KAAK,YAAY;GACjB,KAAK,qBAAqB;GAC1B,OAAO;EACT,SAAS,OAAO;GACd,MAAM,SAAS,sBAAsB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAAG,GAAG;GAChG,KAAK,YAAY,mDAAmD;GACpE,KAAK,cAAc;GACnB,IAAI,CAAC,KAAK,oBAAoB;IAC5B,KAAK,qBAAqB;IAC1B,IAAI;KACF,QAAQ,OAAO,MAAM,eAAe,KAAK,OAAO,KAAK,KAAK,UAAU,gDAAgD;IACtH,QAAQ,CAER;GACF;GACA,OAAO;EACT,UAAU;GACR,KAAK,UAAU;EACjB;CACF;CAEA,WAAmB,OAA6B;EAC9C,KAAK,OAAO,KAAK,KAAK;EACtB,MAAM,WAAW,KAAK,OAAO,SAAS,KAAK;EAC3C,IAAI,WAAW,GAAG,KAAK,OAAO,OAAO,GAAG,QAAQ;CAClD;CAEA,QAAgB,IAAgB;EAC9B,IAAI,KAAK,eAAe,CAAC,KAAK,MAAM;EACpC,KAAK,qBAAqB;EAC1B,MAAM,OAAO,KAAK,YAAY,KAAK,IAAI;EACvC,KAAK,OAAO,MAAM,QAAQ;EAC1B,MAAM,SAAS,OAAO,CAAC,KAAK,aAAa,KAAK,OAAO,CAAC,CAAC,QAAO,UAAS,QAAQ,CAAC,IAAI,CAAC;EACrF,KAAK,WAAW,OAAO,SAAS,KAAK,IAAI,GAAG,MAAM,IAAI,GAAG,QAAQ;EACjE,KAAK,cAAc;CACrB;CAEA,OAAe,IAAgB;EAC7B,IAAI,CAAC,KAAK,aAAa,CAAC,KAAK,MAAM;EACnC,MAAM,QAAQ,GAAG,YAAY,CAAC,CAAC,QAAQ,MAAM,GAAG;EAChD,MAAM,OAAO,GAAG,KAAK,OAAO,GAAG,MAAM,GAAG,KAAK,IAAI,GAAG,KAAK,WAAW;EACpE,IAAI,CAAC,WAAW,KAAK,IAAI,GAAG,MAAM,IAAI,MAAM,yCAAyC;EACrF,MAAM,UAAU,KAAK,KAAK,WAAW,IAAI;EACzC,IAAI,QAAQ,OAAO,GAAG,MAAM,IAAI,MAAM,wCAAwC;EAC9E,IAAI;GACF,WAAW,KAAK,MAAM,OAAO;EAC/B,SAAS,OAAO;GAEd,IADc,MAAgC,SACjC,UAAU;IACrB,KAAK,OAAO;IACZ,KAAK,WAAW,GAAG,QAAQ;IAC3B;GACF;GACA,MAAM;EACR;EACA,KAAK,OAAO;EACZ,KAAK,WAAW,GAAG,QAAQ;CAC7B;CAEA,kBAA0B,IAAgB;EACxC,IAAI,CAAC,KAAK,MAAM;EAChB,MAAM,OAAO,KAAK,YAAY,KAAK,IAAI;EACvC,IAAI,CAAC,QAAQ,KAAK,SAAS,GAAG;EAC9B,IAAI,GAAG,QAAQ,IAAI,KAAK,UAAU,KAAK,UAAU;EACjD,KAAK,OAAO,EAAE;CAChB;CAEA,MAAc,IAAgB;EAC5B,IAAI,CAAC,KAAK,WAAW;EACrB,KAAK,kBAAkB,EAAE;EACzB,MAAM,WAAiE,CAAC;EACxE,IAAI,QAAQ;EACZ,KAAK,MAAM,QAAQ,YAAY,KAAK,SAAS,GAAG;GAC9C,IAAI,CAAC,WAAW,KAAK,IAAI,KAAK,CAAC,KAAK,WAAW,GAAG,KAAK,OAAO,EAAE,GAAG;GACnE,MAAM,OAAO,KAAK,KAAK,WAAW,IAAI;GACtC,MAAM,OAAO,UAAU,IAAI;GAC3B,IAAI,KAAK,eAAe,KAAK,CAAC,KAAK,OAAO,GAAG,MAAM,IAAI,MAAM,+CAA+C;GAC5G,IAAI,GAAG,QAAQ,IAAI,KAAK,WAAW,KAAK,UAAU;IAChD,IAAI;KAAE,WAAW,IAAI;IAAE,QAAQ,CAAyB;IACxD;GACF;GACA,SAAS,KAAK;IAAE;IAAM,MAAM,KAAK;IAAM,OAAO,KAAK;GAAQ,CAAC;GAC5D,SAAS,KAAK;EAChB;EACA,SAAS,MAAM,MAAM,UAAU,KAAK,QAAQ,MAAM,SAAS,KAAK,KAAK,cAAc,MAAM,IAAI,CAAC;EAC9F,OAAO,QAAQ,KAAK,mBAAmB,SAAS,QAAQ;GACtD,MAAM,SAAS,SAAS,MAAM;GAC9B,IAAI;IAAE,WAAW,KAAK,KAAK,WAAW,OAAO,IAAI,CAAC;GAAE,QAAQ,CAAyB;GACrF,SAAS,OAAO;EAClB;CACF;CAEA,uBAAqC;EACnC,IAAI,CAAC,KAAK,WAAW;EACrB,IAAI,KAAK,QACY;QAAA,WAAW,KAAK,MAAM,IAAI,UAAU,KAAK,MAAM,IAAI,KAAA,EAAA,EACtD,eAAe,GAAG,MAAM,IAAI,MAAM,kDAAkD;EAAA;EAEtG,UAAU,KAAK,WAAW;GAAE,WAAW;GAAM,MAAM;EAAM,CAAC;EAC1D,MAAM,OAAO,UAAU,KAAK,SAAS;EACrC,IAAI,KAAK,eAAe,KAAK,CAAC,KAAK,YAAY,GAC7C,MAAM,IAAI,MAAM,mDAAmD;EAErE,MAAM,UAAU,aAAa,KAAK,SAAS;EAC3C,IAAI,KAAK,QAEH;OAAA,CAAC,SADc,aAAa,KAAK,MACd,GAAG,OAAO,GAC/B,MAAM,IAAI,MAAM,oDAAoD;EAAA;CAG1E;CAEA,YAAoB,MAAkF;EACpG,IAAI;GACF,MAAM,OAAO,UAAU,IAAI;GAC3B,IAAI,KAAK,eAAe,KAAK,CAAC,KAAK,OAAO,GAAG,MAAM,IAAI,MAAM,uCAAuC;GACpG,MAAM,WAAW,KAAK,aAAa,QAAQ,IAAI,CAAC,GAAG,SAAS,IAAI,CAAC;GACjE,IAAI,CAAC,SAAS,aAAa,IAAI,GAAG,QAAQ,GAAG,MAAM,IAAI,MAAM,4CAA4C;GACzG,OAAO;EACT,SAAS,OAAO;GACd,IAAK,MAAgC,SAAS,UAAU,OAAO,KAAA;GAC/D,MAAM;EACR;CACF;AACF;AAEA,SAAgB,mBACd,aACA,aACA,cACc;CACd,MAAM,SAAS,OAAO,gBAAgB;CACtC,MAAM,MAAM,SAAS,cAAc,KAAA;CACnC,MAAM,SAAS,SAAS,OAAO,eAAe,YAAY,IAAI;CAC9D,MAAM,WAAW,SAAS,eAAe,gBAA2D,CAAC;CACrG,MAAM,aAAa,yBAAyB,MAAM;CAClD,MAAM,SAAS,qBAAqB,KAAK,OAAO;CAEhD,IAAI;CAUJ,IATiB,QACf,OAAO,aACJ,QAAQ,iBAAiB,KAAA,KACzB,QAAQ,QAAQ,KAAA,KAChB,QAAQ,QAAQ,KAAA,KAChB,QAAQ,iBAAiB,KAAA,KACzB,QAAQ,oBAAoB,KAAA,KAC5B,QAAQ,aAAa,KAAA,CAEf,KAAK,OAAO,WAAW;EAChC,MAAM,MAAM,QAAQ,OAAO,WAAW,GAAG,WAAW,OAAO;EAC3D,IAAI,SAAS,mBAAmB,IAAI,GAAG;EACvC,IAAI,CAAC,QAAQ;GACX,SAAS,IAAI,qBAAqB,YAAY;IAC5C,GAAG;IACH,WAAW,OAAO;IAClB,QAAQ,OAAO;IACf,SAAS,OAAO;GAClB,CAAC;GACD,mBAAmB,IAAI,KAAK,MAAM;EACpC;EACA,UAAU;CACZ,OACE,UAAU,IAAI,qBAAqB,YAAY;EAC7C,GAAG;EACH,GAAI,OAAO,YAAY;GAAE,WAAW,OAAO;GAAW,QAAQ,OAAO;EAAO,IAAI,CAAC;EACjF,GAAI,OAAO,UAAU,EAAE,SAAS,OAAO,QAAQ,IAAI,CAAC;CACtD,CAAC;CAGH,IAAI,QAAQ,yBAAyB,SAAS,CAAC,OAAO,OAAO,QAAQ,UACnE,OAAO;CAET,OAAO,uBAAuB,SAAS,GAAsB;AAC/D;AAEA,SAAgB,oBACd,KACA,QACA,UAAsC,CAAC,GACzB;CACd,MAAM,SAAS,mBAAmB,KAAK,QAAQ,OAAO;CACtD,OAAO,KAAK,iBAAiB,4BAA4B,QAAQ,aAAa;CAC9E,MAAM,OAAO,OAAO,OAAO,QAAQ,WAAW,MAAyB,KAAA;CACvE,IAAI,OAAO,MAAM,WAAW,YAC1B,IAAI;EACF,KAAK,mBAAmB;GACtB,OAAO,MAAM,mBAAmB,uBAAuB;EACzD,GAAG,GAAG,OAAO,OAAO,QAAQ;CAC9B,QAAQ,CAER;CAEF,OAAO;AACT;AAEA,SAAS,uBAAuB,SAAuB,MAAqC;CAC1F,MAAM,WAAW,UAA0B;EACzC,IAAI,MAAM,UAAU,UAAU,MAAM,UAAU,WAAW,MAAM,UAAU,SAAS;EAClF,IAAI;GACF,MAAM,aAAa,KAAK;GACxB,IAAI,CAAC,YAAY;GACjB,MAAM,SAAS,MAAM,iBAAiB,QAClC,GAAG,MAAM,QAAQ,IAAI,MAAM,MAAM,QAAQ,KACzC,MAAM,UAAU,KAAA,IACd,GAAG,MAAM,QAAQ,IAAI,OAAO,MAAM,KAAK,EAAE,KACzC,MAAM;GACZ,MAAM,OAAO,sBAAsB,GAAG,QAAQ,OAAO,IAAI,MAAM,MAAM,KAAK,UAAU,GAAK;GACzF,IAAI,MAAM,UAAU,QACd;QAAA,OAAO,WAAW,SAAS,YAAY,WAAW,KAAK,IAAI;GAAA,OAC1D,IAAI,OAAO,WAAW,UAAU,YACrC,WAAW,MAAM,IAAI;QAChB,IAAI,OAAO,WAAW,SAAS,YACpC,WAAW,KAAK,IAAI;EAExB,QAAQ,CAER;CACF;CACA,MAAM,UAAwB;EAC5B,IAAI,SAAS;GAAE,OAAO,QAAQ;EAAO;EACrC,IAAI,OAAO;GAAE,OAAO,QAAQ;EAAK;EACjC,IAAI,iBAAiB;GAAE,OAAO,QAAQ;EAAe;EACrD,QAAQ,OAAO,SAAS,YAAY,QAAQ,MAAM,OAAO,SAAS,OAAO;EACzE,OAAO,OAAO,SAAS,YAAY,QAAQ,KAAK,OAAO,SAAS,OAAO;EACvE,OAAO,OAAO,SAAS,OAAO,YAAY;GACxC,MAAM,QAAQ,QAAQ,KAAK,OAAO,SAAS,OAAO,OAAO;GACzD,QAAQ;IAAE,OAAO;IAAQ;IAAO;IAAS;IAAO;GAAQ,CAAC;GACzD,OAAO;EACT;EACA,QAAQ,OAAO,SAAS,OAAO,YAAY;GACzC,MAAM,QAAQ,QAAQ,MAAM,OAAO,SAAS,OAAO,OAAO;GAC1D,QAAQ;IAAE,OAAO;IAAS;IAAO;IAAS;IAAO;GAAQ,CAAC;GAC1D,OAAO;EACT;EACA,QAAQ,OAAO,SAAS,OAAO,YAAY;GACzC,MAAM,QAAQ,QAAQ,MAAM,OAAO,SAAS,OAAO,OAAO;GAC1D,QAAQ;IAAE,OAAO;IAAS;IAAO;IAAS;IAAO;GAAQ,CAAC;GAC1D,OAAO;EACT;EACA,QAAO,UAAS;GACd,MAAM,QAAQ,QAAQ,MAAM,KAAK;GACjC,QAAQ,KAAK;GACb,OAAO;EACT;EACA,QAAO,YAAW,wBAAwB,SAAS,OAAO;CAC5D;CACA,OAAO;AACT;AAEA,SAAgB,sBAAsB,SAAS,MAAc;CAC3D,OAAO,GAAG,yBAAyB,MAAM,EAAE,GAAG,QAAQ,IAAI,GAAG,EAAE;AACjE;AAEA,SAAgB,0BAAwD;CACtE,OAAO,mBAAmB,SAAS;AACrC;AAEA,SAAgB,qBAAwB,SAA2B,QAAoB;CACrF,MAAM,SAAS,sBAAsB,mBAAmB,SAAS,GAAG,OAAO,KAAK,CAAC;CACjF,OAAO,mBAAmB,IAAI,QAAQ,MAAM;AAC9C;AAEA,eAAsB,cACpB,QACA,MACA,SACA,QACA,iBAAiC,SACrB;CACZ,MAAM,UAAU,KAAK,IAAI;CACzB,MAAM,OAAO;EAAE,GAAG;EAAS;CAAK;CAChC,OAAO,MAAM,gBAAgB,2BAA2B,IAAI;CAC5D,IAAI;EACF,MAAM,SAAS,MAAM,OAAO;EAC5B,OAAO,MAAM;GACX,OAAO;GACP,OAAO;GACP,SAAS;GACT,SAAS;IAAE,GAAG;IAAM,YAAY,KAAK,IAAI,IAAI;GAAQ;EACvD,CAAC;EACD,OAAO;CACT,SAAS,OAAO;EACd,OAAO,MAAM,eAAe,0BAA0B,OAAO;GAC3D,GAAG;GACH,YAAY,KAAK,IAAI,IAAI;EAC3B,CAAC;EACD,MAAM;CACR;AACF;AAEA,SAAgB,kBACd,QACA,MACA,SACA,QACA,iBAAiC,SAC9B;CACH,MAAM,UAAU,KAAK,IAAI;CACzB,MAAM,OAAO;EAAE,GAAG;EAAS;CAAK;CAChC,OAAO,MAAM,gBAAgB,2BAA2B,IAAI;CAC5D,IAAI;EACF,MAAM,SAAS,OAAO;EACtB,OAAO,MAAM;GACX,OAAO;GACP,OAAO;GACP,SAAS;GACT,SAAS;IAAE,GAAG;IAAM,YAAY,KAAK,IAAI,IAAI;GAAQ;EACvD,CAAC;EACD,OAAO;CACT,SAAS,OAAO;EACd,OAAO,MAAM,eAAe,0BAA0B,OAAO;GAC3D,GAAG;GACH,YAAY,KAAK,IAAI,IAAI;EAC3B,CAAC;EACD,MAAM;CACR;AACF;AAEA,SAAgB,kBAAkB,MAAc,WAAW,QAAoB;CAC7E,MAAM,OAAO,UAAU,IAAI;CAC3B,IAAI,KAAK,eAAe,KAAK,CAAC,KAAK,OAAO,GAAG,MAAM,IAAI,MAAM,uCAAuC;CACpG,IAAI,KAAK,QAAQ,KAAK,YAAY,GAAG,OAAO;CAC5C,MAAM,SAAS,KAAK,IAAI,KAAK,MAAM,QAAQ;CAC3C,MAAM,aAAa,SAAS,MAAM,GAAG;CACrC,IAAI;EACF,MAAM,SAAS,OAAO,MAAM,MAAM;EAClC,MAAM,OAAO,SAAS,YAAY,QAAQ,GAAG,OAAO,QAAQ,KAAK,OAAO,MAAM;EAC9E,IAAI,OAAO,OAAO,SAAS,GAAG,IAAI,CAAC,CAAC,SAAS,MAAM;EACnD,IAAI,KAAK,OAAO,QAAQ;GACtB,MAAM,UAAU,KAAK,QAAQ,IAAI;GACjC,OAAO,YAAY,KAAK,KAAK,KAAK,MAAM,UAAU,CAAC;EACrD;EACA,OAAO;CACT,UAAU;EACR,UAAU,UAAU;CACtB;AACF;AAEA,SAAS,QAAQ,MAAuB;CACtC,IAAI;EACF,UAAU,IAAI;EACd,OAAO;CACT,SAAS,OAAO;EACd,IAAK,MAAgC,SAAS,UAAU,OAAO;EAC/D,MAAM;CACR;AACF;AAEA,SAAS,gBAAgB,SAAgC;CACvD,IAAI;EACF,OAAO,QAAQ,SAAS,KAAK,IAAI,QAAQ,cAAc,OAAO,CAAC,IAAI,QAAQ,OAAO;CACpF,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAS,SAAS,MAAc,OAAwB;CACtD,MAAM,IAAI,QAAQ,IAAI;CACtB,MAAM,IAAI,QAAQ,KAAK;CACvB,OAAO,QAAQ,aAAa,UAAU,EAAE,YAAY,MAAM,EAAE,YAAY,IAAI,MAAM;AACpF;AAEA,SAAS,SAAS,MAAc,QAAyB;CACvD,MAAM,eAAe,QAAQ,IAAI;CACjC,MAAM,iBAAiB,QAAQ,MAAM;CACrC,IAAI,SAAS,cAAc,cAAc,GAAG,OAAO;CACnD,MAAM,SAAS,aAAa,SAAS,GAAG,IAAI,eAAe,eAAe;CAC1E,OAAO,QAAQ,aAAa,UACxB,eAAe,YAAY,CAAC,CAAC,WAAW,OAAO,YAAY,CAAC,IAC5D,eAAe,WAAW,MAAM;AACtC"}
|