sequant 2.3.0 → 2.5.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +2 -2
- package/README.md +125 -160
- package/dist/bin/cli.js +59 -4
- package/dist/dashboard/server.js +1 -0
- package/dist/marketplace/external_plugins/sequant/.claude-plugin/plugin.json +2 -2
- package/dist/marketplace/external_plugins/sequant/README.md +6 -3
- package/dist/marketplace/external_plugins/sequant/hooks/post-tool.sh +92 -0
- package/dist/marketplace/external_plugins/sequant/hooks/pre-tool.sh +18 -9
- package/dist/marketplace/external_plugins/sequant/hooks/relay-check.sh +107 -0
- package/dist/marketplace/external_plugins/sequant/skills/_shared/references/behavior-rule-detection.md +205 -0
- package/dist/marketplace/external_plugins/sequant/skills/_shared/references/subagent-types.md +21 -8
- package/dist/marketplace/external_plugins/sequant/skills/assess/SKILL.md +302 -86
- package/dist/marketplace/external_plugins/sequant/skills/assess/references/predicted-collision-detection.md +109 -0
- package/dist/marketplace/external_plugins/sequant/skills/docs/SKILL.md +141 -22
- package/dist/marketplace/external_plugins/sequant/skills/exec/SKILL.md +83 -78
- package/dist/marketplace/external_plugins/sequant/skills/fullsolve/SKILL.md +377 -137
- package/dist/marketplace/external_plugins/sequant/skills/loop/SKILL.md +28 -0
- package/dist/marketplace/external_plugins/sequant/skills/merger/SKILL.md +621 -0
- package/dist/marketplace/external_plugins/sequant/skills/qa/SKILL.md +741 -232
- package/dist/marketplace/external_plugins/sequant/skills/qa/scripts/quality-checks.sh +47 -1
- package/dist/marketplace/external_plugins/sequant/skills/setup/SKILL.md +12 -6
- package/dist/marketplace/external_plugins/sequant/skills/spec/SKILL.md +217 -964
- package/dist/marketplace/external_plugins/sequant/skills/spec/references/parallel-groups.md +7 -0
- package/dist/marketplace/external_plugins/sequant/skills/spec/references/quality-checklist.md +75 -0
- package/dist/marketplace/external_plugins/sequant/skills/spec/references/recommended-workflow.md +4 -2
- package/dist/marketplace/external_plugins/sequant/skills/test/SKILL.md +0 -27
- package/dist/marketplace/external_plugins/sequant/skills/testgen/SKILL.md +24 -44
- package/dist/src/commands/abort.d.ts +36 -0
- package/dist/src/commands/abort.js +138 -0
- package/dist/src/commands/prompt.d.ts +7 -0
- package/dist/src/commands/prompt.js +101 -7
- package/dist/src/commands/ready-tui-adapter.d.ts +59 -0
- package/dist/src/commands/ready-tui-adapter.js +130 -0
- package/dist/src/commands/ready.d.ts +49 -0
- package/dist/src/commands/ready.js +243 -0
- package/dist/src/commands/run-progress.d.ts +11 -1
- package/dist/src/commands/run-progress.js +20 -3
- package/dist/src/commands/run.js +12 -2
- package/dist/src/commands/status.js +4 -0
- package/dist/src/commands/watch.d.ts +2 -0
- package/dist/src/commands/watch.js +67 -3
- package/dist/src/lib/assess-collision-detect.js +1 -1
- package/dist/src/lib/cli-ui/run-renderer-types.d.ts +39 -0
- package/dist/src/lib/cli-ui/run-renderer.d.ts +34 -2
- package/dist/src/lib/cli-ui/run-renderer.js +250 -33
- package/dist/src/lib/cli-ui/scrollback-harness.d.ts +112 -0
- package/dist/src/lib/cli-ui/scrollback-harness.js +294 -0
- package/dist/src/lib/merge-check/types.js +1 -1
- package/dist/src/lib/relay/archive.js +6 -0
- package/dist/src/lib/relay/types.d.ts +2 -0
- package/dist/src/lib/relay/types.js +9 -0
- package/dist/src/lib/settings.d.ts +34 -0
- package/dist/src/lib/settings.js +23 -1
- package/dist/src/lib/workflow/batch-executor.js +34 -18
- package/dist/src/lib/workflow/drivers/agent-driver.d.ts +48 -1
- package/dist/src/lib/workflow/drivers/aider.d.ts +7 -1
- package/dist/src/lib/workflow/drivers/aider.js +9 -0
- package/dist/src/lib/workflow/drivers/claude-code.d.ts +17 -1
- package/dist/src/lib/workflow/drivers/claude-code.js +51 -2
- package/dist/src/lib/workflow/drivers/index.d.ts +1 -1
- package/dist/src/lib/workflow/event-emitter.d.ts +157 -0
- package/dist/src/lib/workflow/event-emitter.js +102 -0
- package/dist/src/lib/workflow/notice.d.ts +32 -0
- package/dist/src/lib/workflow/notice.js +38 -0
- package/dist/src/lib/workflow/phase-executor.d.ts +9 -21
- package/dist/src/lib/workflow/phase-executor.js +105 -117
- package/dist/src/lib/workflow/phase-mapper.d.ts +26 -13
- package/dist/src/lib/workflow/phase-mapper.js +55 -33
- package/dist/src/lib/workflow/phase-registry.d.ts +127 -0
- package/dist/src/lib/workflow/phase-registry.js +233 -0
- package/dist/src/lib/workflow/platforms/github.d.ts +6 -0
- package/dist/src/lib/workflow/platforms/github.js +17 -0
- package/dist/src/lib/workflow/ready-gate.d.ts +155 -0
- package/dist/src/lib/workflow/ready-gate.js +374 -0
- package/dist/src/lib/workflow/reconcile.js +6 -0
- package/dist/src/lib/workflow/run-log-schema.d.ts +5 -55
- package/dist/src/lib/workflow/run-orchestrator.d.ts +32 -2
- package/dist/src/lib/workflow/run-orchestrator.js +125 -11
- package/dist/src/lib/workflow/state-manager.d.ts +19 -1
- package/dist/src/lib/workflow/state-manager.js +27 -1
- package/dist/src/lib/workflow/state-schema.d.ts +23 -35
- package/dist/src/lib/workflow/state-schema.js +29 -3
- package/dist/src/lib/workflow/types.d.ts +74 -15
- package/dist/src/lib/workflow/types.js +18 -13
- package/dist/src/ui/tui/App.js +8 -2
- package/dist/src/ui/tui/IssueBox.js +3 -4
- package/dist/src/ui/tui/index.d.ts +13 -4
- package/dist/src/ui/tui/index.js +19 -5
- package/dist/src/ui/tui/row-cap.d.ts +51 -0
- package/dist/src/ui/tui/row-cap.js +76 -0
- package/dist/src/ui/tui/teardown.d.ts +20 -0
- package/dist/src/ui/tui/teardown.js +29 -0
- package/dist/src/ui/tui/theme.d.ts +3 -0
- package/dist/src/ui/tui/theme.js +3 -0
- package/package.json +23 -11
- package/templates/hooks/post-tool.sh +81 -0
- package/templates/skills/assess/SKILL.md +28 -28
- package/templates/skills/assess/references/predicted-collision-detection.md +1 -1
- package/templates/skills/qa/SKILL.md +5 -2
- package/templates/skills/setup/SKILL.md +6 -6
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
import { existsSync, statSync, createReadStream, watch } from "fs";
|
|
7
7
|
import chalk from "chalk";
|
|
8
8
|
import { outboxPathFor } from "../lib/relay/paths.js";
|
|
9
|
+
import { listArchives } from "../lib/relay/archive.js";
|
|
10
|
+
import { readPidFile } from "../lib/relay/pid.js";
|
|
9
11
|
import { StateManager } from "../lib/workflow/state-manager.js";
|
|
10
12
|
import { RelayResponseSchema } from "../lib/relay/types.js";
|
|
11
13
|
function formatTimestamp(iso) {
|
|
@@ -82,8 +84,10 @@ export async function watchCommand(argsAndOptions) {
|
|
|
82
84
|
}
|
|
83
85
|
const stateManager = new StateManager();
|
|
84
86
|
const issueState = await stateManager.getIssueState(issueNumber);
|
|
87
|
+
const cwd = options.cwd ?? process.cwd();
|
|
85
88
|
const outboxPath = outboxPathFor(issueNumber, {
|
|
86
89
|
worktreePath: issueState?.worktree,
|
|
90
|
+
cwd,
|
|
87
91
|
});
|
|
88
92
|
const pollIntervalMs = options.pollIntervalMs ?? 200;
|
|
89
93
|
const tail = { offset: 0, partial: "" };
|
|
@@ -91,16 +95,43 @@ export async function watchCommand(argsAndOptions) {
|
|
|
91
95
|
if (existsSync(outboxPath)) {
|
|
92
96
|
tail.offset = statSync(outboxPath).size;
|
|
93
97
|
}
|
|
98
|
+
// Dead-relay detection (#645, Gap 3). The pidfile is written by activateRelay
|
|
99
|
+
// and removed by deactivateRelay. If it's absent and the outbox is absent at
|
|
100
|
+
// startup, there is nothing alive to watch — print a useful pointer and exit.
|
|
101
|
+
const initialPidPresent = readPidFile(issueNumber, cwd) !== null;
|
|
102
|
+
const initialOutboxPresent = existsSync(outboxPath);
|
|
103
|
+
if (!initialPidPresent && !initialOutboxPresent) {
|
|
104
|
+
const archives = listArchives(issueNumber, cwd);
|
|
105
|
+
const summary = `No active relay for #${issueNumber}.`;
|
|
106
|
+
const hint = archives[0]
|
|
107
|
+
? ` Most recent archive: ${archives[0]}`
|
|
108
|
+
: " (no archived runs found)";
|
|
109
|
+
if (options.json) {
|
|
110
|
+
console.log(JSON.stringify({
|
|
111
|
+
ok: false,
|
|
112
|
+
issue: issueNumber,
|
|
113
|
+
reason: "no-active-relay",
|
|
114
|
+
archive: archives[0] ?? null,
|
|
115
|
+
}));
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
console.log(chalk.yellow(summary + hint));
|
|
119
|
+
}
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
94
122
|
if (!options.json) {
|
|
95
123
|
console.log(chalk.gray(`Watching #${issueNumber} outbox — Ctrl+C to stop`));
|
|
96
124
|
}
|
|
97
125
|
let stopped = false;
|
|
98
|
-
|
|
126
|
+
let endReason = null;
|
|
127
|
+
const stop = (reason = "signal") => {
|
|
99
128
|
stopped = true;
|
|
129
|
+
if (!endReason)
|
|
130
|
+
endReason = reason;
|
|
100
131
|
};
|
|
101
|
-
options.signal?.addEventListener("abort", stop);
|
|
132
|
+
options.signal?.addEventListener("abort", () => stop("signal"));
|
|
102
133
|
process.on("SIGINT", () => {
|
|
103
|
-
stop();
|
|
134
|
+
stop("signal");
|
|
104
135
|
if (!options.json)
|
|
105
136
|
console.log(chalk.gray("\nStopped watching."));
|
|
106
137
|
process.exit(0);
|
|
@@ -125,6 +156,7 @@ export async function watchCommand(argsAndOptions) {
|
|
|
125
156
|
};
|
|
126
157
|
// Polling loop — also used as a heartbeat when fs.watch is active so we
|
|
127
158
|
// don't miss events on filesystems where watch is unreliable.
|
|
159
|
+
let sawLivePid = initialPidPresent;
|
|
128
160
|
while (!stopped) {
|
|
129
161
|
try {
|
|
130
162
|
const replies = await readNewLines(outboxPath, tail);
|
|
@@ -133,6 +165,23 @@ export async function watchCommand(argsAndOptions) {
|
|
|
133
165
|
catch {
|
|
134
166
|
/* transient — try again next tick */
|
|
135
167
|
}
|
|
168
|
+
// Dead-relay detection (#645, Gap 3). Once we've seen a live pidfile, its
|
|
169
|
+
// absence means the run has deactivated relay (archive complete). Drain
|
|
170
|
+
// one more poll for late writes, then exit cleanly.
|
|
171
|
+
const pidAlive = readPidFile(issueNumber, cwd) !== null;
|
|
172
|
+
if (sawLivePid && !pidAlive) {
|
|
173
|
+
try {
|
|
174
|
+
const finalReplies = await readNewLines(outboxPath, tail);
|
|
175
|
+
emit(finalReplies);
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
/* swallow */
|
|
179
|
+
}
|
|
180
|
+
stop("relay-ended");
|
|
181
|
+
break;
|
|
182
|
+
}
|
|
183
|
+
if (pidAlive)
|
|
184
|
+
sawLivePid = true;
|
|
136
185
|
await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
|
|
137
186
|
}
|
|
138
187
|
if (watcher) {
|
|
@@ -144,4 +193,19 @@ export async function watchCommand(argsAndOptions) {
|
|
|
144
193
|
}
|
|
145
194
|
}
|
|
146
195
|
void useWatcher; // currently unused beyond best-effort init
|
|
196
|
+
if (endReason === "relay-ended") {
|
|
197
|
+
const archives = listArchives(issueNumber, cwd);
|
|
198
|
+
if (options.json) {
|
|
199
|
+
console.log(JSON.stringify({
|
|
200
|
+
ok: true,
|
|
201
|
+
issue: issueNumber,
|
|
202
|
+
reason: "relay-ended",
|
|
203
|
+
archive: archives[0] ?? null,
|
|
204
|
+
}));
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
const hint = archives[0] ? ` Archive: ${archives[0]}` : "";
|
|
208
|
+
console.log(chalk.gray(`Run for #${issueNumber} ended.${hint}`));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
147
211
|
}
|
|
@@ -208,7 +208,7 @@ export function formatCollisionAnnotations(results) {
|
|
|
208
208
|
if (r.issues.length >= 3 && !chainSuggestion) {
|
|
209
209
|
const ids = r.issues.join(" ");
|
|
210
210
|
chainSuggestion =
|
|
211
|
-
`Chain: npx sequant run ${ids} --chain --qa-gate -
|
|
211
|
+
`Chain: npx sequant run ${ids} --chain --qa-gate -Q ` +
|
|
212
212
|
`# alternative — ${r.issues.length} issues modify ${r.file} ` +
|
|
213
213
|
`(chain length≥3 historically 1/6 = 17%; see docs/reference/chain-mode-analysis-2026-05.md)`;
|
|
214
214
|
}
|
|
@@ -76,6 +76,15 @@ export interface IssueRegistration {
|
|
|
76
76
|
* is known) and switches to the normal phase header once spec completes.
|
|
77
77
|
*/
|
|
78
78
|
autoDetect?: boolean;
|
|
79
|
+
/**
|
|
80
|
+
* #672 AC-2: the resolved phase pipeline for this issue. When set, the live
|
|
81
|
+
* zone seeds one pending cell per planned phase so users see the full
|
|
82
|
+
* roadmap before any phase fires. Cells transition pending → running → ✔/✘
|
|
83
|
+
* in place via subsequent `onEvent` calls (#672 AC-3). When the plan isn't
|
|
84
|
+
* known at registration time (auto-detect mode), call `setPhasePlan` once
|
|
85
|
+
* spec resolves it.
|
|
86
|
+
*/
|
|
87
|
+
plannedPhases?: string[];
|
|
79
88
|
}
|
|
80
89
|
/** Per-issue summary fields used by the final summary table. */
|
|
81
90
|
export interface IssueSummary {
|
|
@@ -106,12 +115,26 @@ export interface RunRenderer {
|
|
|
106
115
|
registerIssue(reg: IssueRegistration): void;
|
|
107
116
|
/** Feed a progress event from batch-executor. */
|
|
108
117
|
onEvent(event: ProgressEvent): void;
|
|
118
|
+
/**
|
|
119
|
+
* #672 AC-2: set or replace the planned phase pipeline for an already-
|
|
120
|
+
* registered issue. Used by auto-detect mode after spec resolves the plan.
|
|
121
|
+
* No-op for unregistered issues (defensive — same as `setPullRequest`).
|
|
122
|
+
* An empty `phases` array clears the plan back to streaming-only behaviour.
|
|
123
|
+
*/
|
|
124
|
+
setPhasePlan(issue: number, phases: string[]): void;
|
|
109
125
|
/** Mark an issue as completed with PR info. Called by orchestrator. */
|
|
110
126
|
setPullRequest(issue: number, prNumber: number, prUrl: string): void;
|
|
111
127
|
/** Pause live updates so verbose streaming can write through. */
|
|
112
128
|
pause(): void;
|
|
113
129
|
/** Resume live updates after streaming ends. */
|
|
114
130
|
resume(): void;
|
|
131
|
+
/**
|
|
132
|
+
* #647 AC-3: print a notice line above the live zone without breaking
|
|
133
|
+
* log-update's cursor model. Use for retry/fallback messages emitted
|
|
134
|
+
* from outside the renderer's event flow (e.g., phase-executor retry
|
|
135
|
+
* paths).
|
|
136
|
+
*/
|
|
137
|
+
appendNotice(message: string): void;
|
|
115
138
|
/** Render the final summary block. */
|
|
116
139
|
renderSummary(input: SummaryRenderInput): void;
|
|
117
140
|
/** Tear down timers, cursor state, signal listeners. */
|
|
@@ -173,6 +196,22 @@ export interface RenderOptions {
|
|
|
173
196
|
* Default: false (matches existing displaySummary behaviour).
|
|
174
197
|
*/
|
|
175
198
|
alwaysRenderSummary?: boolean;
|
|
199
|
+
/**
|
|
200
|
+
* #647: inject a `log-update` instance (typically built via
|
|
201
|
+
* `createLogUpdate(stream)` against a custom stream). Used by the
|
|
202
|
+
* scrollback-harness regression test to drive the real `log-update`
|
|
203
|
+
* through a virtual terminal that tracks scrollback. When set, this takes
|
|
204
|
+
* precedence over both `stdoutWrite` (for the log-update path) and the
|
|
205
|
+
* default `process.stdout`-bound `logUpdate` import.
|
|
206
|
+
*
|
|
207
|
+
* Production code never sets this. Tests that need to assert on
|
|
208
|
+
* `log-update`'s actual erase semantics use it to replace the test stub.
|
|
209
|
+
*/
|
|
210
|
+
logUpdateInstance?: {
|
|
211
|
+
(text: string): void;
|
|
212
|
+
clear(): void;
|
|
213
|
+
done(): void;
|
|
214
|
+
};
|
|
176
215
|
}
|
|
177
216
|
/**
|
|
178
217
|
* Mode the renderer should run in. Auto-detected by `createRunRenderer` from
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
*
|
|
14
14
|
* See issue #618.
|
|
15
15
|
*/
|
|
16
|
+
import type { PhasePauseHandle } from "../workflow/types.js";
|
|
16
17
|
import type { IssueRegistration, IssueState, ProgressEvent, RenderOptions, RendererMode, RunRenderer, SummaryRenderInput } from "./run-renderer-types.js";
|
|
17
18
|
/**
|
|
18
19
|
* #624 Item 4: normalized failure signature for dedup decisions.
|
|
@@ -35,7 +36,7 @@ export declare function failureSignature(error: string | undefined): string;
|
|
|
35
36
|
* (no iteration, iteration === 1, or non-positive).
|
|
36
37
|
*/
|
|
37
38
|
export declare function formatRetrySuffix(iteration: number | undefined, maxIterations: number, kind: "events" | "header"): string;
|
|
38
|
-
declare abstract class BaseRenderer implements RunRenderer {
|
|
39
|
+
declare abstract class BaseRenderer implements RunRenderer, PhasePauseHandle {
|
|
39
40
|
protected readonly issues: Map<number, IssueState>;
|
|
40
41
|
protected readonly stdoutWrite: (s: string) => void;
|
|
41
42
|
protected readonly stderrWrite: (s: string) => void;
|
|
@@ -47,10 +48,18 @@ declare abstract class BaseRenderer implements RunRenderer {
|
|
|
47
48
|
protected disposed: boolean;
|
|
48
49
|
constructor(options: RenderOptions);
|
|
49
50
|
registerIssue(reg: IssueRegistration): void;
|
|
51
|
+
setPhasePlan(issue: number, phases: string[]): void;
|
|
50
52
|
onEvent(event: ProgressEvent): void;
|
|
51
53
|
setPullRequest(issue: number, prNumber: number, prUrl: string): void;
|
|
52
54
|
pause(): void;
|
|
53
55
|
resume(): void;
|
|
56
|
+
/**
|
|
57
|
+
* #647 AC-3: default notice path — just write to the renderer's stdout
|
|
58
|
+
* channel. NonTTYRenderer keeps this default (no live zone to manage).
|
|
59
|
+
* TTYRenderer overrides to clear the live zone before writing so
|
|
60
|
+
* log-update's cursor model stays consistent with the actual terminal.
|
|
61
|
+
*/
|
|
62
|
+
appendNotice(message: string): void;
|
|
54
63
|
abstract renderSummary(input: SummaryRenderInput): void;
|
|
55
64
|
dispose(): void;
|
|
56
65
|
protected applyEvent(state: IssueState, event: ProgressEvent): void;
|
|
@@ -134,6 +143,14 @@ export declare class TTYRenderer extends BaseRenderer {
|
|
|
134
143
|
/**
|
|
135
144
|
* #624 Derived AC-D1: expose the test-only log-update stub. Returns `null`
|
|
136
145
|
* when not in test mode (production renders go through real `log-update`).
|
|
146
|
+
*
|
|
147
|
+
* #647 AC-D3 warning: this stub does NOT model `log-update`'s ANSI cursor
|
|
148
|
+
* or scrollback semantics. Tests that assert on `stub.lastFrame` only see
|
|
149
|
+
* the most recent frame, not whether earlier frames remained stranded in
|
|
150
|
+
* scrollback. Header-count / duplicate-header assertions MUST use
|
|
151
|
+
* `scrollback-harness.ts` (real `createLogUpdate` + VirtualTerminal),
|
|
152
|
+
* otherwise they will pass green even when the production rendering is
|
|
153
|
+
* broken — see #624 for the precedent.
|
|
137
154
|
*/
|
|
138
155
|
getTestStub(): TTYTestStub | null;
|
|
139
156
|
private startLiveTimer;
|
|
@@ -150,6 +167,15 @@ export declare class TTYRenderer extends BaseRenderer {
|
|
|
150
167
|
renderSummary(input: SummaryRenderInput): void;
|
|
151
168
|
protected onPause(): void;
|
|
152
169
|
protected onResume(): void;
|
|
170
|
+
/**
|
|
171
|
+
* #647 AC-3: TTYRenderer override. Writes the notice above the live zone
|
|
172
|
+
* the same way `appendEventLine` does (clear → write → redraw), so
|
|
173
|
+
* log-update's `previousLineCount` stays consistent with the actual
|
|
174
|
+
* terminal state. If the renderer is already paused (e.g., during
|
|
175
|
+
* verbose subprocess streaming), skip the clear/redraw and just write;
|
|
176
|
+
* the eventual `resume()` will redraw cleanly.
|
|
177
|
+
*/
|
|
178
|
+
appendNotice(message: string): void;
|
|
153
179
|
protected onDispose(): void;
|
|
154
180
|
private getColumns;
|
|
155
181
|
/**
|
|
@@ -210,7 +236,13 @@ export declare class TTYRenderer extends BaseRenderer {
|
|
|
210
236
|
private statusCellLines;
|
|
211
237
|
private runHeader;
|
|
212
238
|
private rollupLine;
|
|
213
|
-
|
|
239
|
+
/**
|
|
240
|
+
* Single-issue layout: indented `label value` lines, no box drawing. The
|
|
241
|
+
* label is cyan and padded to `labelW`; continuation lines (multi-line
|
|
242
|
+
* status cells) align under the value column with a blank label. See
|
|
243
|
+
* `renderSingleIssueFrame` for why the bordered grid was dropped.
|
|
244
|
+
*/
|
|
245
|
+
private drawKeyValueLines;
|
|
214
246
|
private drawIssueGrid;
|
|
215
247
|
}
|
|
216
248
|
export interface CreateRendererOptions extends RenderOptions {
|