@clipboard-health/groundcrew 4.48.0 → 4.48.2
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/clearance-allow-hosts +1 -0
- package/crew.config.example.ts +2 -2
- package/dist/commands/doctor.js +0 -25
- package/dist/commands/openWorkspace.d.ts.map +1 -1
- package/dist/commands/openWorkspace.js +14 -17
- package/dist/commands/resumeWorkspace.d.ts.map +1 -1
- package/dist/commands/resumeWorkspace.js +7 -15
- package/dist/commands/setupWorkspace.d.ts.map +1 -1
- package/dist/commands/setupWorkspace.js +16 -18
- package/dist/lib/agentLaunch.d.ts +13 -11
- package/dist/lib/agentLaunch.d.ts.map +1 -1
- package/dist/lib/agentLaunch.js +102 -52
- package/dist/lib/cmuxAgentHookInstall.d.ts +17 -0
- package/dist/lib/cmuxAgentHookInstall.d.ts.map +1 -0
- package/dist/lib/cmuxAgentHookInstall.js +42 -0
- package/dist/lib/codexConfigRelocation.d.ts +60 -0
- package/dist/lib/codexConfigRelocation.d.ts.map +1 -0
- package/dist/lib/codexConfigRelocation.js +70 -0
- package/dist/lib/commandRunner.d.ts +9 -0
- package/dist/lib/commandRunner.d.ts.map +1 -1
- package/dist/lib/commandRunner.js +1 -0
- package/dist/lib/config.d.ts +8 -12
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +1 -2
- package/dist/lib/gitCommonDir.d.ts +7 -0
- package/dist/lib/gitCommonDir.d.ts.map +1 -0
- package/dist/lib/gitCommonDir.js +15 -0
- package/dist/lib/host.d.ts +0 -14
- package/dist/lib/host.d.ts.map +1 -1
- package/dist/lib/host.js +1 -8
- package/dist/lib/launchCommand.d.ts +32 -52
- package/dist/lib/launchCommand.d.ts.map +1 -1
- package/dist/lib/launchCommand.js +96 -187
- package/dist/lib/localRunner.d.ts +4 -5
- package/dist/lib/localRunner.d.ts.map +1 -1
- package/dist/lib/localRunner.js +4 -23
- package/docs/configuration.md +3 -3
- package/docs/runners.md +7 -50
- package/docs/setup-hooks.md +1 -1
- package/docs/spec/e2e-scenario-catalog.md +1 -1
- package/docs/spec/v2-design.md +7 -7
- package/docs/task-sources.md +3 -3
- package/package.json +2 -5
- package/static/demo.tape +0 -2
- package/static/render-demo.sh +0 -1
- package/dist/lib/clearanceHosts.d.ts +0 -31
- package/dist/lib/clearanceHosts.d.ts.map +0 -1
- package/dist/lib/clearanceHosts.js +0 -96
- package/dist/lib/srtLaunch.d.ts +0 -61
- package/dist/lib/srtLaunch.d.ts.map +0 -1
- package/dist/lib/srtLaunch.js +0 -107
- package/dist/lib/srtPolicy.d.ts +0 -118
- package/dist/lib/srtPolicy.d.ts.map +0 -1
- package/dist/lib/srtPolicy.js +0 -371
package/docs/spec/v2-design.md
CHANGED
|
@@ -23,7 +23,7 @@ Locked constraints, not to be re-litigated during implementation:
|
|
|
23
23
|
|
|
24
24
|
- The locked decisions kill or fundamentally reshape most of v1 (~21K source lines, ~38K test lines): `config.ts` dies with the TS-config format, the reviewer PR-polling loop dies, clearance/safehouse/sbx die, several commands die, and the core nouns are redefined. Every external surface breaks: config, CLI, source contract, sandbox posture, completion model.
|
|
25
25
|
- Evolve's safety-net premise was stale: the acceptance suite never runs against v1 (green-on-v1 dropped), so in-place evolution gets zero protection from it. "Evolve" would be the same rewrite executed as a thousand PRs fighting 38K test lines that assert dying behavior.
|
|
26
|
-
- Roughly 15–25% of v1 survives recognizably (
|
|
26
|
+
- Roughly 15–25% of v1 survives recognizably (sandbox policy and launch composition, the cleaner, tmux/git plumbing, doctor pieces, usage, completions). Surviving code is **ported file-by-file with its unit tests**; `git mv` preserves history.
|
|
27
27
|
|
|
28
28
|
**Repo strategy: same repo, isolated `v2/` workspace.** v2 grows on `main` under a top-level `v2/` with its own `package.json`, flat `src/<module>` layout, and its own `CLAUDE.md`/`AGENTS.md`/`CONTEXT.md` carrying the v2 ubiquitous language. v1 and v2 use the same nouns with contradictory meanings ("Workspace" means a terminal pane in v1 and a per-task directory of worktrees in v2), so sharing one `src/` was rejected — it would contaminate agent-driven work. dependency-cruiser forbids cross-tree imports in both directions. **Flip condition:** if v2-building agents keep tripping over v1 despite the boundary, escalate mid-build to a new repo initialized from a full clone, then rename old → `groundcrew-v1`, new → `groundcrew`.
|
|
29
29
|
|
|
@@ -96,14 +96,14 @@ Single-repo tasks stay dead simple as the degenerate case: one designated repo,
|
|
|
96
96
|
**"Plugin" means exactly one thing in v2: a task source.** Everything else that looked pluggable is config or core:
|
|
97
97
|
|
|
98
98
|
- **Agent harnesses are declarative config**, not plugins: `cmd`, resume args, sandbox preferences, plus first-class `model`/`effort` fields. `cmd`-points-at-your-script is the escape hatch for odd agents.
|
|
99
|
-
- **Sandbox runners are core-only**, explicitly not pluggable — a pluggable sandbox is a contradiction under sandboxed-by-default.
|
|
99
|
+
- **Sandbox runners are core-only**, explicitly not pluggable — a pluggable sandbox is a contradiction under sandboxed-by-default. The implementation selects the supported platform runner and keeps its mechanics behind the core sandbox boundary.
|
|
100
100
|
- **No in-process plugin class exists.** The internal `TaskSource` interface survives only as core's private representation of a discovered manifest.
|
|
101
101
|
|
|
102
102
|
**Packaging:** a plugin is a **directory bundle** — `source.json` plus scripts — discovered under `~/.config/groundcrew/task-sources/<name>/` (user) or bundled in the package (first-party). The directory is the plugin: language-agnostic, no runtime loading. Distribution is deliberately not groundcrew's problem (git clone, `curl | tar`, npm postinstall). No registry or catalog in v2.0; provenance tiers collapse to `package | user`. Review isolation is met structurally: third-party code never enters the core repo and never runs in the core process.
|
|
103
103
|
|
|
104
104
|
**Versioning:** `source.json` carries a required integer `protocolVersion: 1` naming the contract generation, bumped only on breaking change — capability-by-omission carries all additive change (no version sniffing: `update` absent means read-only). Parseable-but-unsupported version → explicit actionable error in discovery/doctor/status, never a silent skip; unparseable manifests keep skip-plus-warn. Core declares a supported set; v2.0 ships `{1}`. No v1 command aliases.
|
|
105
105
|
|
|
106
|
-
**Sources are sandboxed by default**, under the same
|
|
106
|
+
**Sources are sandboxed by default**, under the same core sandbox boundary as agents: deny-by-default; a source gets its install dir (read), declared secret files, declared env, stdout/stderr, and a per-source scratch dir. The manifest gains a **network egress allowlist** (e.g. `network: ["api.linear.app"]`) alongside `secrets`/`env`/`prerequisites`. Uniform across origins — bundled first-party sources run under the same policy. Per-source opt-out (`sandbox: false`) is loud in status/doctor.
|
|
107
107
|
|
|
108
108
|
**Batteries included, kernel-pure code paths:** `linear`, `jira`, and `todo-txt` ship as manifest bundles crossing the same process boundary as third-party sources — first-party sources are the protocol's permanent conformance tests. The generic `shell` config-block adapter dies: bring-your-own-scripts is just a user-dir manifest.
|
|
109
109
|
|
|
@@ -190,7 +190,7 @@ Consequences: the session presenter presents sessions, not runs; `status` render
|
|
|
190
190
|
| **Run** | The run record: state machine, **reported layer** (`artifact add`/`done` intake), outcomes, writeback point | the **Writeback port** (consumer-owned, defined in `src/run/`) |
|
|
191
191
|
| **Workspace** | Worktrees, branches, **observed layer** (credential-free git facts), `.groundcrew/task.json` and the task-identity resolver; typed errors that Shell maps to exit codes 2/3 | the workspace marker-file format |
|
|
192
192
|
| **Session** | Harness profiles (declarative), launch composition (presenter → sandbox → agent), pause/resume and session-id capture; cmux/tmux/zellij adapters in-core | the **presenter contract seam** |
|
|
193
|
-
| **Sandbox** | Pure library: `wrap(command, policy) → command`;
|
|
193
|
+
| **Sandbox** | Pure library: `wrap(command, policy) → command`; runner mechanics hidden; no lifecycle ownership | nothing pluggable — core-only by decision |
|
|
194
194
|
| **Shell** | commander wiring, routing (`repo add` → Workspace; `artifact add`/`done` → Run), rendering, error→exit-code mapping; `status` = read model joining Run (reported) and Workspace (observed) | — |
|
|
195
195
|
|
|
196
196
|
The flow model's central invariant gets one owner per layer — **observed = Workspace, reported = Run** — so a lie or missing link is always attributable to exactly one module. Sandbox has two real callers (Session wraps agents, Acquisition wraps sources): a real seam, and the one place that keeps "sandbox runners are not pluggable" enforceable.
|
|
@@ -279,7 +279,7 @@ The file sink gets everything, always. Console: `info`+ by default, human-render
|
|
|
279
279
|
|
|
280
280
|
### 10.5 Reconcile and reaping
|
|
281
281
|
|
|
282
|
-
**Active sweep yes, TTL no.**
|
|
282
|
+
**Active sweep yes, TTL no.** Agent launches are process-scoped, and the interactive baseline forbids killing live agents on a timer. Persistent runner environments remain externally managed.
|
|
283
283
|
|
|
284
284
|
- **Reconcile is an idempotent library routine with two callers**: startup (the crash-safety guarantee — on-disk git/tmux/sandbox state is the source of truth, compared against expected task state; `using`/signal handlers are the graceful path only) and the dispatcher tick (every Nth poll cycle).
|
|
285
285
|
- Auto-GC only the provably dead: presenter surfaces whose process exited, stale run records, clean worktrees of source-terminal tasks (the cleaner, section 5).
|
|
@@ -326,7 +326,7 @@ If Effect is ever reconsidered: gate on v4-stable, follow the opencode pattern (
|
|
|
326
326
|
1. **Build the acceptance suite red-first from the [E2E scenario catalog](./e2e-scenario-catalog.md)** — the `e2e/` package, fixture source, scripted agent, fake `gh`, harness self-tests. This is the entry point; no v2 code before it.
|
|
327
327
|
2. Fix `main`'s release automation (path/scope filter) so v2 commits don't trigger 4.x publishes (section 2).
|
|
328
328
|
3. Scaffold the `v2/` workspace: seven-module skeleton, dependency-cruiser rules (section 9.6) proven to bite, `CONTEXT.md` from the glossary seed, ADRs seeded from the wayfinder resolutions.
|
|
329
|
-
4. Bring the suite green module by module, porting the surviving v1 code (
|
|
329
|
+
4. Bring the suite green module by module, porting the surviving v1 code (sandbox policy and launch composition, cleaner, tmux/git plumbing, doctor pieces) file-by-file with its tests.
|
|
330
330
|
5. Before publishing 5.0.0: resolve the `crew upgrade` ambush (section 11) and execute the cutover (section 2).
|
|
331
331
|
|
|
332
332
|
Post-v2 handoff backlog (deferred by decision): migration easing detail (section 11); presenter bundles and their trust posture (section 8); `crew progress` (section 10.6); an optional repo allowlist (section 4).
|
|
@@ -343,7 +343,7 @@ Every map decision, in resolution order. Full rationale lives in the linked tick
|
|
|
343
343
|
| [Multi-repo task model](https://linear.app/clipboardhealth/issue/DEVOP-5967) | One session per task over a workspace of worktrees | 4 |
|
|
344
344
|
| [Flow model](https://linear.app/clipboardhealth/issue/DEVOP-5968) | Sinks and flows dissolve; `list`/`get`/`update` source contract; run-state machine | 3 |
|
|
345
345
|
| [Effect vs plain TypeScript](https://linear.app/clipboardhealth/issue/DEVOP-5972) | Plain TypeScript; exceptions inside, result-shaped protocol at the boundary; reconcile-on-startup | 12.1 |
|
|
346
|
-
| [Plugin packaging and review isolation](https://linear.app/clipboardhealth/issue/DEVOP-5973) | Task sources are the only plugin kind; directory bundles; `protocolVersion`; sandboxed by default;
|
|
346
|
+
| [Plugin packaging and review isolation](https://linear.app/clipboardhealth/issue/DEVOP-5973) | Task sources are the only plugin kind; directory bundles; `protocolVersion`; sandboxed by default; core runners | 6 |
|
|
347
347
|
| [CLI surface](https://linear.app/clipboardhealth/issue/DEVOP-5975) | 14 commands; `crew.config.jsonc`; install.sh → `crew init` | 7 |
|
|
348
348
|
| [E2E scenario catalog](https://linear.app/clipboardhealth/issue/DEVOP-5974) | v2-only red-first suite; harness self-tests; completion model codified (amends the destination) | 5 |
|
|
349
349
|
| [Headless extension point](https://linear.app/clipboardhealth/issue/DEVOP-5976) | Session-presenter seam; headless = future detached presenter (amends CLI config: `multiplexer` → `presenter`) | 8 |
|
package/docs/task-sources.md
CHANGED
|
@@ -52,9 +52,9 @@ expected or explicitly allowed.
|
|
|
52
52
|
Set `sandboxWritePaths` when the shell source owns local files the agent must
|
|
53
53
|
read and update in place, such as a plan directory. Each path may be absolute,
|
|
54
54
|
relative to the agent working directory, or `~`-prefixed. Under the `safehouse`
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
runner, groundcrew opens those directories for read and write only for tasks
|
|
56
|
+
from that source; an unqualified task id grants every eligible source's
|
|
57
|
+
declared paths. The `sdx` runner does not mount these host paths.
|
|
58
58
|
|
|
59
59
|
```json
|
|
60
60
|
[
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@clipboard-health/groundcrew",
|
|
3
|
-
"version": "4.48.
|
|
3
|
+
"version": "4.48.2",
|
|
4
4
|
"description": "Linear-driven orchestrator that launches AI coding agents in git worktrees, with workspace lifecycle and usage tracking.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agent",
|
|
@@ -63,14 +63,12 @@
|
|
|
63
63
|
"markdown:lint": "markdownlint-cli2 --config config/markdownlint-cli2.jsonc",
|
|
64
64
|
"prepack": "node --run build",
|
|
65
65
|
"prepare": "husky",
|
|
66
|
-
"spell:check": "cspell --config config/cspell.json --no-summary --no-progress --no-must-find-files",
|
|
67
66
|
"sync-ai-rules": "node ./node_modules/@clipboard-health/ai-rules/scripts/sync.js common",
|
|
68
67
|
"syncpack:lint": "syncpack lint --config config/syncpack.config.ts",
|
|
69
68
|
"test": "vitest run --coverage",
|
|
70
69
|
"verify": "node scripts/verifyAll.mts"
|
|
71
70
|
},
|
|
72
71
|
"dependencies": {
|
|
73
|
-
"@anthropic-ai/sandbox-runtime": "0.0.65",
|
|
74
72
|
"@clipboard-health/clearance": "1.4.7",
|
|
75
73
|
"@linear/sdk": "88.1.0",
|
|
76
74
|
"agent-trust": "1.0.0",
|
|
@@ -79,7 +77,7 @@
|
|
|
79
77
|
"zod": "4.4.3"
|
|
80
78
|
},
|
|
81
79
|
"devDependencies": {
|
|
82
|
-
"@clipboard-health/ai-rules": "2.
|
|
80
|
+
"@clipboard-health/ai-rules": "2.43.4",
|
|
83
81
|
"@clipboard-health/oxlint-config": "1.12.9",
|
|
84
82
|
"@nx/js": "22.7.7",
|
|
85
83
|
"@tsconfig/node24": "24.0.4",
|
|
@@ -87,7 +85,6 @@
|
|
|
87
85
|
"@types/node": "25.9.5",
|
|
88
86
|
"@typescript/native-preview": "7.0.0-dev.20260707.2",
|
|
89
87
|
"@vitest/coverage-v8": "4.1.10",
|
|
90
|
-
"cspell": "10.0.1",
|
|
91
88
|
"dependency-cruiser": "18.1.0",
|
|
92
89
|
"husky": "9.1.7",
|
|
93
90
|
"jscpd": "5.0.12",
|
package/static/demo.tape
CHANGED
package/static/render-demo.sh
CHANGED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Translate the existing clearance egress allowlist into srt
|
|
3
|
-
* `network.allowedDomains`. groundcrew keeps one source of truth for which
|
|
4
|
-
* hosts an agent may reach: the `CLEARANCE_ALLOW_HOSTS` env var and the
|
|
5
|
-
* newline-delimited files named by `CLEARANCE_ALLOW_HOSTS_FILES` (the shipped
|
|
6
|
-
* `clearance-allow-hosts` starter plus any personal files).
|
|
7
|
-
*
|
|
8
|
-
* clearance and srt share host-matching semantics — a bare `example.com` is an
|
|
9
|
-
* exact match and `*.example.com` matches subdomains — so the translation is
|
|
10
|
-
* almost an identity. The only real work is parsing (comments, blanks,
|
|
11
|
-
* comma/whitespace/newline separators), de-duplication, and dropping entries
|
|
12
|
-
* srt's domain schema would reject (`*.com`, `*`, leading/trailing dots, bare
|
|
13
|
-
* tokens without a dot like `localhost`) so the generated settings file always
|
|
14
|
-
* validates.
|
|
15
|
-
*/
|
|
16
|
-
export interface CollectAllowedDomainsInput {
|
|
17
|
-
/** Raw `CLEARANCE_ALLOW_HOSTS` value (comma/whitespace separated). */
|
|
18
|
-
hosts?: string | undefined;
|
|
19
|
-
/**
|
|
20
|
-
* Raw `CLEARANCE_ALLOW_HOSTS_FILES` value: a list of file paths joined by
|
|
21
|
-
* the platform path delimiter (`:` on POSIX), each a newline-delimited host
|
|
22
|
-
* list. Missing or unreadable files are skipped with a debug line.
|
|
23
|
-
*/
|
|
24
|
-
files?: string | undefined;
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Parse and validate clearance allow-host sources into a de-duplicated list of
|
|
28
|
-
* srt domain patterns, preserving first-seen order.
|
|
29
|
-
*/
|
|
30
|
-
export declare function collectAllowedDomains(input: CollectAllowedDomainsInput): string[];
|
|
31
|
-
//# sourceMappingURL=clearanceHosts.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"clearanceHosts.d.ts","sourceRoot":"","sources":["../../src/lib/clearanceHosts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAOH,MAAM,WAAW,0BAA0B;IACzC,sEAAsE;IACtE,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,0BAA0B,GAAG,MAAM,EAAE,CA8BjF"}
|
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Translate the existing clearance egress allowlist into srt
|
|
3
|
-
* `network.allowedDomains`. groundcrew keeps one source of truth for which
|
|
4
|
-
* hosts an agent may reach: the `CLEARANCE_ALLOW_HOSTS` env var and the
|
|
5
|
-
* newline-delimited files named by `CLEARANCE_ALLOW_HOSTS_FILES` (the shipped
|
|
6
|
-
* `clearance-allow-hosts` starter plus any personal files).
|
|
7
|
-
*
|
|
8
|
-
* clearance and srt share host-matching semantics — a bare `example.com` is an
|
|
9
|
-
* exact match and `*.example.com` matches subdomains — so the translation is
|
|
10
|
-
* almost an identity. The only real work is parsing (comments, blanks,
|
|
11
|
-
* comma/whitespace/newline separators), de-duplication, and dropping entries
|
|
12
|
-
* srt's domain schema would reject (`*.com`, `*`, leading/trailing dots, bare
|
|
13
|
-
* tokens without a dot like `localhost`) so the generated settings file always
|
|
14
|
-
* validates.
|
|
15
|
-
*/
|
|
16
|
-
import { readFileSync } from "node:fs";
|
|
17
|
-
import { splitPathList } from "./pathList.js";
|
|
18
|
-
import { debug } from "./util.js";
|
|
19
|
-
/**
|
|
20
|
-
* Parse and validate clearance allow-host sources into a de-duplicated list of
|
|
21
|
-
* srt domain patterns, preserving first-seen order.
|
|
22
|
-
*/
|
|
23
|
-
export function collectAllowedDomains(input) {
|
|
24
|
-
const texts = [];
|
|
25
|
-
for (const file of splitPathList(input.files)) {
|
|
26
|
-
try {
|
|
27
|
-
texts.push(readFileSync(file, "utf8"));
|
|
28
|
-
}
|
|
29
|
-
catch (error) {
|
|
30
|
-
debug(`Skipping unreadable CLEARANCE_ALLOW_HOSTS_FILES entry ${file}: ${String(error)}`);
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
if (input.hosts !== undefined) {
|
|
34
|
-
texts.push(input.hosts);
|
|
35
|
-
}
|
|
36
|
-
const seen = new Set();
|
|
37
|
-
const domains = [];
|
|
38
|
-
for (const text of texts) {
|
|
39
|
-
for (const raw of tokenize(text)) {
|
|
40
|
-
const domain = normalizeDomain(raw);
|
|
41
|
-
if (domain === undefined) {
|
|
42
|
-
continue;
|
|
43
|
-
}
|
|
44
|
-
const key = domain.toLowerCase();
|
|
45
|
-
if (seen.has(key)) {
|
|
46
|
-
continue;
|
|
47
|
-
}
|
|
48
|
-
seen.add(key);
|
|
49
|
-
domains.push(domain);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
return domains;
|
|
53
|
-
}
|
|
54
|
-
/**
|
|
55
|
-
* Split a host source into candidate tokens. Handles env-style
|
|
56
|
-
* comma/whitespace separators and file-style newline lists with `#` comments
|
|
57
|
-
* (full-line or trailing — hostnames never contain `#`).
|
|
58
|
-
*/
|
|
59
|
-
function tokenize(text) {
|
|
60
|
-
return text
|
|
61
|
-
.split(/\r?\n/)
|
|
62
|
-
.flatMap((line) => line.replace(/#.*$/, "").split(/[\s,]+/))
|
|
63
|
-
.map((token) => token.trim())
|
|
64
|
-
.filter((token) => token.length > 0);
|
|
65
|
-
}
|
|
66
|
-
/**
|
|
67
|
-
* Normalize a raw host token into a valid srt domain pattern, or `undefined`
|
|
68
|
-
* if srt would reject it. Mirrors srt's `DomainPatternSchema`: a wildcard must
|
|
69
|
-
* be `*.<domain-with-a-dot>`; a bare host must contain a dot and no `*`. A
|
|
70
|
-
* leading-dot suffix form (`.example.com`) is rewritten to `*.example.com`.
|
|
71
|
-
*/
|
|
72
|
-
function normalizeDomain(token) {
|
|
73
|
-
const candidate = token.startsWith(".") ? `*${token}` : token;
|
|
74
|
-
if (candidate.startsWith("*.")) {
|
|
75
|
-
const base = candidate.slice(2);
|
|
76
|
-
return isPlainDomain(base) ? candidate : undefined;
|
|
77
|
-
}
|
|
78
|
-
return isPlainDomain(candidate) ? candidate : undefined;
|
|
79
|
-
}
|
|
80
|
-
function isPlainDomain(value) {
|
|
81
|
-
return (value.length > 0 &&
|
|
82
|
-
!value.includes("*") &&
|
|
83
|
-
// Mirror srt's DomainPatternSchema, which rejects scheme/path/port tokens.
|
|
84
|
-
// This matters for safety, not just correctness: a token srt rejects (e.g.
|
|
85
|
-
// `https://api.github.com`, `api.github.com:443`, `github.com/path`) fails
|
|
86
|
-
// the whole settings file's schema validation, and srt's `loadConfig` then
|
|
87
|
-
// returns null → the CLI silently falls back to a config with no read mask.
|
|
88
|
-
// Dropping such tokens here keeps the generated settings valid (fail closed
|
|
89
|
-
// for that host, never fail open for the launch).
|
|
90
|
-
!value.includes("://") &&
|
|
91
|
-
!value.includes("/") &&
|
|
92
|
-
!value.includes(":") &&
|
|
93
|
-
value.includes(".") &&
|
|
94
|
-
!value.startsWith(".") &&
|
|
95
|
-
!value.endsWith("."));
|
|
96
|
-
}
|
package/dist/lib/srtLaunch.d.ts
DELETED
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import type { AgentDefinition } from "./config.ts";
|
|
2
|
-
export interface StagedSrtLaunch {
|
|
3
|
-
/** Dedicated temp dir holding the settings files (and any relocated config home). */
|
|
4
|
-
directory: string;
|
|
5
|
-
/** Profile-neutral policy for the prepareWorktree wrap (no agent credentials). */
|
|
6
|
-
prepareFile: string;
|
|
7
|
-
/** Full agent policy for the agent wrap. */
|
|
8
|
-
agentFile: string;
|
|
9
|
-
/**
|
|
10
|
-
* Env var pointing the agent at its relocated, writable config home (codex's
|
|
11
|
-
* `CODEX_HOME`). Threaded into the agent wrap by `buildLaunchCommand`.
|
|
12
|
-
* Undefined for read-only agents (claude), which run with a read-only home.
|
|
13
|
-
*/
|
|
14
|
-
agentConfigDirEnv?: {
|
|
15
|
-
name: string;
|
|
16
|
-
value: string;
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
/**
|
|
20
|
-
* Resolve the worktree's real git common dir — the shared `.git` the srt policy
|
|
21
|
-
* fences off (read-grant + narrow write-allow + write-denies). Derived from the
|
|
22
|
-
* worktree itself rather than assuming a `<projectDir>/<repo>/.git` clone, so
|
|
23
|
-
* scripted/sparse-checkout worktrees — whose checkout is owned by an external
|
|
24
|
-
* provisioner and whose `repo` is just an alias with no clone on disk — get the
|
|
25
|
-
* correct dir instead of a phantom path that breaks git access and leaves the
|
|
26
|
-
* real common dir unfenced. For native worktrees this returns the same
|
|
27
|
-
* `<projectDir>/<repo>/.git` as before. `--path-format=absolute` keeps the path
|
|
28
|
-
* absolute regardless of git version or cwd.
|
|
29
|
-
*/
|
|
30
|
-
export declare function resolveGitCommonDir(worktreeDir: string): string;
|
|
31
|
-
/**
|
|
32
|
-
* Generate the srt policies for a launch and stage them, plus — for agents that
|
|
33
|
-
* cannot run with a read-only config home (codex) — a relocated, writable
|
|
34
|
-
* config dir seeded with the minimal files the agent needs to authenticate and
|
|
35
|
-
* keep its config. Shared by `setupWorkspace` (fresh runs) and `resumeWorkspace`
|
|
36
|
-
* (resumes) so both behave identically under the srt runner.
|
|
37
|
-
*
|
|
38
|
-
* Two policies, distinct surfaces:
|
|
39
|
-
* - the `prepare` policy is profile-neutral (empty agent → no `~/.claude` /
|
|
40
|
-
* `~/.codex` grants, no relocated home) so the repo-controlled prepareWorktree
|
|
41
|
-
* hook can't touch the agent's credentials;
|
|
42
|
-
* - the `agent` policy carries the agent's read-only config profile and, when
|
|
43
|
-
* the agent relocates, the writable relocated home.
|
|
44
|
-
*
|
|
45
|
-
* The relocated home lives **inside** the settings dir but is the only thing
|
|
46
|
-
* under it granted to the sandbox — the settings JSON siblings are never
|
|
47
|
-
* read-granted, so the agent can't read or rewrite its own policy. The launch
|
|
48
|
-
* command tears the whole dir down after srt exits.
|
|
49
|
-
*/
|
|
50
|
-
export declare function buildAndStageSrtLaunch(input: {
|
|
51
|
-
task: string;
|
|
52
|
-
worktreeDir: string;
|
|
53
|
-
definition: AgentDefinition;
|
|
54
|
-
/** Local task-source directories the agent needs for self-completion writeback. */
|
|
55
|
-
taskSourceWritePaths?: readonly string[] | undefined;
|
|
56
|
-
/** Absolute host dirs re-opened read-only (`config.local.readOnlyDirs`). */
|
|
57
|
-
readOnlyDirs?: readonly string[] | undefined;
|
|
58
|
-
/** Defaults to `os.homedir()`. Injected in tests to seed from a fixture home. */
|
|
59
|
-
homeDir?: string;
|
|
60
|
-
}): StagedSrtLaunch;
|
|
61
|
-
//# sourceMappingURL=srtLaunch.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"srtLaunch.d.ts","sourceRoot":"","sources":["../../src/lib/srtLaunch.ts"],"names":[],"mappings":"AAOA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAKnD,MAAM,WAAW,eAAe;IAC9B,qFAAqF;IACrF,SAAS,EAAE,MAAM,CAAC;IAClB,kFAAkF;IAClF,WAAW,EAAE,MAAM,CAAC;IACpB,4CAA4C;IAC5C,SAAS,EAAE,MAAM,CAAC;IAClB;;;;OAIG;IACH,iBAAiB,CAAC,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CACrD;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAQ/D;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,sBAAsB,CAAC,KAAK,EAAE;IAC5C,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,eAAe,CAAC;IAC5B,mFAAmF;IACnF,oBAAoB,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IACrD,4EAA4E;IAC5E,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAC7C,iFAAiF;IACjF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB,GAAG,eAAe,CA+ClB"}
|
package/dist/lib/srtLaunch.js
DELETED
|
@@ -1,107 +0,0 @@
|
|
|
1
|
-
import { copyFileSync, existsSync, mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
|
|
2
|
-
import os from "node:os";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
import { clearanceAllowHostsFilesFromEnvironment } from "./clearanceAllowlist.js";
|
|
5
|
-
import { collectAllowedDomains } from "./clearanceHosts.js";
|
|
6
|
-
import { runCommand } from "./commandRunner.js";
|
|
7
|
-
import { inferAgentCommandName } from "./launchCommand.js";
|
|
8
|
-
import { agentConfigRelocation, buildSrtSettings } from "./srtPolicy.js";
|
|
9
|
-
import { readEnvironmentVariable } from "./util.js";
|
|
10
|
-
/**
|
|
11
|
-
* Resolve the worktree's real git common dir — the shared `.git` the srt policy
|
|
12
|
-
* fences off (read-grant + narrow write-allow + write-denies). Derived from the
|
|
13
|
-
* worktree itself rather than assuming a `<projectDir>/<repo>/.git` clone, so
|
|
14
|
-
* scripted/sparse-checkout worktrees — whose checkout is owned by an external
|
|
15
|
-
* provisioner and whose `repo` is just an alias with no clone on disk — get the
|
|
16
|
-
* correct dir instead of a phantom path that breaks git access and leaves the
|
|
17
|
-
* real common dir unfenced. For native worktrees this returns the same
|
|
18
|
-
* `<projectDir>/<repo>/.git` as before. `--path-format=absolute` keeps the path
|
|
19
|
-
* absolute regardless of git version or cwd.
|
|
20
|
-
*/
|
|
21
|
-
export function resolveGitCommonDir(worktreeDir) {
|
|
22
|
-
return runCommand("git", [
|
|
23
|
-
"-C",
|
|
24
|
-
worktreeDir,
|
|
25
|
-
"rev-parse",
|
|
26
|
-
"--path-format=absolute",
|
|
27
|
-
"--git-common-dir",
|
|
28
|
-
]);
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Generate the srt policies for a launch and stage them, plus — for agents that
|
|
32
|
-
* cannot run with a read-only config home (codex) — a relocated, writable
|
|
33
|
-
* config dir seeded with the minimal files the agent needs to authenticate and
|
|
34
|
-
* keep its config. Shared by `setupWorkspace` (fresh runs) and `resumeWorkspace`
|
|
35
|
-
* (resumes) so both behave identically under the srt runner.
|
|
36
|
-
*
|
|
37
|
-
* Two policies, distinct surfaces:
|
|
38
|
-
* - the `prepare` policy is profile-neutral (empty agent → no `~/.claude` /
|
|
39
|
-
* `~/.codex` grants, no relocated home) so the repo-controlled prepareWorktree
|
|
40
|
-
* hook can't touch the agent's credentials;
|
|
41
|
-
* - the `agent` policy carries the agent's read-only config profile and, when
|
|
42
|
-
* the agent relocates, the writable relocated home.
|
|
43
|
-
*
|
|
44
|
-
* The relocated home lives **inside** the settings dir but is the only thing
|
|
45
|
-
* under it granted to the sandbox — the settings JSON siblings are never
|
|
46
|
-
* read-granted, so the agent can't read or rewrite its own policy. The launch
|
|
47
|
-
* command tears the whole dir down after srt exits.
|
|
48
|
-
*/
|
|
49
|
-
export function buildAndStageSrtLaunch(input) {
|
|
50
|
-
const agent = inferAgentCommandName(input.definition.cmd);
|
|
51
|
-
const homeDir = input.homeDir ?? os.homedir();
|
|
52
|
-
const base = {
|
|
53
|
-
worktreeDir: input.worktreeDir,
|
|
54
|
-
gitCommonDir: resolveGitCommonDir(input.worktreeDir),
|
|
55
|
-
allowedDomains: collectAllowedDomains({
|
|
56
|
-
hosts: readEnvironmentVariable("CLEARANCE_ALLOW_HOSTS"),
|
|
57
|
-
files: clearanceAllowHostsFilesFromEnvironment(),
|
|
58
|
-
}),
|
|
59
|
-
readOnlyDirs: input.readOnlyDirs ?? [],
|
|
60
|
-
};
|
|
61
|
-
const directory = mkdtempSync(path.join(os.tmpdir(), `groundcrew-srt-${input.task}-`));
|
|
62
|
-
const relocation = agentConfigRelocation(agent);
|
|
63
|
-
let relocatedConfigDir;
|
|
64
|
-
let agentConfigDirEnv;
|
|
65
|
-
if (relocation !== undefined) {
|
|
66
|
-
relocatedConfigDir = path.join(directory, `${agent}-home`);
|
|
67
|
-
mkdirSync(relocatedConfigDir, { recursive: true });
|
|
68
|
-
seedRelocatedConfigDir({
|
|
69
|
-
sourceDir: path.join(homeDir, relocation.sourceHomeRelativeDir),
|
|
70
|
-
seedFiles: relocation.seedFiles,
|
|
71
|
-
relocatedConfigDir,
|
|
72
|
-
});
|
|
73
|
-
agentConfigDirEnv = { name: relocation.configDirEnv, value: relocatedConfigDir };
|
|
74
|
-
}
|
|
75
|
-
const prepare = buildSrtSettings({ ...base, agent: "" });
|
|
76
|
-
const agentSettings = buildSrtSettings({
|
|
77
|
-
...base,
|
|
78
|
-
agent,
|
|
79
|
-
taskSourceWritePaths: input.taskSourceWritePaths ?? [],
|
|
80
|
-
...(relocatedConfigDir === undefined ? {} : { relocatedConfigDir }),
|
|
81
|
-
});
|
|
82
|
-
const prepareFile = path.join(directory, "prepare-settings.json");
|
|
83
|
-
const agentFile = path.join(directory, "agent-settings.json");
|
|
84
|
-
writeFileSync(prepareFile, `${JSON.stringify(prepare, undefined, 2)}\n`);
|
|
85
|
-
writeFileSync(agentFile, `${JSON.stringify(agentSettings, undefined, 2)}\n`);
|
|
86
|
-
return {
|
|
87
|
-
directory,
|
|
88
|
-
prepareFile,
|
|
89
|
-
agentFile,
|
|
90
|
-
...(agentConfigDirEnv === undefined ? {} : { agentConfigDirEnv }),
|
|
91
|
-
};
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* Copy the agent's minimal credential/config files into the relocated home.
|
|
95
|
-
* Best-effort per file: a missing source (e.g. the user isn't logged into the
|
|
96
|
-
* agent, or has no config) is skipped rather than aborting the launch — the
|
|
97
|
-
* agent then reports its own "not logged in" state, which is the correct signal.
|
|
98
|
-
*/
|
|
99
|
-
function seedRelocatedConfigDir(input) {
|
|
100
|
-
for (const file of input.seedFiles) {
|
|
101
|
-
const source = path.join(input.sourceDir, file);
|
|
102
|
-
if (!existsSync(source)) {
|
|
103
|
-
continue;
|
|
104
|
-
}
|
|
105
|
-
copyFileSync(source, path.join(input.relocatedConfigDir, file));
|
|
106
|
-
}
|
|
107
|
-
}
|
package/dist/lib/srtPolicy.d.ts
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generate the srt (Anthropic sandbox-runtime) settings object for a single
|
|
3
|
-
* agent launch. srt is only a sandbox engine — it has no groundcrew- or
|
|
4
|
-
* agent-aware policy of its own — so this module owns the policy ergonomics
|
|
5
|
-
* that safehouse previously supplied via its bundled `.sb` profiles.
|
|
6
|
-
*
|
|
7
|
-
* The shape, in one place:
|
|
8
|
-
*
|
|
9
|
-
* - **Reads** start broad and are clamped: `denyRead` masks the whole home
|
|
10
|
-
* region (`/Users` on macOS, `/home` + `/root` on Linux) so the agent cannot
|
|
11
|
-
* read `~/.ssh`, `~/.aws`, shell history, or unrelated repos; `allowRead`
|
|
12
|
-
* then re-opens exactly the worktree, the repo's git metadata, the language
|
|
13
|
-
* toolchains needed to *run* the agent, the agent's own config/credential
|
|
14
|
-
* dirs, and — on macOS, for keychain-authenticated agents (claude) — the user
|
|
15
|
-
* keychain dir (`~/Library/Keychains`) so the agent can authenticate under
|
|
16
|
-
* the home mask. srt skips non-existent allow/deny paths, so listing a path
|
|
17
|
-
* that isn't present (a Linux keychain dir, an uninstalled toolchain) is
|
|
18
|
-
* harmless.
|
|
19
|
-
* - **Writes** are allow-only in srt. Two STAFF-1305 structural fixes shape it:
|
|
20
|
-
* 1. **Agent state (work item 1).** The host-CLI persistence vector (planting
|
|
21
|
-
* hooks, `mcpServers`, commands, plugins, … that execute on the user's
|
|
22
|
-
* next host run) is closed per agent. **claude** runs with a writable
|
|
23
|
-
* `~/.claude` (its Bash tool creates `session-env`/scratch state there) but
|
|
24
|
-
* every fixed-path executable/instruction surface — including
|
|
25
|
-
* `~/.claude.json` (`mcpServers`) and the bundled `chrome` binary — is
|
|
26
|
-
* denied; claude tolerates those write denials. **codex** hard-fails with a
|
|
27
|
-
* read-only home, so it is pointed at a relocated, per-launch writable
|
|
28
|
-
* config dir (`CODEX_HOME`, see {@link agentConfigRelocation}) and its real
|
|
29
|
-
* `~/.codex` is never write-granted at all. **cursor-agent** keeps a
|
|
30
|
-
* writable `~/.cursor` (chats/projects/resume) but denies mcp.json,
|
|
31
|
-
* hooks, rules, skills, plugins, and commands — the host-RCE surfaces.
|
|
32
|
-
* 2. **Git (work item 2).** The git common dir is granted write as a **narrow
|
|
33
|
-
* allowlist** of exactly the paths `status/diff/add/commit/push/gc` write —
|
|
34
|
-
* never wholesale — so the per-worktree gitdir redirection files, sibling
|
|
35
|
-
* worktree gitdirs, and the repo `config`/`hooks` stay unwritable.
|
|
36
|
-
* `denyWrite` is belt-and-suspenders over global toolchain bins and the agent
|
|
37
|
-
* homes; it uses **literal paths only** because bubblewrap silently ignores
|
|
38
|
-
* globs on Linux.
|
|
39
|
-
* - **Network** is allow-only and sourced from the existing clearance
|
|
40
|
-
* allowlist (see {@link ./clearanceHosts.ts}); local binding and unix sockets
|
|
41
|
-
* stay off (the docker socket and the DNS-exfil vector in srt#88).
|
|
42
|
-
*
|
|
43
|
-
* `allowPty` is on because the agent runs interactively under tmux;
|
|
44
|
-
* `allowGitConfig` stays off so the agent cannot rewrite `~/.gitconfig` or
|
|
45
|
-
* `.git/config` (both readable, just not writable).
|
|
46
|
-
*/
|
|
47
|
-
import { type SandboxRuntimeConfig } from "@anthropic-ai/sandbox-runtime";
|
|
48
|
-
export interface BuildSrtSettingsInput {
|
|
49
|
-
/** Absolute worktree directory the agent runs in (read + write). */
|
|
50
|
-
worktreeDir: string;
|
|
51
|
-
/**
|
|
52
|
-
* Absolute path to the repo's git common dir (the parent clone's `.git`).
|
|
53
|
-
* Granted read wholesale, but write only as a narrow allowlist of the paths
|
|
54
|
-
* the git workflow actually touches (see `GIT_COMMON_WRITE_PATHS`).
|
|
55
|
-
*/
|
|
56
|
-
gitCommonDir: string;
|
|
57
|
-
/** Agent identity (e.g. "claude", "codex") used to pick the credential profile. */
|
|
58
|
-
agent: string;
|
|
59
|
-
/** srt `network.allowedDomains`, already translated from the clearance allowlist. */
|
|
60
|
-
allowedDomains: readonly string[];
|
|
61
|
-
/**
|
|
62
|
-
* Absolute path to the agent's relocated, writable config/state home for this
|
|
63
|
-
* launch (codex's `CODEX_HOME`). When set it is granted read + write so the
|
|
64
|
-
* agent persists session state there instead of its real home, which stays
|
|
65
|
-
* read-only. claude does not relocate (its macOS keychain credential is bound
|
|
66
|
-
* to the default config dir), so this is omitted for it.
|
|
67
|
-
*/
|
|
68
|
-
relocatedConfigDir?: string;
|
|
69
|
-
/**
|
|
70
|
-
* Local task-source directories the worker needs for self-completion
|
|
71
|
-
* writeback, such as a todo-txt file's parent directory and task prompt dir.
|
|
72
|
-
*/
|
|
73
|
-
taskSourceWritePaths?: readonly string[];
|
|
74
|
-
/** Defaults to `process.platform`. Injected in tests to exercise both deny-read roots. */
|
|
75
|
-
platform?: NodeJS.Platform;
|
|
76
|
-
/** Defaults to `os.homedir()`. Injected in tests. */
|
|
77
|
-
homeDir?: string;
|
|
78
|
-
/** Defaults to `process.execPath`; used to locate the global node_modules to deny writes to. */
|
|
79
|
-
nodeExecPath?: string;
|
|
80
|
-
/**
|
|
81
|
-
* Absolute host dirs re-opened read-only from `config.local.readOnlyDirs`
|
|
82
|
-
* (toolchains the profile masks but doesn't re-open, e.g. tfenv). srt skips
|
|
83
|
-
* any that don't exist, so passing an absent path is harmless.
|
|
84
|
-
*/
|
|
85
|
-
readOnlyDirs?: readonly string[];
|
|
86
|
-
}
|
|
87
|
-
/**
|
|
88
|
-
* How an agent that cannot run with a read-only config home is pointed at a
|
|
89
|
-
* relocated, per-launch writable home instead. The launch stages a temp dir,
|
|
90
|
-
* seeds it with the minimal files the agent needs to authenticate + keep its
|
|
91
|
-
* config, exports `configDirEnv` to that dir, and grants it write — so the real
|
|
92
|
-
* home (which holds the persistence surfaces) is never written.
|
|
93
|
-
*
|
|
94
|
-
* Empirically (STAFF-1305 live validation, macOS): codex hard-fails to launch
|
|
95
|
-
* with a read-only `~/.codex` ("failed to initialize in-process app-server
|
|
96
|
-
* client") and authenticates from a file (`auth.json`), so relocating
|
|
97
|
-
* `CODEX_HOME` + seeding `auth.json`/`config.toml` both unblocks it and closes
|
|
98
|
-
* persistence. claude is deliberately absent: its macOS keychain credential is
|
|
99
|
-
* bound to the default config dir, so relocating `CLAUDE_CONFIG_DIR` breaks
|
|
100
|
-
* auth — claude instead runs with a writable home whose executable surfaces are
|
|
101
|
-
* denied (see `AGENT_SRT_PROFILES`).
|
|
102
|
-
*/
|
|
103
|
-
export interface AgentConfigRelocation {
|
|
104
|
-
/** Env var that points the agent at a relocated config home. */
|
|
105
|
-
configDirEnv: string;
|
|
106
|
-
/** Home-relative dir the seed files are copied from (the agent's real home). */
|
|
107
|
-
sourceHomeRelativeDir: string;
|
|
108
|
-
/** Files (relative to `sourceHomeRelativeDir`) seeded into the relocated home. */
|
|
109
|
-
seedFiles: readonly string[];
|
|
110
|
-
}
|
|
111
|
-
/**
|
|
112
|
-
* Return the config-relocation spec for an agent, or `undefined` when the agent
|
|
113
|
-
* runs with a writable real home + deny-list and does not relocate (claude,
|
|
114
|
-
* unknown agents).
|
|
115
|
-
*/
|
|
116
|
-
export declare function agentConfigRelocation(agent: string): AgentConfigRelocation | undefined;
|
|
117
|
-
export declare function buildSrtSettings(input: BuildSrtSettingsInput): SandboxRuntimeConfig;
|
|
118
|
-
//# sourceMappingURL=srtPolicy.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"srtPolicy.d.ts","sourceRoot":"","sources":["../../src/lib/srtPolicy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AAMH,OAAO,EAEL,KAAK,oBAAoB,EAC1B,MAAM,+BAA+B,CAAC;AAEvC,MAAM,WAAW,qBAAqB;IACpC,oEAAoE;IACpE,WAAW,EAAE,MAAM,CAAC;IACpB;;;;OAIG;IACH,YAAY,EAAE,MAAM,CAAC;IACrB,mFAAmF;IACnF,KAAK,EAAE,MAAM,CAAC;IACd,qFAAqF;IACrF,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAClC;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;OAGG;IACH,oBAAoB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,0FAA0F;IAC1F,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,qDAAqD;IACrD,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,gGAAgG;IAChG,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,YAAY,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;CAClC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,WAAW,qBAAqB;IACpC,gEAAgE;IAChE,YAAY,EAAE,MAAM,CAAC;IACrB,gFAAgF;IAChF,qBAAqB,EAAE,MAAM,CAAC;IAC9B,kFAAkF;IAClF,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9B;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,qBAAqB,GAAG,SAAS,CAEtF;AAgOD,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,oBAAoB,CA0HnF"}
|