@defrex/autobuild 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/LICENSE +201 -0
- package/README.md +192 -0
- package/SPEC.md +2269 -0
- package/bin/ab-build-runner.ts +47 -0
- package/bin/ab-dev.ts +27 -0
- package/bin/ab-dispatch-kernel.ts +92 -0
- package/bin/ab-harvest-runner.ts +29 -0
- package/bin/ab-init-probe.ts +19 -0
- package/bin/ab-process-group-reaper.ts +32 -0
- package/bin/ab.ts +6 -0
- package/bin/agent/ab +3 -0
- package/docs/answering-blocked-builds.md +89 -0
- package/docs/architecture.md +726 -0
- package/docs/assets/headline-wide.png +0 -0
- package/docs/configuration.md +1623 -0
- package/docs/hosted-dispatcher-cutover-validation.md +178 -0
- package/docs/hosted-dispatcher.md +204 -0
- package/docs/mcp.md +119 -0
- package/docs/operator-api.md +186 -0
- package/docs/remote-store-protocol.md +972 -0
- package/docs/remote-ticket-protocol.md +79 -0
- package/docs/setup.md +289 -0
- package/docs/spec-standard.md +63 -0
- package/docs/ticket-dependencies.md +59 -0
- package/docs/vercel-sandbox-rollout-probe.md +7 -0
- package/package.json +135 -0
- package/packages/core/src/cli/args.ts +74 -0
- package/packages/core/src/cli/artifact.ts +182 -0
- package/packages/core/src/cli/binary.ts +198 -0
- package/packages/core/src/cli/build-control.ts +789 -0
- package/packages/core/src/cli/build-progress.ts +55 -0
- package/packages/core/src/cli/bulk-control.ts +385 -0
- package/packages/core/src/cli/context.ts +470 -0
- package/packages/core/src/cli/dashboard/actions.ts +59 -0
- package/packages/core/src/cli/dashboard/cells.ts +92 -0
- package/packages/core/src/cli/dashboard/composer.ts +230 -0
- package/packages/core/src/cli/dashboard/detail.ts +100 -0
- package/packages/core/src/cli/dashboard/frame-image.ts +373 -0
- package/packages/core/src/cli/dashboard/live.ts +152 -0
- package/packages/core/src/cli/dashboard/model.ts +1173 -0
- package/packages/core/src/cli/dashboard/poll.ts +196 -0
- package/packages/core/src/cli/dashboard/render.ts +1568 -0
- package/packages/core/src/cli/dashboard/selection.ts +66 -0
- package/packages/core/src/cli/dashboard/session-feed.ts +244 -0
- package/packages/core/src/cli/dashboard/session-view.ts +506 -0
- package/packages/core/src/cli/dashboard/transcript.ts +108 -0
- package/packages/core/src/cli/dev.ts +46 -0
- package/packages/core/src/cli/dispatch-frontend.ts +1045 -0
- package/packages/core/src/cli/dispatch-process.ts +259 -0
- package/packages/core/src/cli/dispatch.ts +3692 -0
- package/packages/core/src/cli/dotenv.ts +48 -0
- package/packages/core/src/cli/env.ts +206 -0
- package/packages/core/src/cli/harvest.ts +698 -0
- package/packages/core/src/cli/help.ts +713 -0
- package/packages/core/src/cli/init-agent.ts +106 -0
- package/packages/core/src/cli/init-validation.ts +583 -0
- package/packages/core/src/cli/init.ts +756 -0
- package/packages/core/src/cli/installation.ts +333 -0
- package/packages/core/src/cli/keyboard.ts +261 -0
- package/packages/core/src/cli/main.ts +1399 -0
- package/packages/core/src/cli/mcp.ts +279 -0
- package/packages/core/src/cli/models.ts +65 -0
- package/packages/core/src/cli/observe.ts +55 -0
- package/packages/core/src/cli/plugin.ts +227 -0
- package/packages/core/src/cli/pr-attachments.ts +138 -0
- package/packages/core/src/cli/pr-summary.ts +124 -0
- package/packages/core/src/cli/publication-settlement.ts +188 -0
- package/packages/core/src/cli/repo-state.ts +213 -0
- package/packages/core/src/cli/repository-status.ts +80 -0
- package/packages/core/src/cli/self-update.ts +443 -0
- package/packages/core/src/cli/status.ts +895 -0
- package/packages/core/src/cli/store-opening.contract.ts +289 -0
- package/packages/core/src/cli/store-opening.ts +167 -0
- package/packages/core/src/cli/store-ref.ts +33 -0
- package/packages/core/src/cli/terminal-restore.ts +174 -0
- package/packages/core/src/cli/terminal.ts +361 -0
- package/packages/core/src/cli/terminals.ts +995 -0
- package/packages/core/src/cli/testkit.ts +194 -0
- package/packages/core/src/cli/ticket.ts +484 -0
- package/packages/core/src/cli/upgrade-agent.ts +180 -0
- package/packages/core/src/cli/upgrade-commit.ts +468 -0
- package/packages/core/src/cli/upgrade-notice.ts +91 -0
- package/packages/core/src/cli/upgrade-progress.ts +110 -0
- package/packages/core/src/cli/upgrade.ts +888 -0
- package/packages/core/src/cli/wait.ts +587 -0
- package/packages/core/src/cli/watch.ts +932 -0
- package/packages/core/src/config/live.ts +255 -0
- package/packages/core/src/config/load.ts +98 -0
- package/packages/core/src/config/roles.ts +438 -0
- package/packages/core/src/config/schema.ts +774 -0
- package/packages/core/src/distribution.ts +24 -0
- package/packages/core/src/events/catalog.ts +201 -0
- package/packages/core/src/events/envelope.ts +59 -0
- package/packages/core/src/events/payloads.ts +618 -0
- package/packages/core/src/events/repository.ts +559 -0
- package/packages/core/src/events/sessions.ts +180 -0
- package/packages/core/src/harvest/schema.ts +147 -0
- package/packages/core/src/ids.ts +45 -0
- package/packages/core/src/integration/harness.ts +681 -0
- package/packages/core/src/kernel/auto-merge.ts +259 -0
- package/packages/core/src/kernel/converge.ts +117 -0
- package/packages/core/src/kernel/dispatch-settings.ts +60 -0
- package/packages/core/src/kernel/dispatch-status.ts +141 -0
- package/packages/core/src/kernel/engine.ts +1047 -0
- package/packages/core/src/kernel/harvest.ts +823 -0
- package/packages/core/src/kernel/origin.ts +69 -0
- package/packages/core/src/kernel/phases.ts +196 -0
- package/packages/core/src/kernel/plan-verify-selection.ts +105 -0
- package/packages/core/src/kernel/pr-attachments.ts +82 -0
- package/packages/core/src/kernel/reducer.ts +752 -0
- package/packages/core/src/kernel/stall.ts +155 -0
- package/packages/core/src/kernel/verify-applicability.ts +52 -0
- package/packages/core/src/markdown.ts +67 -0
- package/packages/core/src/ontology.ts +288 -0
- package/packages/core/src/open-map.ts +203 -0
- package/packages/core/src/operator/annotations.ts +187 -0
- package/packages/core/src/operator/client.ts +311 -0
- package/packages/core/src/operator/control.ts +117 -0
- package/packages/core/src/operator/index.ts +62 -0
- package/packages/core/src/operator/presentation.ts +22 -0
- package/packages/core/src/operator/protocol.ts +98 -0
- package/packages/core/src/operator/query.ts +201 -0
- package/packages/core/src/operator/registry.ts +1043 -0
- package/packages/core/src/operator/requests.ts +138 -0
- package/packages/core/src/operator/sandbox.ts +458 -0
- package/packages/core/src/operator/server.ts +616 -0
- package/packages/core/src/operator/sessions.ts +223 -0
- package/packages/core/src/operator/tickets.ts +177 -0
- package/packages/core/src/plugin-sdk/index.ts +174 -0
- package/packages/core/src/plugins/contract-entry.ts +74 -0
- package/packages/core/src/plugins/contract-env.ts +3 -0
- package/packages/core/src/plugins/load.ts +259 -0
- package/packages/core/src/plugins/manifest.ts +236 -0
- package/packages/core/src/plugins/registry.ts +274 -0
- package/packages/core/src/plugins/runtimes.ts +75 -0
- package/packages/core/src/ports/forge/contract.ts +258 -0
- package/packages/core/src/ports/forge/create.ts +74 -0
- package/packages/core/src/ports/forge/fake.ts +488 -0
- package/packages/core/src/ports/forge/github-pr-attachments.ts +351 -0
- package/packages/core/src/ports/forge/github-transport.ts +352 -0
- package/packages/core/src/ports/forge/github.ts +1162 -0
- package/packages/core/src/ports/forge/local-git.ts +568 -0
- package/packages/core/src/ports/runner/claude.ts +873 -0
- package/packages/core/src/ports/runner/cli-stream.ts +90 -0
- package/packages/core/src/ports/runner/codex.ts +873 -0
- package/packages/core/src/ports/runner/contract.ts +492 -0
- package/packages/core/src/ports/runner/fake.ts +213 -0
- package/packages/core/src/ports/runner/json-record.ts +2 -0
- package/packages/core/src/ports/runner/live-contract-fixture.ts +176 -0
- package/packages/core/src/ports/runner/one-shot.ts +29 -0
- package/packages/core/src/ports/runner/pi-bridge-core.ts +136 -0
- package/packages/core/src/ports/runner/pi-bridge.js +6 -0
- package/packages/core/src/ports/runner/pi-rpc.ts +454 -0
- package/packages/core/src/ports/runner/pi.ts +617 -0
- package/packages/core/src/ports/runner/production.ts +74 -0
- package/packages/core/src/ports/runner/provider-error.ts +121 -0
- package/packages/core/src/ports/runner/routing.ts +234 -0
- package/packages/core/src/ports/runner/runtime.ts +214 -0
- package/packages/core/src/ports/runner/session-env.ts +34 -0
- package/packages/core/src/ports/runner/stream-parts.ts +103 -0
- package/packages/core/src/ports/tickets/contract.ts +570 -0
- package/packages/core/src/ports/tickets/create.ts +135 -0
- package/packages/core/src/ports/tickets/fake.ts +215 -0
- package/packages/core/src/ports/tickets/file.ts +559 -0
- package/packages/core/src/ports/tickets/linear.ts +811 -0
- package/packages/core/src/ports/tickets/operations.ts +161 -0
- package/packages/core/src/ports/tickets/remote-index.ts +13 -0
- package/packages/core/src/ports/tickets/remote-protocol.ts +80 -0
- package/packages/core/src/ports/tickets/remote-server.ts +185 -0
- package/packages/core/src/ports/tickets/remote.ts +130 -0
- package/packages/core/src/ports/tickets/update.ts +51 -0
- package/packages/core/src/ports/types.ts +528 -0
- package/packages/core/src/ports/workspace/build-execution.ts +123 -0
- package/packages/core/src/ports/workspace/contract.ts +136 -0
- package/packages/core/src/ports/workspace/create.ts +132 -0
- package/packages/core/src/ports/workspace/distribution-archive.ts +317 -0
- package/packages/core/src/ports/workspace/fake.ts +432 -0
- package/packages/core/src/ports/workspace/git-worktree.ts +848 -0
- package/packages/core/src/ports/workspace/harvest-execution.ts +111 -0
- package/packages/core/src/ports/workspace/in-process-build-execution.ts +30 -0
- package/packages/core/src/ports/workspace/local-build-execution.ts +88 -0
- package/packages/core/src/ports/workspace/operator-sandbox.ts +142 -0
- package/packages/core/src/ports/workspace/phase-scratch.ts +156 -0
- package/packages/core/src/ports/workspace/vercel-sandbox.ts +2541 -0
- package/packages/core/src/processes/build-child-exit.ts +78 -0
- package/packages/core/src/processes/build-child.ts +119 -0
- package/packages/core/src/processes/build-execution-state.ts +95 -0
- package/packages/core/src/processes/build-parent-watch.ts +35 -0
- package/packages/core/src/processes/build-runner.ts +2614 -0
- package/packages/core/src/processes/dispatcher.ts +2825 -0
- package/packages/core/src/processes/execution-settlement.ts +171 -0
- package/packages/core/src/processes/harvest-child.ts +115 -0
- package/packages/core/src/processes/harvest-execution-state.ts +121 -0
- package/packages/core/src/processes/harvest-runner.ts +1656 -0
- package/packages/core/src/processes/harvest.ts +595 -0
- package/packages/core/src/processes/infrastructure-failure-budget.ts +146 -0
- package/packages/core/src/processes/phase-failure-budget.ts +52 -0
- package/packages/core/src/processes/process-group-reaper.ts +35 -0
- package/packages/core/src/processes/process-group.ts +65 -0
- package/packages/core/src/processes/publication-loss.ts +62 -0
- package/packages/core/src/processes/publication-state.ts +93 -0
- package/packages/core/src/processes/sandbox-state.ts +101 -0
- package/packages/core/src/registry.ts +24 -0
- package/packages/core/src/sessions/reducer.ts +185 -0
- package/packages/core/src/skills.ts +7 -0
- package/packages/core/src/spec-standard.ts +47 -0
- package/packages/core/src/store/adapter.ts +80 -0
- package/packages/core/src/store/build-scope.ts +272 -0
- package/packages/core/src/store/contract.ts +2581 -0
- package/packages/core/src/store/local/blobs.ts +38 -0
- package/packages/core/src/store/local/schema.ts +180 -0
- package/packages/core/src/store/local/store.ts +1713 -0
- package/packages/core/src/store/memory.ts +1138 -0
- package/packages/core/src/store/phase-session.ts +425 -0
- package/packages/core/src/store/remote/client.ts +780 -0
- package/packages/core/src/store/remote/index.ts +12 -0
- package/packages/core/src/store/remote/protocol.ts +348 -0
- package/packages/core/src/store/remote/server.ts +923 -0
- package/packages/core/src/store/remote/token.ts +137 -0
- package/packages/core/src/store/remote/version.ts +10 -0
- package/packages/core/src/store/retention.ts +91 -0
- package/packages/core/src/store/session-handle.ts +265 -0
- package/packages/core/src/store/streams/assemble.ts +79 -0
- package/packages/core/src/store/streams/lock.ts +42 -0
- package/packages/core/src/store/streams/session-writer.ts +306 -0
- package/packages/core/src/store/streams/types.ts +171 -0
- package/packages/core/src/store/streams/wait.ts +127 -0
- package/packages/core/src/store/subscribe.ts +105 -0
- package/packages/core/src/store/types.ts +466 -0
- package/packages/core/src/testing/fixed.ts +44 -0
- package/packages/core/src/testing/store-failures.ts +32 -0
- package/packages/core/src/zod-issues.ts +91 -0
- package/skills/code-review/SKILL.md +100 -0
- package/skills/finalize/SKILL.md +44 -0
- package/skills/guide/SKILL.md +1781 -0
- package/skills/guide/references/operator-api.md +164 -0
- package/skills/guide/references/plugin-authoring.md +354 -0
- package/skills/guide/references/remote-store-protocol.md +972 -0
- package/skills/guide/references/remote-ticket-protocol.md +79 -0
- package/skills/guide/references/setup.md +289 -0
- package/skills/guide/references/spec-standard.md +63 -0
- package/skills/guide/references/ticket-dependencies.md +59 -0
- package/skills/harvest/SKILL.md +66 -0
- package/skills/harvest-review/SKILL.md +63 -0
- package/skills/implement/SKILL.md +82 -0
- package/skills/plan/SKILL.md +123 -0
- package/skills/plan-review/SKILL.md +121 -0
- package/skills/reconcile/SKILL.md +65 -0
- package/skills/spec/SKILL.md +132 -0
- package/skills/tickets/SKILL.md +139 -0
- package/templates/autobuild.toml +36 -0
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The live region: the one impure piece of the dashboard, and the only thing
|
|
3
|
+
* here that touches the writer.
|
|
4
|
+
*
|
|
5
|
+
* The job is narrow — keep a block of lines repainted in place from the top
|
|
6
|
+
* of the screen, and never let it accumulate. Everything interesting about it
|
|
7
|
+
* is a consequence of one invariant:
|
|
8
|
+
*
|
|
9
|
+
* >>> The region's first line is always terminal row 1. <<<
|
|
10
|
+
*
|
|
11
|
+
* Dispatcher warnings are conditional frame chrome, so this seam owns only
|
|
12
|
+
* in-place replacement and cursor restoration. Routine notices never enter
|
|
13
|
+
* the live region, and nothing may print through it into dashboard scrollback.
|
|
14
|
+
*/
|
|
15
|
+
import type { KeyboardProtocol } from '../keyboard'
|
|
16
|
+
import type { TerminalOut } from '../terminal'
|
|
17
|
+
import {
|
|
18
|
+
ALTERNATE_SCREEN_MODE,
|
|
19
|
+
BRACKETED_PASTE_MODE,
|
|
20
|
+
HIDDEN_CURSOR_MODE,
|
|
21
|
+
} from '../terminal-restore'
|
|
22
|
+
|
|
23
|
+
/** Clear the entire active display. */
|
|
24
|
+
const CLEAR_DISPLAY = '\x1b[2J'
|
|
25
|
+
const CURSOR_POSITION = (row: number): string => `\x1b[${row};1H`
|
|
26
|
+
/**
|
|
27
|
+
* Bracketed paste (DEC mode 2004): the terminal brackets pasted text in
|
|
28
|
+
* `ESC[200~` … `ESC[201~`, which is the only way the input seam can tell a
|
|
29
|
+
* paste from typing and therefore the only way a multi-line paste can be
|
|
30
|
+
* inserted rather than submitted at its first line break.
|
|
31
|
+
*
|
|
32
|
+
* A terminal without the mode ignores both writes, and there a paste degrades
|
|
33
|
+
* to exactly today's behavior — the newline arrives as an ordinary keypress.
|
|
34
|
+
* Nothing in the codebase can detect that, so it is documented rather than
|
|
35
|
+
* worked around.
|
|
36
|
+
*/
|
|
37
|
+
/**
|
|
38
|
+
* How many lines a region may paint on a `rows`-row screen: **one fewer than
|
|
39
|
+
* the screen has**.
|
|
40
|
+
*
|
|
41
|
+
* `update` terminates EVERY line with `\n`, the last one included, so after
|
|
42
|
+
* painting N lines the cursor rests on a fresh row BELOW the frame — and that
|
|
43
|
+
* row has to exist. At N === rows the final newline scrolls the frame's top
|
|
44
|
+
* line away even when painting a freshly cleared alternate screen. N === rows
|
|
45
|
+
* is therefore the one height that cannot be painted.
|
|
46
|
+
*
|
|
47
|
+
* This lives here, next to the newline that causes it, because no other seam
|
|
48
|
+
* can see it: `render.ts` counts lines and knows nothing of the trailing
|
|
49
|
+
* newline, and a fake `TerminalOut` collects strings and knows nothing of
|
|
50
|
+
* scrolling. The invariant is `frame.length < term.rows`, and it is only
|
|
51
|
+
* observable where a real region meets a real screen.
|
|
52
|
+
*
|
|
53
|
+
* 0 for a 1-row screen: there is no paintable height, and the honest answer is
|
|
54
|
+
* to paint nothing rather than a header that scrolls itself off.
|
|
55
|
+
*/
|
|
56
|
+
export function paintableRows(rows: number): number {
|
|
57
|
+
return Math.max(0, rows - 1)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export class LiveRegion {
|
|
61
|
+
/** The frame currently painted — `[]` when the region is empty. Also the
|
|
62
|
+
* identical-frame check's comparand. */
|
|
63
|
+
private painted: string[] = []
|
|
64
|
+
/** Terminal height at the last paint; a resize invalidates equality. */
|
|
65
|
+
private paintedRows: number | undefined
|
|
66
|
+
private alternate = false
|
|
67
|
+
private hidden = false
|
|
68
|
+
private bracketedPaste = false
|
|
69
|
+
private finished = false
|
|
70
|
+
|
|
71
|
+
constructor(
|
|
72
|
+
private readonly term: TerminalOut,
|
|
73
|
+
private readonly keyboard?: KeyboardProtocol,
|
|
74
|
+
) {}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Repaint the region in place.
|
|
78
|
+
*
|
|
79
|
+
* Each effective paint replaces the whole alternate display and anchors the
|
|
80
|
+
* frame at row 1, so its header stays fixed as the frame grows, shrinks, or
|
|
81
|
+
* crosses a resize. Equal lines at equal height cost zero writes and zero
|
|
82
|
+
* flicker; a resize invalidates the paint even when the lines are unchanged.
|
|
83
|
+
*/
|
|
84
|
+
update(lines: string[]): void {
|
|
85
|
+
if (this.finished) return
|
|
86
|
+
|
|
87
|
+
const rows = this.term.rows
|
|
88
|
+
// An empty initial render (for example, a one-row terminal) is not a real
|
|
89
|
+
// paint and must not switch screens merely to display nothing.
|
|
90
|
+
if (!this.alternate && lines.length === 0) return
|
|
91
|
+
if (this.paintedRows === rows && sameFrame(this.painted, lines)) return
|
|
92
|
+
|
|
93
|
+
if (!this.alternate) {
|
|
94
|
+
this.term.modes.enter(ALTERNATE_SCREEN_MODE)
|
|
95
|
+
this.alternate = true
|
|
96
|
+
this.term.modes.enter(HIDDEN_CURSOR_MODE)
|
|
97
|
+
this.hidden = true
|
|
98
|
+
this.term.modes.enter(BRACKETED_PASTE_MODE)
|
|
99
|
+
this.bracketedPaste = true
|
|
100
|
+
// Kitty keyboard mode stacks are per-screen, so negotiate only after
|
|
101
|
+
// the alternate screen is active.
|
|
102
|
+
this.keyboard?.screenEntered()
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
this.term.write(CLEAR_DISPLAY + CURSOR_POSITION(1))
|
|
106
|
+
const frame = lines.map((line) => `${line}\n`).join('')
|
|
107
|
+
if (frame.length > 0) this.term.write(frame)
|
|
108
|
+
this.painted = [...lines]
|
|
109
|
+
this.paintedRows = rows
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Release the region: restore the normal screen, copy the last frame there,
|
|
114
|
+
* restore the cursor, and stop tracking. Idempotent.
|
|
115
|
+
*
|
|
116
|
+
* The copy preserves the existing external contract: the final frame is the
|
|
117
|
+
* answer the operator ran the command for, just as `git log` and a finished
|
|
118
|
+
* progress bar leave their output on screen. The normal display is never
|
|
119
|
+
* erased during teardown.
|
|
120
|
+
*/
|
|
121
|
+
finish(): void {
|
|
122
|
+
if (this.finished) return
|
|
123
|
+
this.finished = true
|
|
124
|
+
|
|
125
|
+
// Leave the terminal's paste mode as we found it, BEFORE handing the
|
|
126
|
+
// normal screen back: a mode left on outlives the process and would
|
|
127
|
+
// bracket the operator's next shell paste.
|
|
128
|
+
if (this.bracketedPaste) {
|
|
129
|
+
this.term.modes.leave(BRACKETED_PASTE_MODE)
|
|
130
|
+
this.bracketedPaste = false
|
|
131
|
+
}
|
|
132
|
+
// Balance an outstanding keyboard push before returning to the normal
|
|
133
|
+
// screen. Calling this even when no frame painted also latches late replies.
|
|
134
|
+
this.keyboard?.screenLeaving()
|
|
135
|
+
if (this.alternate) {
|
|
136
|
+
this.term.modes.leave(ALTERNATE_SCREEN_MODE)
|
|
137
|
+
this.alternate = false
|
|
138
|
+
const frame = this.painted.map((line) => `${line}\n`).join('')
|
|
139
|
+
if (frame.length > 0) this.term.write(frame)
|
|
140
|
+
}
|
|
141
|
+
if (this.hidden) {
|
|
142
|
+
this.term.modes.leave(HIDDEN_CURSOR_MODE)
|
|
143
|
+
this.hidden = false
|
|
144
|
+
}
|
|
145
|
+
this.painted = []
|
|
146
|
+
this.paintedRows = undefined
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function sameFrame(a: string[], b: string[]): boolean {
|
|
151
|
+
return a.length === b.length && a.every((line, i) => line === b[i])
|
|
152
|
+
}
|