@aexol/spectral 0.9.194 → 0.9.195
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/dist/commands/serve.d.ts.map +1 -1
- package/dist/commands/serve.js +5 -0
- package/dist/extensions/ports/platform.d.ts.map +1 -1
- package/dist/extensions/ports/platform.js +9 -3
- package/dist/extensions/spectral-vision-fallback.d.ts +2 -3
- package/dist/extensions/spectral-vision-fallback.d.ts.map +1 -1
- package/dist/extensions/spectral-vision-fallback.js +25 -20
- package/dist/relay/dispatcher.d.ts +6 -1
- package/dist/relay/dispatcher.d.ts.map +1 -1
- package/dist/relay/dispatcher.js +54 -0
- package/dist/sdk/coding-agent/core/agent-session.d.ts +4 -0
- package/dist/sdk/coding-agent/core/agent-session.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/agent-session.js +3 -0
- package/dist/sdk/coding-agent/core/bash-jobs.d.ts +1 -0
- package/dist/sdk/coding-agent/core/bash-jobs.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/bash-jobs.js +1 -1
- package/dist/sdk/coding-agent/core/sdk.d.ts +3 -0
- package/dist/sdk/coding-agent/core/sdk.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/sdk.js +1 -0
- package/dist/sdk/coding-agent/core/tools/bash.d.ts +6 -0
- package/dist/sdk/coding-agent/core/tools/bash.d.ts.map +1 -1
- package/dist/sdk/coding-agent/core/tools/bash.js +14 -6
- package/dist/server/agent-bridge.d.ts +7 -3
- package/dist/server/agent-bridge.d.ts.map +1 -1
- package/dist/server/agent-bridge.js +10 -8
- package/dist/server/dev-port-detection.d.ts +35 -0
- package/dist/server/dev-port-detection.d.ts.map +1 -0
- package/dist/server/dev-port-detection.js +113 -0
- package/dist/server/dev-process-registry.d.ts +93 -0
- package/dist/server/dev-process-registry.d.ts.map +1 -0
- package/dist/server/dev-process-registry.js +421 -0
- package/dist/server/handlers/dev-processes.d.ts +49 -0
- package/dist/server/handlers/dev-processes.d.ts.map +1 -0
- package/dist/server/handlers/dev-processes.js +200 -0
- package/dist/server/session-stream.d.ts +11 -0
- package/dist/server/session-stream.d.ts.map +1 -1
- package/dist/server/session-stream.js +19 -0
- package/dist/server/storage.d.ts +27 -0
- package/dist/server/storage.d.ts.map +1 -1
- package/dist/server/storage.js +71 -0
- package/dist/server/wire.d.ts +67 -0
- package/dist/server/wire.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -53,6 +53,7 @@ import { type AgentSettings } from "./handlers/agent-settings.js";
|
|
|
53
53
|
import { fetchAllowedModels as defaultFetchAllowedModels } from "../relay/models-fetch.js";
|
|
54
54
|
import type { SessionMemorySnapshot } from "./storage.js";
|
|
55
55
|
import type { ImageAttachment, ServerEvent, WireMessage } from "./wire.js";
|
|
56
|
+
import type { DevProcessRegistry } from "./dev-process-registry.js";
|
|
56
57
|
interface AgentBridgeCompactionOptions {
|
|
57
58
|
customInstructions?: string;
|
|
58
59
|
phaseBoundary?: boolean;
|
|
@@ -139,6 +140,8 @@ export interface AgentBridgeOptions {
|
|
|
139
140
|
* session is not hot-reloaded.
|
|
140
141
|
*/
|
|
141
142
|
agentSettings?: AgentSettings;
|
|
143
|
+
/** Machine-level registry for primary-agent background bash jobs. */
|
|
144
|
+
devProcessRegistry?: DevProcessRegistry;
|
|
142
145
|
/**
|
|
143
146
|
* Called when cloud-agent discovery reports the cached machine JWT was
|
|
144
147
|
* rejected by the backend (HTTP 401/403 or a GraphQL auth error). The
|
|
@@ -371,9 +374,10 @@ export declare class AgentBridge {
|
|
|
371
374
|
*
|
|
372
375
|
* When `images` is non-empty, each base64-encoded attachment is converted
|
|
373
376
|
* to a spectral `ImageContent` block and passed as `options.images` to
|
|
374
|
-
* `session.prompt()`.
|
|
375
|
-
*
|
|
376
|
-
*
|
|
377
|
+
* `session.prompt()`. Image-capable models receive the images natively;
|
|
378
|
+
* models without image support are handled by the spectral-vision-fallback
|
|
379
|
+
* extension, which replaces images with text descriptions before the
|
|
380
|
+
* provider request.
|
|
377
381
|
*/
|
|
378
382
|
prompt(text: string, images?: ImageAttachment[]): Promise<void>;
|
|
379
383
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-bridge.d.ts","sourceRoot":"","sources":["../../src/server/agent-bridge.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AAaH,OAAO,EAQL,KAAK,iBAAiB,EACtB,KAAK,gBAAgB,EAEtB,MAAM,8BAA8B,CAAC;AAatC,OAAO,EAEL,KAAK,aAAa,EACnB,MAAM,8BAA8B,CAAC;AAatC,OAAO,EACL,kBAAkB,IAAI,yBAAyB,EAEhD,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"agent-bridge.d.ts","sourceRoot":"","sources":["../../src/server/agent-bridge.ts"],"names":[],"mappings":"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"}
|
|
@@ -875,6 +875,7 @@ export class AgentBridge {
|
|
|
875
875
|
modelRegistry: this.modelRegistry,
|
|
876
876
|
settingsManager,
|
|
877
877
|
sessionId: this.opts.sessionId,
|
|
878
|
+
devProcessRegistry: this.opts.devProcessRegistry,
|
|
878
879
|
});
|
|
879
880
|
this.session = result.session;
|
|
880
881
|
// Headless UI context: forwards extension notify() calls as wire events
|
|
@@ -1042,7 +1043,7 @@ export class AgentBridge {
|
|
|
1042
1043
|
return ["text", "image"];
|
|
1043
1044
|
}
|
|
1044
1045
|
console.info(`[agent-bridge] Model ${m.modelId} (${m.provider}) reports supportsImages: false ` +
|
|
1045
|
-
`— not adding image to input modalities (vision
|
|
1046
|
+
`— not adding image to input modalities (vision fallback will analyze images before provider requests)`);
|
|
1046
1047
|
return ["text"];
|
|
1047
1048
|
}
|
|
1048
1049
|
registerSyntheticProviders(allowedModels) {
|
|
@@ -1344,18 +1345,19 @@ export class AgentBridge {
|
|
|
1344
1345
|
*
|
|
1345
1346
|
* When `images` is non-empty, each base64-encoded attachment is converted
|
|
1346
1347
|
* to a spectral `ImageContent` block and passed as `options.images` to
|
|
1347
|
-
* `session.prompt()`.
|
|
1348
|
-
*
|
|
1349
|
-
*
|
|
1348
|
+
* `session.prompt()`. Image-capable models receive the images natively;
|
|
1349
|
+
* models without image support are handled by the spectral-vision-fallback
|
|
1350
|
+
* extension, which replaces images with text descriptions before the
|
|
1351
|
+
* provider request.
|
|
1350
1352
|
*/
|
|
1351
1353
|
async prompt(text, images) {
|
|
1352
1354
|
if (!this.session)
|
|
1353
1355
|
throw new Error("AgentBridge.start() not called");
|
|
1354
1356
|
// Always pass images through to the session.
|
|
1355
|
-
//
|
|
1356
|
-
//
|
|
1357
|
-
//
|
|
1358
|
-
//
|
|
1357
|
+
// Image-capable models receive the images natively. Models without image
|
|
1358
|
+
// support are handled by the spectral-vision-fallback extension, which
|
|
1359
|
+
// intercepts images via the `context` event and replaces them with text
|
|
1360
|
+
// descriptions from a vision-capable model BEFORE they reach the LLM.
|
|
1359
1361
|
try {
|
|
1360
1362
|
if (images && images.length > 0) {
|
|
1361
1363
|
const imageContents = images.map((img) => ({
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { DetectedDevServer } from "./wire.js";
|
|
2
|
+
/**
|
|
3
|
+
* Scan all listening TCP ports and group them by owning pid.
|
|
4
|
+
*
|
|
5
|
+
* Reuses the ports extension's native-tooling parser rather than
|
|
6
|
+
* reimplementing lsof/ss/netstat parsing here.
|
|
7
|
+
*/
|
|
8
|
+
export declare function getTcpPortsByPid(): Promise<Map<number, number[]>>;
|
|
9
|
+
/**
|
|
10
|
+
* Best-effort resolution of a pid's current working directory.
|
|
11
|
+
*
|
|
12
|
+
* Swallows all errors and returns null when the process has exited, the
|
|
13
|
+
* platform is unsupported, or the OS tooling cannot inspect the process.
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolvePidCwd(pid: number): Promise<string | null>;
|
|
16
|
+
export declare function findProjectForCwd(cwd: string | null, projects: Array<{
|
|
17
|
+
id: string;
|
|
18
|
+
name: string;
|
|
19
|
+
path: string;
|
|
20
|
+
}>): {
|
|
21
|
+
id: string;
|
|
22
|
+
name: string;
|
|
23
|
+
} | null;
|
|
24
|
+
/**
|
|
25
|
+
* Scan listening TCP servers and match them to known Spectral projects.
|
|
26
|
+
*
|
|
27
|
+
* Results are deduplicated by `pid:port`, project-matched servers are sorted
|
|
28
|
+
* first, and ties are ordered by port ascending for a stable settings UI.
|
|
29
|
+
*/
|
|
30
|
+
export declare function listDetectedDevServers(projects: Array<{
|
|
31
|
+
id: string;
|
|
32
|
+
name: string;
|
|
33
|
+
path: string;
|
|
34
|
+
}>): Promise<DetectedDevServer[]>;
|
|
35
|
+
//# sourceMappingURL=dev-port-detection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-port-detection.d.ts","sourceRoot":"","sources":["../../src/server/dev-port-detection.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAInD;;;;;GAKG;AACH,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC,CAevE;AAED;;;;;GAKG;AACH,wBAAsB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA0BvE;AAED,wBAAgB,iBAAiB,CAChC,GAAG,EAAE,MAAM,GAAG,IAAI,EAClB,QAAQ,EAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GACzD;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAYrC;AAED;;;;;GAKG;AACH,wBAAsB,sBAAsB,CAC3C,QAAQ,EAAE,KAAK,CAAC;IAAE,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,GACzD,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAsC9B"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { readlink } from "node:fs/promises";
|
|
3
|
+
import { platform as osPlatform } from "node:os";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
import { scanPorts } from "../extensions/ports/platform.js";
|
|
6
|
+
const execFileAsync = promisify(execFile);
|
|
7
|
+
/**
|
|
8
|
+
* Scan all listening TCP ports and group them by owning pid.
|
|
9
|
+
*
|
|
10
|
+
* Reuses the ports extension's native-tooling parser rather than
|
|
11
|
+
* reimplementing lsof/ss/netstat parsing here.
|
|
12
|
+
*/
|
|
13
|
+
export async function getTcpPortsByPid() {
|
|
14
|
+
const { entries } = await scanPorts();
|
|
15
|
+
const byPid = new Map();
|
|
16
|
+
for (const entry of entries) {
|
|
17
|
+
if (entry.protocol !== "tcp" || entry.pid === null)
|
|
18
|
+
continue;
|
|
19
|
+
const ports = byPid.get(entry.pid) ?? [];
|
|
20
|
+
if (!ports.includes(entry.port))
|
|
21
|
+
ports.push(entry.port);
|
|
22
|
+
byPid.set(entry.pid, ports);
|
|
23
|
+
}
|
|
24
|
+
for (const ports of byPid.values()) {
|
|
25
|
+
ports.sort((a, b) => a - b);
|
|
26
|
+
}
|
|
27
|
+
return byPid;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Best-effort resolution of a pid's current working directory.
|
|
31
|
+
*
|
|
32
|
+
* Swallows all errors and returns null when the process has exited, the
|
|
33
|
+
* platform is unsupported, or the OS tooling cannot inspect the process.
|
|
34
|
+
*/
|
|
35
|
+
export async function resolvePidCwd(pid) {
|
|
36
|
+
const platform = osPlatform();
|
|
37
|
+
if (platform === "darwin") {
|
|
38
|
+
try {
|
|
39
|
+
const { stdout } = await execFileAsync("lsof", ["-a", "-p", String(pid), "-d", "cwd", "-Fn"], { timeout: 5_000, maxBuffer: 1024 * 1024 });
|
|
40
|
+
const cwdLine = stdout.split("\n").find((line) => line.startsWith("n"));
|
|
41
|
+
return cwdLine ? cwdLine.slice(1).trimEnd() : null;
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (platform === "linux") {
|
|
48
|
+
try {
|
|
49
|
+
return await readlink(`/proc/${pid}/cwd`);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
export function findProjectForCwd(cwd, projects) {
|
|
58
|
+
if (!cwd)
|
|
59
|
+
return null;
|
|
60
|
+
for (const project of projects) {
|
|
61
|
+
if (cwd === project.path ||
|
|
62
|
+
cwd.startsWith(`${project.path}/`) ||
|
|
63
|
+
cwd.startsWith(`${project.path}\\`)) {
|
|
64
|
+
return { id: project.id, name: project.name };
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Scan listening TCP servers and match them to known Spectral projects.
|
|
71
|
+
*
|
|
72
|
+
* Results are deduplicated by `pid:port`, project-matched servers are sorted
|
|
73
|
+
* first, and ties are ordered by port ascending for a stable settings UI.
|
|
74
|
+
*/
|
|
75
|
+
export async function listDetectedDevServers(projects) {
|
|
76
|
+
const { entries } = await scanPorts();
|
|
77
|
+
const seen = new Set();
|
|
78
|
+
const cwdCache = new Map();
|
|
79
|
+
const servers = [];
|
|
80
|
+
for (const entry of entries) {
|
|
81
|
+
if (entry.protocol !== "tcp" || entry.pid === null)
|
|
82
|
+
continue;
|
|
83
|
+
const key = `${entry.pid}:${entry.port}`;
|
|
84
|
+
if (seen.has(key))
|
|
85
|
+
continue;
|
|
86
|
+
seen.add(key);
|
|
87
|
+
let cwd = cwdCache.get(entry.pid);
|
|
88
|
+
if (cwd === undefined) {
|
|
89
|
+
cwd = await resolvePidCwd(entry.pid);
|
|
90
|
+
cwdCache.set(entry.pid, cwd);
|
|
91
|
+
}
|
|
92
|
+
const project = findProjectForCwd(cwd, projects);
|
|
93
|
+
servers.push({
|
|
94
|
+
id: key,
|
|
95
|
+
pid: entry.pid,
|
|
96
|
+
port: entry.port,
|
|
97
|
+
address: entry.address,
|
|
98
|
+
process: entry.process,
|
|
99
|
+
cwd: project ? cwd : null,
|
|
100
|
+
projectId: project?.id ?? null,
|
|
101
|
+
projectName: project?.name ?? null,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
return servers.sort((a, b) => {
|
|
105
|
+
const aMatched = a.projectId !== null ? 0 : 1;
|
|
106
|
+
const bMatched = b.projectId !== null ? 0 : 1;
|
|
107
|
+
if (aMatched !== bMatched)
|
|
108
|
+
return aMatched - bMatched;
|
|
109
|
+
if (a.port !== b.port)
|
|
110
|
+
return a.port - b.port;
|
|
111
|
+
return a.id.localeCompare(b.id);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { BashJobRegistry, type BashJob } from "../sdk/coding-agent/core/bash-jobs.js";
|
|
2
|
+
import { type OutputAccumulator as OutputAccumulatorType } from "../sdk/coding-agent/core/tools/output-accumulator.js";
|
|
3
|
+
import type { DevProcessSummary, ServerEvent } from "./wire.js";
|
|
4
|
+
export type { DevProcessStatus, DevProcessSummary } from "./wire.js";
|
|
5
|
+
/**
|
|
6
|
+
* Events emitted by the machine-level dev-process registry. Kept as a subset
|
|
7
|
+
* of `ServerEvent` so the session-stream manager can fan them out unchanged.
|
|
8
|
+
*/
|
|
9
|
+
export type DevProcessRegistryEvent = Extract<ServerEvent, {
|
|
10
|
+
type: "dev_processes_changed" | "dev_process_output";
|
|
11
|
+
}>;
|
|
12
|
+
/**
|
|
13
|
+
* Stable definition for a machine-level dev process. Definitions are persisted
|
|
14
|
+
* in SQLite (`dev_process_definitions`) so a command only has to pass the
|
|
15
|
+
* startup allow-list once.
|
|
16
|
+
*/
|
|
17
|
+
export interface DevProcessDefinitionInput {
|
|
18
|
+
definitionId?: string;
|
|
19
|
+
label?: string;
|
|
20
|
+
command: string;
|
|
21
|
+
cwd: string;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Machine-level registry for background bash jobs that should be visible in
|
|
25
|
+
* the landing "Dev servers" settings section.
|
|
26
|
+
*
|
|
27
|
+
* Extends the session-scoped `BashJobRegistry` so the existing bash tool
|
|
28
|
+
* factories accept it unchanged, while adding the list/summary surface and
|
|
29
|
+
* durable pidfile tracking under `<agentDir>/dev-processes/`.
|
|
30
|
+
*/
|
|
31
|
+
export declare class DevProcessRegistry extends BashJobRegistry {
|
|
32
|
+
private readonly records;
|
|
33
|
+
private readonly pidDir;
|
|
34
|
+
private readonly listeners;
|
|
35
|
+
private portRefresh;
|
|
36
|
+
constructor(agentDir: string);
|
|
37
|
+
/**
|
|
38
|
+
* Subscribe to registry events. Returns an unsubscribe function.
|
|
39
|
+
* Listeners are invoked synchronously and are expected to be cheap;
|
|
40
|
+
* a throwing listener is logged and isolated so it cannot break process
|
|
41
|
+
* tracking.
|
|
42
|
+
*/
|
|
43
|
+
onEvent(listener: (event: DevProcessRegistryEvent) => void): () => void;
|
|
44
|
+
create(input: {
|
|
45
|
+
id?: string;
|
|
46
|
+
pid?: number;
|
|
47
|
+
command: string;
|
|
48
|
+
cwd: string;
|
|
49
|
+
output: OutputAccumulatorType;
|
|
50
|
+
fullOutputPath: string;
|
|
51
|
+
kill: () => void;
|
|
52
|
+
definitionId?: string;
|
|
53
|
+
label?: string;
|
|
54
|
+
}): BashJob;
|
|
55
|
+
markExited(id: string, exitCode: number | null): void;
|
|
56
|
+
markKilled(id: string): void;
|
|
57
|
+
/** List all tracked processes, newest first. */
|
|
58
|
+
list(): DevProcessSummary[];
|
|
59
|
+
/**
|
|
60
|
+
* Spawn a persistent dev process. Unlike the agent's normal background bash
|
|
61
|
+
* path, this process is NOT force-killed after the 30-minute hard deadline
|
|
62
|
+
* (`waitForChildProcess(_, 0)`). It remains killable through the registry.
|
|
63
|
+
*/
|
|
64
|
+
spawnDaemon(definition: DevProcessDefinitionInput): DevProcessSummary;
|
|
65
|
+
/**
|
|
66
|
+
* Re-scan listening TCP ports and update any live record whose port set
|
|
67
|
+
* changed. Called automatically after a new daemon binds and at the end of
|
|
68
|
+
* pidfile reconciliation; `GET /api/dev-processes` also refreshes eagerly.
|
|
69
|
+
*/
|
|
70
|
+
refreshPorts(): Promise<void>;
|
|
71
|
+
private schedulePortRefresh;
|
|
72
|
+
/**
|
|
73
|
+
* Kill an existing dev process and start a fresh one from its saved
|
|
74
|
+
* definition. Returns null when the process id is unknown.
|
|
75
|
+
*/
|
|
76
|
+
restart(id: string): DevProcessSummary | null;
|
|
77
|
+
/**
|
|
78
|
+
* Rehydrate processes from durable pidfiles after a CLI restart.
|
|
79
|
+
*
|
|
80
|
+
* A live PID becomes a killable `recovered` record whose output is no
|
|
81
|
+
* longer attached; a dead PID has its stale pidfile removed.
|
|
82
|
+
*/
|
|
83
|
+
reconcile(): void;
|
|
84
|
+
private toSummary;
|
|
85
|
+
private emitOutput;
|
|
86
|
+
private emit;
|
|
87
|
+
private pidfilePath;
|
|
88
|
+
private writePidfile;
|
|
89
|
+
private readPidfile;
|
|
90
|
+
private removePidfile;
|
|
91
|
+
private isPidAlive;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=dev-process-registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dev-process-registry.d.ts","sourceRoot":"","sources":["../../src/server/dev-process-registry.ts"],"names":[],"mappings":"AAUA,OAAO,EACN,eAAe,EACf,KAAK,OAAO,EACZ,MAAM,uCAAuC,CAAC;AAG/C,OAAO,EAEN,KAAK,iBAAiB,IAAI,qBAAqB,EAC/C,MAAM,sDAAsD,CAAC;AAE9D,OAAO,KAAK,EAEX,iBAAiB,EACjB,WAAW,EACX,MAAM,WAAW,CAAC;AAEnB,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAErE;;;GAGG;AACH,MAAM,MAAM,uBAAuB,GAAG,OAAO,CAC5C,WAAW,EACX;IAAE,IAAI,EAAE,uBAAuB,GAAG,oBAAoB,CAAA;CAAE,CACxD,CAAC;AAKF;;;;GAIG;AACH,MAAM,WAAW,yBAAyB;IACzC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,GAAG,EAAE,MAAM,CAAC;CACZ;AAkCD;;;;;;;GAOG;AACH,qBAAa,kBAAmB,SAAQ,eAAe;IACtD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAuC;IAC/D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAEtB;IACJ,OAAO,CAAC,WAAW,CAA8B;gBAErC,QAAQ,EAAE,MAAM;IAK5B;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,GAAG,MAAM,IAAI;IAO9D,MAAM,CAAC,KAAK,EAAE;QACtB,EAAE,CAAC,EAAE,MAAM,CAAC;QACZ,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,OAAO,EAAE,MAAM,CAAC;QAChB,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE,qBAAqB,CAAC;QAC9B,cAAc,EAAE,MAAM,CAAC;QACvB,IAAI,EAAE,MAAM,IAAI,CAAC;QACjB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,KAAK,CAAC,EAAE,MAAM,CAAC;KACf,GAAG,OAAO;IA4BF,UAAU,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAoBrD,UAAU,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAoBrC,gDAAgD;IAChD,IAAI,IAAI,iBAAiB,EAAE;IAM3B;;;;OAIG;IACH,WAAW,CAAC,UAAU,EAAE,yBAAyB,GAAG,iBAAiB;IAoErE;;;;OAIG;IACG,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IA4CnC,OAAO,CAAC,mBAAmB;IAO3B;;;OAGG;IACH,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,iBAAiB,GAAG,IAAI;IAiB7C;;;;;OAKG;IACH,SAAS,IAAI,IAAI;IA+DjB,OAAO,CAAC,SAAS;IAkBjB,OAAO,CAAC,UAAU;IAqBlB,OAAO,CAAC,IAAI;IAcZ,OAAO,CAAC,WAAW;IAInB,OAAO,CAAC,YAAY;IAoBpB,OAAO,CAAC,WAAW;IAuBnB,OAAO,CAAC,aAAa;IAQrB,OAAO,CAAC,UAAU;CAQlB"}
|