@tangle-network/agent-app 0.45.27 → 0.45.29
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/{DesignCanvas-3RRPVPCP.js → DesignCanvas-RSWQCXM3.js} +2 -2
- package/dist/{DesignCanvasEditor-KAMMEX7E.js → DesignCanvasEditor-XZMGIST7.js} +4 -4
- package/dist/assistant/index.d.ts +63 -39
- package/dist/assistant/index.js +23 -5
- package/dist/assistant/index.js.map +1 -1
- package/dist/chat-react/index.d.ts +25 -2
- package/dist/chat-react/index.js.map +1 -1
- package/dist/chat-routes/index.js +1 -1
- package/dist/{chunk-ODYE4A7L.js → chunk-3I27SDU3.js} +142 -58
- package/dist/chunk-3I27SDU3.js.map +1 -0
- package/dist/chunk-C3CAYGGQ.js +18 -0
- package/dist/chunk-C3CAYGGQ.js.map +1 -0
- package/dist/chunk-EMLGWEHV.js +33 -0
- package/dist/chunk-EMLGWEHV.js.map +1 -0
- package/dist/{chunk-XEA5EG6P.js → chunk-EPZYYWCT.js} +8 -30
- package/dist/chunk-EPZYYWCT.js.map +1 -0
- package/dist/{chunk-NC2V63EL.js → chunk-FNM7JYME.js} +88 -21
- package/dist/chunk-FNM7JYME.js.map +1 -0
- package/dist/{chunk-WD2B6HGY.js → chunk-K4RA5T3A.js} +11 -32
- package/dist/chunk-K4RA5T3A.js.map +1 -0
- package/dist/{chunk-4QVG4WJQ.js → chunk-OYTMILRK.js} +10 -3
- package/dist/chunk-OYTMILRK.js.map +1 -0
- package/dist/{chunk-OYGM5WLD.js → chunk-PURHUAOR.js} +52 -15
- package/dist/chunk-PURHUAOR.js.map +1 -0
- package/dist/{chunk-AWPK7ZDS.js → chunk-QGOU3VXH.js} +3 -3
- package/dist/{chunk-RT5RKAFX.js → chunk-QGSVF6DZ.js} +8 -18
- package/dist/chunk-QGSVF6DZ.js.map +1 -0
- package/dist/{chunk-OTLEYHOG.js → chunk-RRH23CGS.js} +24 -4
- package/dist/chunk-RRH23CGS.js.map +1 -0
- package/dist/{chunk-N3G3FSM4.js → chunk-YXA3U4JZ.js} +9 -47
- package/dist/chunk-YXA3U4JZ.js.map +1 -0
- package/dist/design-canvas-react/engine.d.ts +15 -1
- package/dist/design-canvas-react/engine.js +6 -2
- package/dist/design-canvas-react/index.d.ts +13 -1
- package/dist/design-canvas-react/index.js +13 -8
- package/dist/design-canvas-react/index.js.map +1 -1
- package/dist/design-canvas-react/lazy.js +1 -1
- package/dist/legibility/cli.js +14 -8
- package/dist/legibility/cli.js.map +1 -1
- package/dist/legibility/index.js +2 -1
- package/dist/peer-floors/cli.js +14 -8
- package/dist/peer-floors/cli.js.map +1 -1
- package/dist/preflight/cli.js +4 -2
- package/dist/preflight/cli.js.map +1 -1
- package/dist/{run-tEsZUhAf.d.ts → run-D80hoLKh.d.ts} +1 -1
- package/dist/sandbox/index.d.ts +11 -1
- package/dist/sandbox/index.js +1 -1
- package/dist/signoff/cli.d.ts +2 -2
- package/dist/signoff/cli.js +6 -18
- package/dist/signoff/cli.js.map +1 -1
- package/dist/signoff/index.d.ts +4 -420
- package/dist/signoff/index.js +3 -55
- package/dist/signoff/proof-cli.js +11 -6
- package/dist/signoff/proof-cli.js.map +1 -1
- package/dist/signoff/proof.d.ts +70 -271
- package/dist/signoff/proof.js +3 -73
- package/dist/signoff/proof.js.map +1 -1
- package/dist/theme-contract/cli.js +6 -2
- package/dist/theme-contract/cli.js.map +1 -1
- package/dist/theme-contract/index.js +2 -1
- package/dist/{types-U7Nz-txa.d.ts → types-Bd6uW9vQ.d.ts} +1 -1
- package/dist/web-react/index.d.ts +51 -44
- package/dist/web-react/index.js +1 -1
- package/package.json +14 -3
- package/dist/chunk-4QVG4WJQ.js.map +0 -1
- package/dist/chunk-N3G3FSM4.js.map +0 -1
- package/dist/chunk-NC2V63EL.js.map +0 -1
- package/dist/chunk-ODYE4A7L.js.map +0 -1
- package/dist/chunk-OTLEYHOG.js.map +0 -1
- package/dist/chunk-OYGM5WLD.js.map +0 -1
- package/dist/chunk-RT5RKAFX.js.map +0 -1
- package/dist/chunk-WD2B6HGY.js.map +0 -1
- package/dist/chunk-XEA5EG6P.js.map +0 -1
- /package/dist/{DesignCanvas-3RRPVPCP.js.map → DesignCanvas-RSWQCXM3.js.map} +0 -0
- /package/dist/{DesignCanvasEditor-KAMMEX7E.js.map → DesignCanvasEditor-XZMGIST7.js.map} +0 -0
- /package/dist/{chunk-AWPK7ZDS.js.map → chunk-QGOU3VXH.js.map} +0 -0
package/dist/signoff/index.d.ts
CHANGED
|
@@ -1,32 +1,7 @@
|
|
|
1
|
-
export { R as RunSignoffOptions, r as runSignoff } from '../run-
|
|
2
|
-
import {
|
|
3
|
-
export {
|
|
1
|
+
export { R as RunSignoffOptions, r as runSignoff } from '../run-D80hoLKh.js';
|
|
2
|
+
import { L as LoadedSignoffConfig, b as SignoffReport } from '../types-Bd6uW9vQ.js';
|
|
3
|
+
export { c as SignoffAttempt, d as SignoffConfig, e as SignoffConfigOrigin, a as SignoffEvent, f as SignoffHostFacts, g as SignoffInstallResult, h as SignoffInstallSpec, i as SignoffRepoFacts, j as SignoffShuffleSpec, S as SignoffSource, k as SignoffStepResult, l as SignoffStepSpec, m as SignoffStepStatus } from '../types-Bd6uW9vQ.js';
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
* Where the step list comes from.
|
|
7
|
-
*
|
|
8
|
-
* It is declared by the repo, never hardcoded here, because the three workflows
|
|
9
|
-
* this has to reproduce do not share a shape: tax-agent filters its install to
|
|
10
|
-
* one workspace package and runs a Python toolkit suite; legal-agent runs a
|
|
11
|
-
* react-router typegen and a Worker startup check; agent-app runs a named
|
|
12
|
-
* incident-class gate, a generated-project test and knip. A checker that
|
|
13
|
-
* hardcodes any one of those is wrong for the other two.
|
|
14
|
-
*
|
|
15
|
-
* Three sources, in order:
|
|
16
|
-
* 1. `signoff.config.mjs` (or `.js`) — the full surface, including per-step
|
|
17
|
-
* `env`, `needs` and shuffle specs.
|
|
18
|
-
* 2. a `signoff` key in `package.json` — the same shape, JSON-only.
|
|
19
|
-
* 3. derived from the repo's own `scripts`, so a repo gets a real gate before
|
|
20
|
-
* anyone writes a config. The derivation is documented below and its origin
|
|
21
|
-
* is stamped into the proof, because a derived run is a weaker claim than a
|
|
22
|
-
* declared one.
|
|
23
|
-
*/
|
|
24
|
-
declare const SIGNOFF_CONFIG_FILES: readonly string[];
|
|
25
|
-
declare function parseSignoffConfig(value: unknown, where: string): SignoffConfig;
|
|
26
|
-
declare function deriveSignoffConfig(scripts: Readonly<Record<string, string>>): {
|
|
27
|
-
readonly config: SignoffConfig;
|
|
28
|
-
readonly used: readonly string[];
|
|
29
|
-
};
|
|
30
5
|
interface LoadSignoffConfigOptions {
|
|
31
6
|
readonly repoRoot: string;
|
|
32
7
|
/** Explicit config path. Missing file is an error, never a silent fallback. */
|
|
@@ -34,397 +9,6 @@ interface LoadSignoffConfigOptions {
|
|
|
34
9
|
}
|
|
35
10
|
declare function loadSignoffConfig(options: LoadSignoffConfigOptions): Promise<LoadedSignoffConfig>;
|
|
36
11
|
|
|
37
|
-
/** Concurrency actually achieved, measured from the step windows rather than
|
|
38
|
-
* asserted from the config. A claimed speedup nobody measured is a wish. */
|
|
39
|
-
declare function peakConcurrency(steps: readonly SignoffStepResult[]): number;
|
|
40
12
|
declare function formatSignoffReport(report: SignoffReport): string;
|
|
41
|
-
/** One line for a commit message, a PR comment, or a chat handoff. */
|
|
42
|
-
declare function formatSignoffLine(report: SignoffReport): string;
|
|
43
13
|
|
|
44
|
-
|
|
45
|
-
* Refuse to sign off on a runtime the product does not ship.
|
|
46
|
-
*
|
|
47
|
-
* CI pins its Node version (`actions/setup-node` with `node-version: 22` in all
|
|
48
|
-
* three fleet workflows). A developer's shell does not — this host runs 24. A
|
|
49
|
-
* local gate that inherits whatever is on `PATH` therefore verifies a runtime
|
|
50
|
-
* nobody deploys, and reports it as the runtime that ships. That is the same
|
|
51
|
-
* failure shape as a number measured in a narrower context than it is
|
|
52
|
-
* presented in, and it is not acceptable in the thing that replaces the merge
|
|
53
|
-
* gate.
|
|
54
|
-
*
|
|
55
|
-
* So the pin is read from the repo and enforced, or it is absent and the report
|
|
56
|
-
* says so. Three sources, in order:
|
|
57
|
-
*
|
|
58
|
-
* - `signoff.config.mjs`'s `nodeVersion` — the explicit declaration.
|
|
59
|
-
* - `.nvmrc` — the pin a repo already keeps for humans.
|
|
60
|
-
* - the `pull_request` workflows' `node-version` (`./workflow-pin`) — the pin
|
|
61
|
-
* CI itself runs on.
|
|
62
|
-
*
|
|
63
|
-
* **The third source was added because the first two were empty on the entire
|
|
64
|
-
* fleet, and that produced a false PASS.** legal-agent `4c0d688` failed CI on
|
|
65
|
-
* `Cannot bundle Node.js built-in "node:sqlite"` and this gate signed it off.
|
|
66
|
-
* Reproduced in one installed tree, same bytes: Node 22 fails both files, Node
|
|
67
|
-
* 24 passes both, every time. No `.nvmrc` exists in tax-agent, legal-agent or
|
|
68
|
-
* agent-app; all three pin `node-version: 22` in the workflow being replaced.
|
|
69
|
-
* A gate that replaces a workflow has to read the runtime that workflow pins.
|
|
70
|
-
*
|
|
71
|
-
* A `.nvmrc` and a workflow that disagree is not resolved by preference — it is
|
|
72
|
-
* a refusal, because it means the local gate and CI verify different runtimes,
|
|
73
|
-
* which is the exact defect this module exists to prevent.
|
|
74
|
-
*
|
|
75
|
-
* **`engines.node` is deliberately NOT read.** It is a floor (`">=18"`), not a
|
|
76
|
-
* pin, so treating it as one manufactures refusals on every version above the
|
|
77
|
-
* floor — and an unsatisfiable gate does not stop bad work, it gets waived.
|
|
78
|
-
*/
|
|
79
|
-
interface NodeVersionRequirement {
|
|
80
|
-
/** The declared major, e.g. `22`. */
|
|
81
|
-
readonly major: number;
|
|
82
|
-
readonly declared: string;
|
|
83
|
-
/** Where it came from, for the proof. */
|
|
84
|
-
readonly source: string;
|
|
85
|
-
}
|
|
86
|
-
declare function resolveNodeRequirement(repoRoot: string, configured?: string): NodeVersionRequirement | null;
|
|
87
|
-
/**
|
|
88
|
-
* Throw when the running Node cannot stand in for the declared one.
|
|
89
|
-
*
|
|
90
|
-
* Major-only: a patch difference is not a different runtime, and demanding an
|
|
91
|
-
* exact patch would refuse every host that has not just re-installed.
|
|
92
|
-
*/
|
|
93
|
-
declare function assertNodeVersion(requirement: NodeVersionRequirement | null, running?: string): void;
|
|
94
|
-
|
|
95
|
-
/**
|
|
96
|
-
* The dependency graph, and running it as wide as the graph allows.
|
|
97
|
-
*
|
|
98
|
-
* CI runs these steps in a line because a YAML `steps:` list is a line. Nothing
|
|
99
|
-
* about the work requires that: typecheck, the suite, the build and knip all
|
|
100
|
-
* read source and none reads another's output. On a 32-core host, running them
|
|
101
|
-
* concurrently is free wall-clock, and it is the only reason a strictly larger
|
|
102
|
-
* verification can still finish faster than the CI it replaces.
|
|
103
|
-
*
|
|
104
|
-
* The graph is declared per step (`needs`), validated here, and the schedule is
|
|
105
|
-
* derived from it — so an omitted edge is a correctness bug the repo owns, and
|
|
106
|
-
* a cycle is named rather than deadlocking.
|
|
107
|
-
*/
|
|
108
|
-
interface GraphNode {
|
|
109
|
-
readonly name: string;
|
|
110
|
-
readonly needs?: readonly string[];
|
|
111
|
-
}
|
|
112
|
-
/** Throws on a duplicate name, a dangling `needs`, or a cycle. */
|
|
113
|
-
declare function validateGraph(nodes: readonly GraphNode[]): void;
|
|
114
|
-
type TaskStatus = 'passed' | 'failed' | 'skipped' | 'cancelled' | 'blocked';
|
|
115
|
-
interface TaskOutcome<T> {
|
|
116
|
-
readonly name: string;
|
|
117
|
-
readonly status: TaskStatus;
|
|
118
|
-
/** Present for anything that actually ran, including a cancelled task. */
|
|
119
|
-
readonly value: T | null;
|
|
120
|
-
readonly startedAtMs: number | null;
|
|
121
|
-
readonly finishedAtMs: number | null;
|
|
122
|
-
}
|
|
123
|
-
interface RunGraphOptions<TNode extends GraphNode, TValue> {
|
|
124
|
-
readonly nodes: readonly TNode[];
|
|
125
|
-
readonly maxParallel: number;
|
|
126
|
-
/** `false` stops scheduling after the first failure and kills what is running. */
|
|
127
|
-
readonly keepGoing: boolean;
|
|
128
|
-
/** Runs one node. Resolves `{ ok }` for pass/fail; must not throw for a
|
|
129
|
-
* failing command — a non-zero exit is data. */
|
|
130
|
-
readonly run: (node: TNode, signal: AbortSignal) => Promise<{
|
|
131
|
-
readonly ok: boolean;
|
|
132
|
-
readonly value: TValue;
|
|
133
|
-
}>;
|
|
134
|
-
/** Wall-clock origin, so outcomes are comparable across the whole run. */
|
|
135
|
-
readonly now?: () => number;
|
|
136
|
-
}
|
|
137
|
-
/**
|
|
138
|
-
* Run the graph, honouring dependencies and the parallelism cap.
|
|
139
|
-
*
|
|
140
|
-
* Failure semantics, stated because they are the difference between a gate you
|
|
141
|
-
* trust and one you argue with:
|
|
142
|
-
* - fail-fast (default): nothing new is scheduled, everything in flight is
|
|
143
|
-
* aborted and reported as `cancelled`, everything unstarted as `skipped`.
|
|
144
|
-
* - `keepGoing`: independent work continues, but a step whose dependency
|
|
145
|
-
* failed is reported `blocked`. It is never reported as passed, and never
|
|
146
|
-
* silently omitted.
|
|
147
|
-
*/
|
|
148
|
-
declare function runGraph<TNode extends GraphNode, TValue>(options: RunGraphOptions<TNode, TValue>): Promise<TaskOutcome<TValue>[]>;
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Suite-order randomization — the half of this gate that CI does not have.
|
|
152
|
-
*
|
|
153
|
-
* CI runs one arbitrary file order, so a scheduling-dependent failure is a coin
|
|
154
|
-
* flip it happens to win or lose. The `node:sqlite` bundling failure that
|
|
155
|
-
* started this was exactly that shape: it reproduced under CI's clean install
|
|
156
|
-
* and worker sharding and not under a warm local run, and a single fixed order
|
|
157
|
-
* could have missed it in either direction.
|
|
158
|
-
*
|
|
159
|
-
* Two rules make randomization useful rather than merely noisy:
|
|
160
|
-
*
|
|
161
|
-
* 1. **Every seed is recorded.** A shuffled failure that cannot be replayed is
|
|
162
|
-
* a rumour. The report carries the base seed and every derived seed, and the
|
|
163
|
-
* derivation is a pure function of `(base, step, index)` — so `--seed <base>`
|
|
164
|
-
* reproduces the whole run, and a single seed replays one step.
|
|
165
|
-
* 2. **Files are shuffled; tests inside a file are not.** File order is what
|
|
166
|
-
* module-graph and worker-scheduling failures depend on. Shuffling the tests
|
|
167
|
-
* within a file mostly finds intentional ordering in a `describe` block,
|
|
168
|
-
* which is a false alarm, and a gate that cries wolf gets waived.
|
|
169
|
-
*/
|
|
170
|
-
/**
|
|
171
|
-
* Vitest file-order shuffle. `{seed}` is substituted per attempt.
|
|
172
|
-
*
|
|
173
|
-
* **No `--` separator, and that is measured, not assumed.** The habit is to
|
|
174
|
-
* write `pnpm run test -- --flag`, and it silently breaks this: pnpm forwards
|
|
175
|
-
* script arguments verbatim without needing the separator, so the `--` reaches
|
|
176
|
-
* vitest, whose CLI treats it as end-of-options and drops everything after it.
|
|
177
|
-
* Measured on this repo, four test files, `--reporter=verbose`:
|
|
178
|
-
*
|
|
179
|
-
* | invocation | seed 1 order | seed 2 order |
|
|
180
|
-
* |---|---|---|
|
|
181
|
-
* | `vitest run -- --sequence.shuffle.files=true --sequence.seed=N` | schedule, store, seeds, config | schedule, store, seeds, config |
|
|
182
|
-
* | `vitest run --sequence.shuffle.files=true --sequence.seed=N` | schedule, config, store, seeds | seeds, schedule, config, store |
|
|
183
|
-
*
|
|
184
|
-
* The first row is the failure this gate exists to prevent, applied to itself:
|
|
185
|
-
* a run that reports "2 orders, seeds recorded" while running one fixed order
|
|
186
|
-
* twice. If a runner ever does need a separator, its config declares its own
|
|
187
|
-
* `shuffle.args`.
|
|
188
|
-
*/
|
|
189
|
-
declare const DEFAULT_SHUFFLE_ARGS: readonly string[];
|
|
190
|
-
declare const DEFAULT_SHUFFLE_RUNS = 2;
|
|
191
|
-
/** A fresh base seed. Random, then recorded — never a fixed constant, or the
|
|
192
|
-
* "randomized" order is one more fixed order. */
|
|
193
|
-
declare function newSeedBase(): number;
|
|
194
|
-
/**
|
|
195
|
-
* Derive a step's Nth seed from the base.
|
|
196
|
-
*
|
|
197
|
-
* A hash rather than `base + n`: adjacent seeds produce correlated orders in
|
|
198
|
-
* some runners, and the point is independent samples of the order space.
|
|
199
|
-
*/
|
|
200
|
-
declare function deriveSeed(base: number, stepName: string, index: number): number;
|
|
201
|
-
/**
|
|
202
|
-
* Refuse a shuffled step whose command cannot receive the appended flags.
|
|
203
|
-
*
|
|
204
|
-
* pnpm only forwards extra arguments to a script through `run`, `exec` or
|
|
205
|
-
* `dlx`. The shorthand form puts pnpm's own option parser in front of them, and
|
|
206
|
-
* what happens next is a version lottery. Measured on this host, appending
|
|
207
|
-
* `--sequence.shuffle.files=true --sequence.seed=7` to a script that prints its
|
|
208
|
-
* `process.argv`:
|
|
209
|
-
*
|
|
210
|
-
* | command | pnpm 9.15.9 | pnpm 10.22.0 |
|
|
211
|
-
* |---|---|---|
|
|
212
|
-
* | `pnpm run t <flags>` | forwarded, exit 0 | forwarded, exit 0 |
|
|
213
|
-
* | `pnpm exec node probe.mjs <flags>` | forwarded, exit 0 | forwarded, exit 0 |
|
|
214
|
-
* | `pnpm t <flags>` | `Unknown options`, exit 1 | exit 254, script never ran |
|
|
215
|
-
* | `pnpm --filter web t <flags>` | `Unknown options`, exit 1 | **exit 0, script never ran** |
|
|
216
|
-
*
|
|
217
|
-
* That last cell is the reason this is a refusal and not a note. tax-agent's CI
|
|
218
|
-
* runs `pnpm --filter web test`, and a config that copied the line verbatim
|
|
219
|
-
* would, on pnpm 10, report a green "unit tests" step that executed zero tests.
|
|
220
|
-
* A gate reporting safety it did not provide is the exact failure this module
|
|
221
|
-
* exists to prevent, and the shorthand makes it silent.
|
|
222
|
-
*
|
|
223
|
-
* This is a static approximation — it checks the invocation SHAPE, not what the
|
|
224
|
-
* runner received — so it is deliberately narrow: it fires only on commands
|
|
225
|
-
* that invoke pnpm, and only on steps that get arguments appended.
|
|
226
|
-
*/
|
|
227
|
-
declare function assertShuffleArgsReachTheRunner(steps: readonly SignoffStepSpec[]): void;
|
|
228
|
-
interface StepAttemptPlan {
|
|
229
|
-
readonly command: string;
|
|
230
|
-
readonly seed: number | null;
|
|
231
|
-
}
|
|
232
|
-
/**
|
|
233
|
-
* Expand one step into the commands that will actually run.
|
|
234
|
-
*
|
|
235
|
-
* An unshuffled step is one attempt with no seed. A shuffled step is one
|
|
236
|
-
* attempt per seed, each with the seed substituted into the appended arguments.
|
|
237
|
-
*/
|
|
238
|
-
declare function planAttempts(step: SignoffStepSpec, seedBase: number, overrideRuns?: number): StepAttemptPlan[];
|
|
239
|
-
|
|
240
|
-
/**
|
|
241
|
-
* Read the Node pin out of the workflows that gate a merge.
|
|
242
|
-
*
|
|
243
|
-
* This exists because of a measured false PASS. legal-agent `4c0d688` was red
|
|
244
|
-
* in CI on two test files — `Cannot bundle Node.js built-in "node:sqlite"` —
|
|
245
|
-
* and the sign-off gate signed it off. Same bytes, same lockfile, same
|
|
246
|
-
* hermetic tree: on Node 22 both files fail, on Node 24 both pass, 100% of the
|
|
247
|
-
* time. The runtime was the whole difference, and the gate never saw it because
|
|
248
|
-
* it looked for a pin in exactly two places the fleet does not keep one.
|
|
249
|
-
*
|
|
250
|
-
* None of tax-agent, legal-agent or agent-app has a `.nvmrc`. All three pin
|
|
251
|
-
* `node-version: 22` in the workflow that IS the merge gate. So the pin the
|
|
252
|
-
* gate is replacing was sitting in the file it is replacing, unread.
|
|
253
|
-
*
|
|
254
|
-
* Scope is deliberate: only workflows that trigger on `pull_request`. Those are
|
|
255
|
-
* the merge gate. agent-app's `publish.yml` runs Node 24.18.0 for its release
|
|
256
|
-
* jobs and triggers on push, and reading it would put the gate in permanent
|
|
257
|
-
* conflict with `ci.yml`'s Node 22.
|
|
258
|
-
*
|
|
259
|
-
* Nothing here guesses. An expression (`${{ matrix.node }}`) is not a version,
|
|
260
|
-
* a `node-version-file` that does not exist is not a pin, and two merge-gate
|
|
261
|
-
* workflows on different majors is a question this module cannot answer — each
|
|
262
|
-
* one refuses and names the config key that settles it.
|
|
263
|
-
*/
|
|
264
|
-
interface WorkflowNodePin {
|
|
265
|
-
/** Repo-relative workflow path, for the proof. */
|
|
266
|
-
readonly file: string;
|
|
267
|
-
/** The version as written: `22`, `24.18.0`, `v20`. */
|
|
268
|
-
readonly value: string;
|
|
269
|
-
/** `node-version`, or the `node-version-file` path it was read through. */
|
|
270
|
-
readonly via: string;
|
|
271
|
-
}
|
|
272
|
-
/**
|
|
273
|
-
* Does this workflow run on `pull_request`?
|
|
274
|
-
*
|
|
275
|
-
* Both spellings the fleet uses are handled: the block form every workflow here
|
|
276
|
-
* writes, and the inline form (`on: [push, pull_request]`). `on` is quoted in
|
|
277
|
-
* some repos because YAML 1.1 reads a bare `on` as the boolean true, so the
|
|
278
|
-
* quoted keys are matched too.
|
|
279
|
-
*/
|
|
280
|
-
declare function triggersOnPullRequest(source: string): boolean;
|
|
281
|
-
/** Node pins declared by every workflow that gates a merge, in filename order. */
|
|
282
|
-
declare function scanMergeGateNodePins(repoRoot: string): WorkflowNodePin[];
|
|
283
|
-
interface ResolvedWorkflowPin {
|
|
284
|
-
readonly major: number;
|
|
285
|
-
readonly declared: string;
|
|
286
|
-
readonly source: string;
|
|
287
|
-
}
|
|
288
|
-
/**
|
|
289
|
-
* One Node major for the whole merge gate, or a refusal that names why not.
|
|
290
|
-
*
|
|
291
|
-
* `majorOf` is injected rather than imported to keep the direction of the
|
|
292
|
-
* dependency one-way: `node-version.ts` owns what a version string means, this
|
|
293
|
-
* module owns where the string lives.
|
|
294
|
-
*/
|
|
295
|
-
declare function resolveWorkflowNodePin(repoRoot: string, majorOf: (raw: string) => number | null): ResolvedWorkflowPin | null;
|
|
296
|
-
|
|
297
|
-
/**
|
|
298
|
-
* The pristine package store, cached on the lockfile so speed is not paid for
|
|
299
|
-
* with a stale module graph.
|
|
300
|
-
*
|
|
301
|
-
* The whole mechanical difference between "green locally" and "red in CI" is
|
|
302
|
-
* that CI installs `--frozen-lockfile` into an isolated store with no
|
|
303
|
-
* `node_modules` and no framework cache, and a local run reuses both. Only one
|
|
304
|
-
* of those two is safe to keep:
|
|
305
|
-
*
|
|
306
|
-
* - `node_modules` and any build/framework cache are **never** reused. They are
|
|
307
|
-
* what masked the failure this gate exists to catch, and they are recreated
|
|
308
|
-
* for every run inside a fresh `git worktree`.
|
|
309
|
-
* - The **store** is reused, keyed on the bytes that decide what gets installed.
|
|
310
|
-
* A pnpm store is content-addressed: every entry is named by the hash of what
|
|
311
|
-
* is in it, so a reused entry cannot be a different version of a package than
|
|
312
|
-
* the lockfile asked for. Reusing it skips downloads, not resolution.
|
|
313
|
-
*
|
|
314
|
-
* The key covers every manifest in the tree — lockfile, workspace file, every
|
|
315
|
-
* `package.json`, `.npmrc`. Change any of them and the key changes, so the next
|
|
316
|
-
* run installs into an empty store and pays the honest cold cost. Generations
|
|
317
|
-
* are kept (default 4) rather than one, so moving between a branch and `main`
|
|
318
|
-
* finds both warm instead of thrashing.
|
|
319
|
-
*/
|
|
320
|
-
/** Basenames whose bytes decide what an install resolves to. */
|
|
321
|
-
declare const MANIFEST_FILES: readonly string[];
|
|
322
|
-
interface StoreResolution {
|
|
323
|
-
readonly storeDir: string;
|
|
324
|
-
readonly cacheKey: string;
|
|
325
|
-
/** True when a store for this key already had content. */
|
|
326
|
-
readonly hit: boolean;
|
|
327
|
-
/** Repo-relative manifest paths, sorted — the inputs to `cacheKey`. */
|
|
328
|
-
readonly keyedOn: readonly string[];
|
|
329
|
-
/** Store directories removed by the generation cap. */
|
|
330
|
-
readonly pruned: readonly string[];
|
|
331
|
-
}
|
|
332
|
-
interface ResolveStoreOptions {
|
|
333
|
-
/** The clean tree whose manifests are hashed. */
|
|
334
|
-
readonly treePath: string;
|
|
335
|
-
readonly cacheDir: string;
|
|
336
|
-
/** Generations to keep. Default 4. */
|
|
337
|
-
readonly generations?: number;
|
|
338
|
-
}
|
|
339
|
-
/** Every manifest in the tree, repo-relative and sorted. */
|
|
340
|
-
declare function manifestFiles(treePath: string): string[];
|
|
341
|
-
/** sha256 over each manifest's path and content — order-independent by sorting. */
|
|
342
|
-
declare function manifestCacheKey(treePath: string, files: readonly string[]): string;
|
|
343
|
-
declare function resolveStore(options: ResolveStoreOptions): StoreResolution;
|
|
344
|
-
|
|
345
|
-
/**
|
|
346
|
-
* Materialize the pristine checkout every step runs against.
|
|
347
|
-
*
|
|
348
|
-
* **The decision, and the measurement behind it: a `git worktree` of HEAD, not
|
|
349
|
-
* a filtered copy.** Both were timed on two real repos on this host:
|
|
350
|
-
*
|
|
351
|
-
* | repo | `git worktree add --detach` | `rsync -a --exclude node_modules --exclude .git` |
|
|
352
|
-
* |---|---|---|
|
|
353
|
-
* | agent-app (931 tracked files) | **0.04 s / 11 MB** | 0.12 s / 25 MB |
|
|
354
|
-
* | legal-agent (753 tracked files) | **0.06 s / 24 MB** | **3.97 s / 2.7 GB** |
|
|
355
|
-
*
|
|
356
|
-
* The 2.7 GB is the argument. The copy carried `build/`, `.react-router/` and
|
|
357
|
-
* `.wrangler/` — generated output and framework caches — because an exclude
|
|
358
|
-
* list is a hand-maintained enumeration of things to leave behind, and it is
|
|
359
|
-
* never complete. A warm Vite cache is precisely what made the `node:sqlite`
|
|
360
|
-
* bundling failure invisible locally while CI saw it, so a materializer that
|
|
361
|
-
* can leak one has defeated its own purpose. `git` already knows what is source
|
|
362
|
-
* and what is generated, and `.gitignore` is that list, maintained by the repo.
|
|
363
|
-
*
|
|
364
|
-
* The overlay on top is what keeps the gate usable before you commit:
|
|
365
|
-
* `source: 'working-tree'` applies `git diff HEAD` (staged and unstaged) as a
|
|
366
|
-
* patch and copies untracked, non-ignored files in. `source: 'head'` verifies
|
|
367
|
-
* exactly the commit that would merge.
|
|
368
|
-
*/
|
|
369
|
-
interface MaterializeOptions {
|
|
370
|
-
readonly repoDir: string;
|
|
371
|
-
readonly dest: string;
|
|
372
|
-
readonly source: SignoffSource;
|
|
373
|
-
/** Gitignored files the run genuinely needs. Missing ones abort. */
|
|
374
|
-
readonly carryFiles?: readonly string[];
|
|
375
|
-
}
|
|
376
|
-
interface CleanTree extends SignoffRepoFacts {
|
|
377
|
-
/** Absolute path of the materialized checkout. */
|
|
378
|
-
readonly path: string;
|
|
379
|
-
}
|
|
380
|
-
declare function repoRootOf(dir: string): string;
|
|
381
|
-
declare function materializeCleanTree(options: MaterializeOptions): CleanTree;
|
|
382
|
-
/** Unregister and delete a materialized tree. */
|
|
383
|
-
declare function removeCleanTree(tree: CleanTree): void;
|
|
384
|
-
|
|
385
|
-
/**
|
|
386
|
-
* The subprocess primitive every sign-off step runs through.
|
|
387
|
-
*
|
|
388
|
-
* Three properties the gate depends on, none of which `execSync` gives:
|
|
389
|
-
*
|
|
390
|
-
* 1. **Cancellable.** Under fail-fast, a build still running when typecheck
|
|
391
|
-
* fails is killed rather than waited out — otherwise the "faster than CI"
|
|
392
|
-
* claim is spent waiting for work whose verdict no longer matters.
|
|
393
|
-
* 2. **Process-group kill.** Steps run through `sh -c`, so killing the shell
|
|
394
|
-
* leaves `vitest`'s forks and `tsup`'s dts worker orphaned and holding CPU.
|
|
395
|
-
* The child is spawned as a group leader and the whole group is signalled.
|
|
396
|
-
* 3. **Bounded, non-silent capture.** Output is capped, and when the cap is hit
|
|
397
|
-
* the elision is stated in the captured text. A gate that quietly drops the
|
|
398
|
-
* middle of a failure log is a gate that hides the failure.
|
|
399
|
-
*/
|
|
400
|
-
interface CommandResult {
|
|
401
|
-
readonly command: string;
|
|
402
|
-
readonly cwd: string;
|
|
403
|
-
readonly exitCode: number;
|
|
404
|
-
readonly signal: string | null;
|
|
405
|
-
readonly durationMs: number;
|
|
406
|
-
readonly output: string;
|
|
407
|
-
readonly truncated: boolean;
|
|
408
|
-
readonly timedOut: boolean;
|
|
409
|
-
}
|
|
410
|
-
interface RunCommandOptions {
|
|
411
|
-
readonly command: string;
|
|
412
|
-
readonly cwd: string;
|
|
413
|
-
readonly env?: Readonly<Record<string, string | undefined>>;
|
|
414
|
-
readonly timeoutMs?: number;
|
|
415
|
-
readonly signal?: AbortSignal;
|
|
416
|
-
/** Retained bytes before elision. Default 2 MiB. */
|
|
417
|
-
readonly maxOutputBytes?: number;
|
|
418
|
-
/** Grace between SIGTERM and SIGKILL. Default 5 s. */
|
|
419
|
-
readonly killGraceMs?: number;
|
|
420
|
-
readonly onData?: (chunk: string) => void;
|
|
421
|
-
}
|
|
422
|
-
/**
|
|
423
|
-
* Run one shell command to completion and report what happened.
|
|
424
|
-
*
|
|
425
|
-
* Never throws on a non-zero exit — a failing step is data, not an exception.
|
|
426
|
-
* It throws only when the process could not be started at all.
|
|
427
|
-
*/
|
|
428
|
-
declare function runCommand(options: RunCommandOptions): Promise<CommandResult>;
|
|
429
|
-
|
|
430
|
-
export { type CleanTree, type CommandResult, DEFAULT_SHUFFLE_ARGS, DEFAULT_SHUFFLE_RUNS, type GraphNode, type LoadSignoffConfigOptions, LoadedSignoffConfig, MANIFEST_FILES, type MaterializeOptions, type NodeVersionRequirement, type ResolveStoreOptions, type ResolvedWorkflowPin, type RunCommandOptions, type RunGraphOptions, SIGNOFF_CONFIG_FILES, SignoffConfig, SignoffRepoFacts, SignoffReport, SignoffSource, SignoffStepResult, SignoffStepSpec, type StepAttemptPlan, type StoreResolution, type TaskOutcome, type TaskStatus, type WorkflowNodePin, assertNodeVersion, assertShuffleArgsReachTheRunner, deriveSeed, deriveSignoffConfig, formatSignoffLine, formatSignoffReport, loadSignoffConfig, manifestCacheKey, manifestFiles, materializeCleanTree, newSeedBase, parseSignoffConfig, peakConcurrency, planAttempts, removeCleanTree, repoRootOf, resolveNodeRequirement, resolveStore, resolveWorkflowNodePin, runCommand, runGraph, scanMergeGateNodePins, triggersOnPullRequest, validateGraph };
|
|
14
|
+
export { type LoadSignoffConfigOptions, LoadedSignoffConfig, SignoffReport, formatSignoffReport, loadSignoffConfig };
|
package/dist/signoff/index.js
CHANGED
|
@@ -1,63 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
-
DEFAULT_SHUFFLE_ARGS,
|
|
3
|
-
DEFAULT_SHUFFLE_RUNS,
|
|
4
|
-
MANIFEST_FILES,
|
|
5
|
-
SIGNOFF_CONFIG_FILES,
|
|
6
|
-
assertNodeVersion,
|
|
7
|
-
assertShuffleArgsReachTheRunner,
|
|
8
|
-
deriveSeed,
|
|
9
|
-
deriveSignoffConfig,
|
|
10
|
-
formatSignoffLine,
|
|
11
2
|
formatSignoffReport,
|
|
12
3
|
loadSignoffConfig,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
materializeCleanTree,
|
|
16
|
-
newSeedBase,
|
|
17
|
-
parseSignoffConfig,
|
|
18
|
-
peakConcurrency,
|
|
19
|
-
planAttempts,
|
|
20
|
-
removeCleanTree,
|
|
21
|
-
repoRootOf,
|
|
22
|
-
resolveNodeRequirement,
|
|
23
|
-
resolveStore,
|
|
24
|
-
resolveWorkflowNodePin,
|
|
25
|
-
runCommand,
|
|
26
|
-
runGraph,
|
|
27
|
-
runSignoff,
|
|
28
|
-
scanMergeGateNodePins,
|
|
29
|
-
triggersOnPullRequest,
|
|
30
|
-
validateGraph
|
|
31
|
-
} from "../chunk-WD2B6HGY.js";
|
|
4
|
+
runSignoff
|
|
5
|
+
} from "../chunk-K4RA5T3A.js";
|
|
32
6
|
export {
|
|
33
|
-
DEFAULT_SHUFFLE_ARGS,
|
|
34
|
-
DEFAULT_SHUFFLE_RUNS,
|
|
35
|
-
MANIFEST_FILES,
|
|
36
|
-
SIGNOFF_CONFIG_FILES,
|
|
37
|
-
assertNodeVersion,
|
|
38
|
-
assertShuffleArgsReachTheRunner,
|
|
39
|
-
deriveSeed,
|
|
40
|
-
deriveSignoffConfig,
|
|
41
|
-
formatSignoffLine,
|
|
42
7
|
formatSignoffReport,
|
|
43
8
|
loadSignoffConfig,
|
|
44
|
-
|
|
45
|
-
manifestFiles,
|
|
46
|
-
materializeCleanTree,
|
|
47
|
-
newSeedBase,
|
|
48
|
-
parseSignoffConfig,
|
|
49
|
-
peakConcurrency,
|
|
50
|
-
planAttempts,
|
|
51
|
-
removeCleanTree,
|
|
52
|
-
repoRootOf,
|
|
53
|
-
resolveNodeRequirement,
|
|
54
|
-
resolveStore,
|
|
55
|
-
resolveWorkflowNodePin,
|
|
56
|
-
runCommand,
|
|
57
|
-
runGraph,
|
|
58
|
-
runSignoff,
|
|
59
|
-
scanMergeGateNodePins,
|
|
60
|
-
triggersOnPullRequest,
|
|
61
|
-
validateGraph
|
|
9
|
+
runSignoff
|
|
62
10
|
};
|
|
63
11
|
//# sourceMappingURL=index.js.map
|
|
@@ -5,7 +5,10 @@ import {
|
|
|
5
5
|
readSignoffKey,
|
|
6
6
|
verifySignoffAtRev,
|
|
7
7
|
verifySignoffProofFile
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-YXA3U4JZ.js";
|
|
9
|
+
import {
|
|
10
|
+
invokedAsScript
|
|
11
|
+
} from "../chunk-C3CAYGGQ.js";
|
|
9
12
|
|
|
10
13
|
// src/signoff/proof-cli.ts
|
|
11
14
|
function parseArgs(argv) {
|
|
@@ -82,11 +85,13 @@ ${outcome.hint.map((line) => ` ${line}`).join("\n")}
|
|
|
82
85
|
}
|
|
83
86
|
return report(outcome, options);
|
|
84
87
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
88
|
+
if (invokedAsScript(import.meta.url, process.argv[1])) {
|
|
89
|
+
try {
|
|
90
|
+
process.exit(main());
|
|
91
|
+
} catch (error) {
|
|
92
|
+
process.stderr.write(`agent-app-verify-proof failed: ${error instanceof Error ? error.message : String(error)}
|
|
89
93
|
`);
|
|
90
|
-
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
91
96
|
}
|
|
92
97
|
//# sourceMappingURL=proof-cli.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/signoff/proof-cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * `agent-app-verify-proof` — the command a reader runs to decide whether a\n * commit was signed off, by what, and whether it passed.\n *\n * agent-app-verify-proof <sha|rev> # look the proof up by commit\n * agent-app-verify-proof --file proof.json # check a proof document\n *\n * Exits 0 only when every check passes. Exits 1 on any failure, and on a commit\n * with no proof at all — \"nobody signed this off\" is a rejection, not a pass.\n */\nimport { formatSignoffSummary, readSignoffKey } from './proof-record'\nimport { formatSignoffVerification, verifySignoffAtRev, verifySignoffProofFile, type SignoffVerification } from './proof-verify'\n\ninterface CliOptions {\n readonly rev: string\n readonly file: string | null\n readonly dir: string\n readonly keyPath: string | null\n readonly repo: string | null\n readonly json: boolean\n}\n\nfunction parseArgs(argv: readonly string[]): CliOptions {\n let rev = 'HEAD'\n let file: string | null = null\n let dir = process.cwd()\n let keyPath: string | null = null\n let repo: string | null = null\n let json = false\n let sawRev = false\n\n for (let index = 0; index < argv.length; index += 1) {\n const arg = argv[index] as string\n const next = (): string => {\n const value = argv[index + 1]\n if (value === undefined) throw new Error(`${arg} requires a value`)\n index += 1\n return value\n }\n if (arg === '--file') file = next()\n else if (arg === '--dir') dir = next()\n else if (arg === '--key') keyPath = next()\n else if (arg === '--repo') repo = next()\n else if (arg === '--json') json = true\n else if (arg.startsWith('-')) throw new Error(`unknown flag ${arg}`)\n else {\n if (sawRev) throw new Error(`unexpected second revision ${arg}`)\n rev = arg\n sawRev = true\n }\n }\n return { rev, file, dir, keyPath, repo, json }\n}\n\nfunction report(result: SignoffVerification, options: CliOptions): number {\n if (options.json) {\n process.stdout.write(\n `${JSON.stringify(\n {\n ok: result.ok,\n commitBinding: result.commitBinding,\n macChecked: result.macChecked,\n failures: result.failures,\n requiredSteps: result.requiredSteps,\n summary: formatSignoffSummary(result.proof),\n proof: result.proof,\n },\n null,\n 2,\n )}\\n`,\n )\n } else {\n process.stdout.write(`${formatSignoffVerification(result)}\\n${formatSignoffSummary(result.proof)}\\n`)\n }\n return result.ok ? 0 : 1\n}\n\nfunction main(): number {\n const options = parseArgs(process.argv.slice(2))\n const key = options.keyPath === null ? undefined : readSignoffKey(options.keyPath)\n\n if (options.file !== null) {\n return report(verifySignoffProofFile({ repoDir: options.dir, file: options.file, rev: options.rev, key, expectRepo: options.repo ?? undefined }), options)\n }\n\n const outcome = verifySignoffAtRev({ repoDir: options.dir, rev: options.rev, key, expectRepo: options.repo ?? undefined })\n if (!outcome.found) {\n process.stdout.write(\n `NO PROOF ${outcome.commit}\\n nothing is attached under refs/notes/signoff, and no proof covers this commit's tree\\n if notes exist upstream, this repo may not fetch them:\\n${outcome.hint.map((line) => ` ${line}`).join('\\n')}\\n`,\n )\n return 1\n }\n return report(outcome, options)\n}\n\
|
|
1
|
+
{"version":3,"sources":["../../src/signoff/proof-cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * `agent-app-verify-proof` — the command a reader runs to decide whether a\n * commit was signed off, by what, and whether it passed.\n *\n * agent-app-verify-proof <sha|rev> # look the proof up by commit\n * agent-app-verify-proof --file proof.json # check a proof document\n *\n * Exits 0 only when every check passes. Exits 1 on any failure, and on a commit\n * with no proof at all — \"nobody signed this off\" is a rejection, not a pass.\n */\nimport { invokedAsScript } from './invoked-as-script'\nimport { formatSignoffSummary, readSignoffKey } from './proof-record'\nimport { formatSignoffVerification, verifySignoffAtRev, verifySignoffProofFile, type SignoffVerification } from './proof-verify'\n\ninterface CliOptions {\n readonly rev: string\n readonly file: string | null\n readonly dir: string\n readonly keyPath: string | null\n readonly repo: string | null\n readonly json: boolean\n}\n\nfunction parseArgs(argv: readonly string[]): CliOptions {\n let rev = 'HEAD'\n let file: string | null = null\n let dir = process.cwd()\n let keyPath: string | null = null\n let repo: string | null = null\n let json = false\n let sawRev = false\n\n for (let index = 0; index < argv.length; index += 1) {\n const arg = argv[index] as string\n const next = (): string => {\n const value = argv[index + 1]\n if (value === undefined) throw new Error(`${arg} requires a value`)\n index += 1\n return value\n }\n if (arg === '--file') file = next()\n else if (arg === '--dir') dir = next()\n else if (arg === '--key') keyPath = next()\n else if (arg === '--repo') repo = next()\n else if (arg === '--json') json = true\n else if (arg.startsWith('-')) throw new Error(`unknown flag ${arg}`)\n else {\n if (sawRev) throw new Error(`unexpected second revision ${arg}`)\n rev = arg\n sawRev = true\n }\n }\n return { rev, file, dir, keyPath, repo, json }\n}\n\nfunction report(result: SignoffVerification, options: CliOptions): number {\n if (options.json) {\n process.stdout.write(\n `${JSON.stringify(\n {\n ok: result.ok,\n commitBinding: result.commitBinding,\n macChecked: result.macChecked,\n failures: result.failures,\n requiredSteps: result.requiredSteps,\n summary: formatSignoffSummary(result.proof),\n proof: result.proof,\n },\n null,\n 2,\n )}\\n`,\n )\n } else {\n process.stdout.write(`${formatSignoffVerification(result)}\\n${formatSignoffSummary(result.proof)}\\n`)\n }\n return result.ok ? 0 : 1\n}\n\nfunction main(): number {\n const options = parseArgs(process.argv.slice(2))\n const key = options.keyPath === null ? undefined : readSignoffKey(options.keyPath)\n\n if (options.file !== null) {\n return report(verifySignoffProofFile({ repoDir: options.dir, file: options.file, rev: options.rev, key, expectRepo: options.repo ?? undefined }), options)\n }\n\n const outcome = verifySignoffAtRev({ repoDir: options.dir, rev: options.rev, key, expectRepo: options.repo ?? undefined })\n if (!outcome.found) {\n process.stdout.write(\n `NO PROOF ${outcome.commit}\\n nothing is attached under refs/notes/signoff, and no proof covers this commit's tree\\n if notes exist upstream, this repo may not fetch them:\\n${outcome.hint.map((line) => ` ${line}`).join('\\n')}\\n`,\n )\n return 1\n }\n return report(outcome, options)\n}\n\n/* c8 ignore start — process wiring, exercised by the bin itself */\nif (invokedAsScript(import.meta.url, process.argv[1])) {\n try {\n process.exit(main())\n } catch (error) {\n process.stderr.write(`agent-app-verify-proof failed: ${error instanceof Error ? error.message : String(error)}\\n`)\n process.exit(1)\n }\n}\n/* c8 ignore stop */\n"],"mappings":";;;;;;;;;;;;;AAwBA,SAAS,UAAU,MAAqC;AACtD,MAAI,MAAM;AACV,MAAI,OAAsB;AAC1B,MAAI,MAAM,QAAQ,IAAI;AACtB,MAAI,UAAyB;AAC7B,MAAI,OAAsB;AAC1B,MAAI,OAAO;AACX,MAAI,SAAS;AAEb,WAAS,QAAQ,GAAG,QAAQ,KAAK,QAAQ,SAAS,GAAG;AACnD,UAAM,MAAM,KAAK,KAAK;AACtB,UAAM,OAAO,MAAc;AACzB,YAAM,QAAQ,KAAK,QAAQ,CAAC;AAC5B,UAAI,UAAU,OAAW,OAAM,IAAI,MAAM,GAAG,GAAG,mBAAmB;AAClE,eAAS;AACT,aAAO;AAAA,IACT;AACA,QAAI,QAAQ,SAAU,QAAO,KAAK;AAAA,aACzB,QAAQ,QAAS,OAAM,KAAK;AAAA,aAC5B,QAAQ,QAAS,WAAU,KAAK;AAAA,aAChC,QAAQ,SAAU,QAAO,KAAK;AAAA,aAC9B,QAAQ,SAAU,QAAO;AAAA,aACzB,IAAI,WAAW,GAAG,EAAG,OAAM,IAAI,MAAM,gBAAgB,GAAG,EAAE;AAAA,SAC9D;AACH,UAAI,OAAQ,OAAM,IAAI,MAAM,8BAA8B,GAAG,EAAE;AAC/D,YAAM;AACN,eAAS;AAAA,IACX;AAAA,EACF;AACA,SAAO,EAAE,KAAK,MAAM,KAAK,SAAS,MAAM,KAAK;AAC/C;AAEA,SAAS,OAAO,QAA6B,SAA6B;AACxE,MAAI,QAAQ,MAAM;AAChB,YAAQ,OAAO;AAAA,MACb,GAAG,KAAK;AAAA,QACN;AAAA,UACE,IAAI,OAAO;AAAA,UACX,eAAe,OAAO;AAAA,UACtB,YAAY,OAAO;AAAA,UACnB,UAAU,OAAO;AAAA,UACjB,eAAe,OAAO;AAAA,UACtB,SAAS,qBAAqB,OAAO,KAAK;AAAA,UAC1C,OAAO,OAAO;AAAA,QAChB;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA;AAAA,IACH;AAAA,EACF,OAAO;AACL,YAAQ,OAAO,MAAM,GAAG,0BAA0B,MAAM,CAAC;AAAA,EAAK,qBAAqB,OAAO,KAAK,CAAC;AAAA,CAAI;AAAA,EACtG;AACA,SAAO,OAAO,KAAK,IAAI;AACzB;AAEA,SAAS,OAAe;AACtB,QAAM,UAAU,UAAU,QAAQ,KAAK,MAAM,CAAC,CAAC;AAC/C,QAAM,MAAM,QAAQ,YAAY,OAAO,SAAY,eAAe,QAAQ,OAAO;AAEjF,MAAI,QAAQ,SAAS,MAAM;AACzB,WAAO,OAAO,uBAAuB,EAAE,SAAS,QAAQ,KAAK,MAAM,QAAQ,MAAM,KAAK,QAAQ,KAAK,KAAK,YAAY,QAAQ,QAAQ,OAAU,CAAC,GAAG,OAAO;AAAA,EAC3J;AAEA,QAAM,UAAU,mBAAmB,EAAE,SAAS,QAAQ,KAAK,KAAK,QAAQ,KAAK,KAAK,YAAY,QAAQ,QAAQ,OAAU,CAAC;AACzH,MAAI,CAAC,QAAQ,OAAO;AAClB,YAAQ,OAAO;AAAA,MACb,YAAY,QAAQ,MAAM;AAAA;AAAA;AAAA,EAAuJ,QAAQ,KAAK,IAAI,CAAC,SAAS,OAAO,IAAI,EAAE,EAAE,KAAK,IAAI,CAAC;AAAA;AAAA,IACvO;AACA,WAAO;AAAA,EACT;AACA,SAAO,OAAO,SAAS,OAAO;AAChC;AAGA,IAAI,gBAAgB,YAAY,KAAK,QAAQ,KAAK,CAAC,CAAC,GAAG;AACrD,MAAI;AACF,YAAQ,KAAK,KAAK,CAAC;AAAA,EACrB,SAAS,OAAO;AACd,YAAQ,OAAO,MAAM,kCAAkC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,CAAI;AACjH,YAAQ,KAAK,CAAC;AAAA,EAChB;AACF;","names":[]}
|