@silvermoon-work/task-builder-cli 0.1.0-canary-261109-3 → 0.1.0-canary-261111-1

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/README.md CHANGED
@@ -1,150 +1,196 @@
1
1
  # @silvermoon-work/task-builder-cli
2
2
 
3
- Independent Commander 14 CLI and reusable command runner for
4
- [@silvermoon-work/task-builder](../task-builder/README.md).
5
- The core owns read-only tree transformations; this package owns argument parsing,
6
- help, version, output rendering, host paths, file writes and executable signals.
7
- It does not implement bundling or CAR encoding itself.
8
-
9
- The preceding release was `0.1.0-canary-261109-1`, depending on
10
- `task-builder@0.1.0-canary-261109-2`. CLI version `0.1.0-canary-261109-3`
11
- requires core `0.1.0-canary-261109-3` and generates the migrated SDK template.
12
- The older core package's binary is not
13
- this Commander CLI. See the
14
- [published-package verification guide](../../docs/end-to-end-verification.md).
3
+ Task production CLI and reusable command runner. Node >=22.13 is required.
4
+ This document describes the redesigned source interface; registry acceptance
5
+ requires separately publishing compatible Builder and CLI packages.
15
6
 
16
7
  ## Commands
17
8
 
18
9
  ```text
19
- task-builder init <new-directory> [--name <name>] [--cli-version <specifier>] [--builder-version <specifier>]
20
- task-builder build <task.build.yaml> [--root <directory>] [--trusted-contract]
21
- task-builder car <bundle-directory> <output.car> [--cid-file <output.cid>]
22
- task-builder check <bundle.car> [--expected-cid <cid>]
23
- ```
24
-
25
- The original `task-builder <task.build.yaml> [build options]` shorthand is
26
- Commander's default build command, not a second hand-written parser.
27
-
28
- ```text
10
+ task-builder init <directory> [--package-name <name>] [--task-name <name>]
11
+ task-builder build [build-file] [--out-dir <directory>] [--no-car]
12
+ task-builder car <task-file> [--output <file>]
29
13
  task-builder --help
30
14
  task-builder build --help
31
- task-builder help car
32
15
  task-builder --version
33
- task-builder check bundle.car --format text
34
- task-builder --format json check bundle.car
35
16
  ```
36
17
 
37
- Commander provides required arguments, allowed choices, option parsing and help.
38
- Duplicate options are rejected through Commander option events.
39
- `--format json|text` works before or after a subcommand. Results/errors default
40
- to JSON for compatibility with the original CLI; `--format text` selects
41
- human-readable summaries. Help/version remain text.
42
-
43
- Success result shapes are unchanged:
44
-
45
- | Command | JSON result |
46
- | --- | --- |
47
- | init | `{ directory, files, nextSteps }` |
48
- | build | `{ directory, files }` |
49
- | car | `{ car, cid, cidFile? }` |
50
- | check | `{ cid, declaration, bytes, files }` |
51
-
52
- Failures go to stderr, never success-shaped stdout. JSON errors use
53
- `{ error: { code, message } }`. Exit codes are 0 for success/help/version,
54
- 2 for Commander usage errors, 1 for operation errors, 130 for SIGINT and
55
- 143 for SIGTERM. There is no automatic install, overwrite, upload, pin or publish.
18
+ Only these three commands exist. No arguments prints help, without building.
19
+ There is no `help` command, implicit build, `check`, `--root`, `--format`,
20
+ `--trusted-contract`, dependency-version selection, CID sidecar, or business
21
+ field override. Unknown, extra and duplicate options/arguments fail.
22
+ No command prompts, reads stdin, installs dependencies, uploads or publishes.
23
+ TTY and redirected invocations have the same behavior.
24
+
25
+ Successful business commands write one newline-terminated
26
+ `{ "ok": true, "data": ... }` object to stdout. Failures write one
27
+ `{ "ok": false, "error": { "code": "...", "message": "..." } }` object to stdout;
28
+ `error` has exactly those two nonempty fields. Verified partial delivery can
29
+ also appear as `data` in that same failure object.
30
+ Stderr is reserved for CLI diagnostics and is empty by default. Help and root
31
+ version are text on stdout. Exit status is 0 for success/help/version, 1 for
32
+ ordinary failures. Confirmed cancellation and cleanup use 130 (SIGINT) or 143
33
+ (SIGTERM); an uncertain delivery or cleanup always uses 1.
56
34
 
57
35
  ## Init composition
58
36
 
59
- Core init returns a package-manager-neutral private project with Task sources,
60
- JSON contract, shared text Schema, greeting resource and a dependency on
61
- [@silvermoon-work/task-sdk](../task-sdk/README.md).
62
- The CLI adds:
63
-
64
- - build/CAR/check npm scripts referencing the `task-builder` binary;
65
- - direct dev dependencies on **both** CLI and core;
66
- - the pnpm version, scoped esbuild approval and release-age exceptions.
67
-
68
- The current template uses SDK `0.1.0-canary-261109-2`.
69
- The preceding CLI/core releases still generate the old SDK template; use
70
- CLI version 3 with core version 3 for this default.
71
- `createCliTaskProject({ sdkVersion: "file:task-sdk.tgz" })` can still
72
- select a local SDK tarball for development. SDK/protocol release-age exceptions
73
- now use `@silvermoon-work` names.
74
-
75
- Default CLI/core versions come from the installed package metadata, not a moving
76
- `canary` tag. `--cli-version` selects the CLI dependency. An explicit
77
- `--builder-version` also adds a scoped pnpm override so a local core tarball is
78
- used by the CLI's transitive dependency as well. This is useful before publication
79
- and avoids accidentally mixing the new CLI with the old published core.
80
- The override and scoped release-age exceptions live in `pnpm-workspace.yaml`,
81
- not the obsolete `package.json#pnpm` configuration. Exemptions use selected
82
- package names, so local `file:` specs are not incorrectly treated as npm versions.
83
-
84
- Init only writes into a new directory whose parent exists and is canonical.
85
- It does not merge with an existing project or modify an ancestor package.
86
- `--name` defaults to `task-project`. Additional Task template settings are
87
- available through the library's `createCliTaskProject` options, not new CLI flags.
88
-
89
- ## Reusable runner
90
-
91
- ```ts
92
- import { runTaskBuilderCli } from "@silvermoon-work/task-builder-cli";
37
+ The target must not exist and its parent must exist. Package name defaults to
38
+ the target directory basename; invalid names require `--package-name` rather
39
+ than silent sanitization. Task name defaults to the package's unscoped name.
40
+ The editable template includes:
41
+
42
+ - `package.json`, `.gitignore`, `pnpm-workspace.yaml`;
43
+ - `task.build.yaml`, explicit `src/main.ts` and `src/helper.ts`;
44
+ - a TypeBox `src/contract.ts` with a shared Schema and `assets/greeting.txt`;
45
+ - Node minimum 22.13.0, `assets/**` → `resources/`, no binary rules;
46
+ - build, standalone CAR and explicit `clean` scripts; no dependency installation.
47
+
48
+ The contract exports `Input` and `Result` via `Static<typeof ...>`; the Worker
49
+ imports those types with `import type` rather than repeating the data shapes.
50
+ The shared nonempty text reference retains its string type, and the runtime
51
+ Schemas still reject extra properties and enforce integer byte/chunk counts
52
+ with minima 0/1. Messages remain disabled. The generated package explicitly
53
+ depends on `@sinclair/typebox@0.34.52`, not Builder's transitive dependencies.
54
+ Build evaluates this TS contract as trusted code; esbuild transpiles but does
55
+ not typecheck it. JSON contracts remain supported when selected in the build
56
+ description.
57
+
58
+ Init fixes `outDirectory` to `dist/<derived-name>` using the final package name,
59
+ not the Task name: `echo-task` → `dist/echo-task`, `@acme/echo-task` →
60
+ `dist/acme-echo-task`. Only the leading `@` is removed and the scope separator
61
+ `/` becomes `-`. Invalid npm names and unsafe derived paths (such as Windows
62
+ device names or trailing dots) are rejected, never silently sanitized.
63
+ Later package-name changes do not rewrite the build description or scripts.
64
+
65
+ `pnpm build` writes that module and its sibling `dist/<derived-name>.car`.
66
+ `pnpm car` reads `dist/<derived-name>/task.yaml` and targets the same CAR,
67
+ refusing to overwrite an existing archive, including one produced by build.
68
+ Use `pnpm build --no-car` before `pnpm car` when no archive already exists.
69
+ `pnpm clean` runs `rimraf dist`, deleting all modules and CARs under `dist`;
70
+ rimraf 6.1.4 is a generated devDependency compatible with Node >=22.13.0.
71
+ Build never runs clean automatically: validation still precedes delivery,
72
+ and stale unselected files remain until explicitly cleaned.
73
+
74
+ CLI and Builder dependency versions come from their actual installed metadata;
75
+ the SDK version comes from that Builder release's template. They need not
76
+ match. No remote tag lookup or version override is performed.
77
+ The pnpm template allows only esbuild's postinstall (`allowBuilds.esbuild: true`);
78
+ no other dependency build scripts are approved. Do not omit optional dependencies.
79
+
80
+ ## Build and delivery
81
+
82
+ `build-file` defaults to `task.build.yaml`. Source, contract, Task identity,
83
+ runtime and resource rules come only from that file, which is not rewritten.
84
+ Output precedence is `--out-dir` > `outDirectory` > description-directory `dist`.
85
+ CLI paths are relative to cwd; description paths are relative to the description.
86
+ Within pnpm workspaces, the existing workspace tree supplies installed
87
+ dependencies; imports never fall back to the CLI's dependencies.
88
+
89
+ The default CAR is the module directory's sibling `<directory-basename>.car`.
90
+ Both the module and CAR remain after success. `--no-car` does not update or
91
+ delete an older CAR: `data.car: null` describes this invocation only.
92
+
93
+ ```json
94
+ {
95
+ "ok": true,
96
+ "data": {
97
+ "module": {
98
+ "directory": "<absolute module directory>",
99
+ "declaration": "<absolute task.yaml>",
100
+ "files": ["task.yaml", "worker.cjs", "schemas/input.json", "schemas/result.json", "schemas/message.json"]
101
+ },
102
+ "cid": "<verified module CID>",
103
+ "car": { "path": "<absolute CAR>" }
104
+ }
105
+ }
106
+ ```
93
107
 
94
- const exitCode = await runTaskBuilderCli(
95
- ["build", "task.build.yaml", "--format", "json"],
96
- {
97
- cwd: projectDirectory,
98
- signal,
99
- stdout: { write: (text) => output.write(text) },
100
- stderr: { write: (text) => diagnostics.write(text) },
101
- },
102
- );
108
+ Files list exactly the current declaration's selection, including any selected
109
+ resources/shared Schemas, using protocol `/` paths. Other paths are host
110
+ absolute paths. Old unselected files stay on disk but are excluded from
111
+ validation and CAR. No output directory is recursively cleared.
112
+
113
+ All generated bytes are staged and validated before replacing any outputs.
114
+ Each file replacement uses complete sibling bytes rather than truncating the
115
+ old file. Delivered module bytes and CAR bytes are validated again.
116
+ A failure during module updates can leave some replaced files and reports
117
+ `DELIVERY_UNCERTAIN`, not a claimed rollback or a trusted module. A valid new
118
+ module survives CAR failure, reported with its module/CID, `car: null` and an
119
+ explanation that any unreplaced old CAR is not a new delivery.
120
+ Staging belongs only to the invocation and is released on completion/failure.
121
+ Input/output overlap, observable destination races and linked output paths are
122
+ rejected. Filesystem checks are not an OS sandbox against a malicious author.
123
+
124
+ JS/TS contracts execute automatically as trusted Node code, in a short-lived
125
+ process with captured standard streams and a 30-second internal evaluation
126
+ budget. Console calls and direct stdout/stderr writes, including caught or
127
+ deferred attempts and evaluating dependencies, fail with
128
+ `CONTRACT_OUTPUT_FORBIDDEN`; output is never forwarded or silently accepted.
129
+ JSON contracts do not execute. This is **not a security sandbox**; other host
130
+ side effects remain possible. Task runtime logging is unaffected.
131
+
132
+ ## Standalone CAR
133
+
134
+ `car` accepts an existing `task.yaml` file, never a directory. Only its declared
135
+ files are read and validated; unselected notes, stale files and archives are
136
+ not read. It never reads the build description, compiles, runs contracts or
137
+ installs packages. Links cannot escape the module tree, and reads detect
138
+ observable input changes.
139
+ The input module directory and all its ancestors must be canonical, without
140
+ symlinks or Windows junctions. Linked module/ancestor aliases are rejected
141
+ before choosing or writing an output, so they cannot disguise an internal CAR
142
+ destination as an external one.
143
+
144
+ The default destination uses the same sibling naming rule as build; `--output`
145
+ is cwd-relative and must be outside the input module. Existing destinations
146
+ are always refused, including during competing creation.
147
+
148
+ ```json
149
+ {
150
+ "ok": true,
151
+ "data": {
152
+ "car": "<absolute CAR>",
153
+ "cid": "<verified CID>",
154
+ "declaration": "<absolute input task.yaml>",
155
+ "files": ["<complete declared selection>"]
156
+ }
157
+ }
103
158
  ```
104
159
 
105
- Each invocation has its own Commander instance and returns an exit code.
106
- Importing the library does not run a command, change `process.exitCode`, install
107
- signal handlers or start a service. Programmatic abort returns 130; only `bin.js`
108
- owns process SIGINT/SIGTERM and their corresponding executable exit codes.
109
- `createCliTaskProject(options)` can be used independently to decorate a template
110
- without writing it to disk or installing packages.
160
+ Use the compiler-free `@silvermoon-work/task-builder/car` subpath for standalone
161
+ CAR inspection or the Engine CLI for actual consumption, not a removed
162
+ `task-builder check` command.
111
163
 
112
- ## Node adapters
164
+ ## Reusable runner and adapters
113
165
 
114
166
  ```ts
115
- import { buildTaskBundle } from "@silvermoon-work/task-builder";
116
- import {
117
- directoryTree,
118
- writeDirectory,
119
- writeStreamFile,
120
- } from "@silvermoon-work/task-builder-cli/node";
121
-
122
- const input = await directoryTree(projectDirectory);
123
- const output = await buildTaskBundle(input, options);
124
- await writeDirectory(output, newOutputDirectory);
167
+ import { runTaskBuilderCli } from "@silvermoon-work/task-builder-cli";
168
+
169
+ const exitCode = await runTaskBuilderCli(["build", "task.build.yaml"], {
170
+ cwd: projectDirectory,
171
+ signal,
172
+ stdout: { write: (text) => output.write(text) },
173
+ });
125
174
  ```
126
175
 
127
- The directory adapter is a live view, not an atomic snapshot or OS sandbox.
128
- Confined links support pnpm's virtual store; escaping links and special files
129
- are rejected. Writes use backpressure, cancellation and exclusive file creation.
130
- Only newly created files/directories are removed on failure; existing output
131
- is never overwritten. CAR and CID paths cannot be inside the Bundle directory
132
- or equal each other. A failed CID sidecar write removes the newly created CAR.
176
+ The runner does not exit the host, read stdin or install signal handlers.
177
+ Programmatic cancellation defaults to 130; executable signal ownership belongs
178
+ to `bin.js`. `createCliTaskProject({ name, taskName })` creates a template without
179
+ writing files. `./node` exports `directoryTree`, `writeDirectory` and
180
+ `writeStreamFile`: these lower-level writers retain exclusive-create semantics.
181
+ The CLI's build delivery layer implements replacement separately.
133
182
 
134
- ## Repository development
183
+ ## Development and distribution
135
184
 
136
185
  ```text
137
186
  pnpm --filter @silvermoon-work/task-builder-cli... build
138
- node packages/task-builder-cli/dist/bin.js --help
139
- node packages/task-builder-cli/dist/bin.js build packages/task-builder/examples/echo/task.build.yaml
140
- pnpm --filter @silvermoon-work/task-builder-cli test
187
+ pnpm --filter @silvermoon-work/task-builder --filter @silvermoon-work/task-builder-cli test
141
188
  pnpm check
142
189
  ```
143
190
 
144
- The example build fails explicitly if its output already exists.
145
- CLI and core are separately mapped publishable libraries. Publish the exact
146
- core dependency before tagging this CLI; this package needs its own npm package
147
- identity and Trusted Publisher configuration. No release is performed by build.
148
- The modified core must receive a new version first: do not publish this CLI
149
- against the old `task-builder@0.1.0-canary-261109-1`, which lacks `./tree` and
150
- the current init/CAR APIs.
191
+ Published packages contain precompiled `dist`, including the contract evaluation
192
+ process entrypoint. CLI bin is `dist/bin.js`; consumers do not compile the CLI.
193
+ Builder and CLI are separate releases. Required internal versions must already
194
+ exist in the configured registry; never auto-publish dependencies. The
195
+ [consumer guide](../../docs/end-to-end-verification.md) distinguishes local
196
+ development from an actual registry-installed Canary verification.
package/dist/bin.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { runTaskBuilderCli } from "./index.js";
3
3
  const controller = new AbortController();
4
- let interrupted;
4
+ let interrupted = 130;
5
5
  const interrupt = () => {
6
6
  interrupted = 130;
7
7
  controller.abort(new Error("Task builder interrupted by SIGINT."));
@@ -13,8 +13,10 @@ const terminate = () => {
13
13
  process.once("SIGINT", interrupt);
14
14
  process.once("SIGTERM", terminate);
15
15
  try {
16
- const code = await runTaskBuilderCli(process.argv.slice(2), { signal: controller.signal });
17
- process.exitCode = interrupted ?? code;
16
+ process.exitCode = await runTaskBuilderCli(process.argv.slice(2), {
17
+ signal: controller.signal,
18
+ signalExitCode: () => interrupted,
19
+ });
18
20
  }
19
21
  finally {
20
22
  process.removeListener("SIGINT", interrupt);
package/dist/bin.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE/C,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;AACzC,IAAI,WAA+B,CAAC;AACpC,MAAM,SAAS,GAAG,GAAG,EAAE;IACrB,WAAW,GAAG,GAAG,CAAC;IAClB,UAAU,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC,CAAC;AACrE,CAAC,CAAC;AACF,MAAM,SAAS,GAAG,GAAG,EAAE;IACrB,WAAW,GAAG,GAAG,CAAC;IAClB,UAAU,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC,CAAC;AACtE,CAAC,CAAC;AACF,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;AAClC,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;AACnC,IAAI,CAAC;IACH,MAAM,IAAI,GAAG,MAAM,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC;IAC3F,OAAO,CAAC,QAAQ,GAAG,WAAW,IAAI,IAAI,CAAC;AACzC,CAAC;QAAS,CAAC;IACT,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC5C,OAAO,CAAC,cAAc,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;AAC/C,CAAC"}
1
+ {"version":3,"file":"bin.js","sourceRoot":"","sources":["../src/bin.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AAE/C,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;AACzC,IAAI,WAAW,GAAc,GAAG,CAAC;AACjC,MAAM,SAAS,GAAG,GAAG,EAAE;IACrB,WAAW,GAAG,GAAG,CAAC;IAClB,UAAU,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC,CAAC;AACrE,CAAC,CAAC;AACF,MAAM,SAAS,GAAG,GAAG,EAAE;IACrB,WAAW,GAAG,GAAG,CAAC;IAClB,UAAU,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,sCAAsC,CAAC,CAAC,CAAC;AACtE,CAAC,CAAC;AACF,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;AAClC,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;AACnC,IAAI,CAAC;IACH,OAAO,CAAC,QAAQ,GAAG,MAAM,iBAAiB,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;QAChE,MAAM,EAAE,UAAU,CAAC,MAAM;QACzB,cAAc,EAAE,GAAG,EAAE,CAAC,WAAW;KAClC,CAAC,CAAC;AACL,CAAC;QAAS,CAAC;IACT,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC5C,OAAO,CAAC,cAAc,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,28 @@
1
+ import { type ReadonlyFileTree, type ReadOptions } from "@silvermoon-work/task-builder/tree";
2
+ export declare class CliError extends Error {
3
+ readonly code: string;
4
+ readonly data?: object | undefined;
5
+ constructor(code: string, message: string, data?: object | undefined);
6
+ }
7
+ export declare function inside(root: string, path: string): boolean;
8
+ export declare function defaultCar(directory: string): string;
9
+ export declare function info(path: string): Promise<import("fs").BigIntStats | undefined>;
10
+ /** Reject links in every existing component, including links inside output directories. */
11
+ export declare function safePath(path: string): Promise<void>;
12
+ export declare function selectedModule(directory: string, options?: ReadOptions): Promise<{
13
+ tree: ReadonlyFileTree;
14
+ files: string[];
15
+ }>;
16
+ export declare function verifyModule(directory: string, options?: ReadOptions): Promise<{
17
+ archive: import("@silvermoon-work/task-builder/car").EncodedTaskBundleCar;
18
+ tree: ReadonlyFileTree;
19
+ files: string[];
20
+ }>;
21
+ /** Complete and validate a sibling before atomic replacement; never truncate an old target. */
22
+ export declare function deliverFile(stream: ReadableStream<Uint8Array>, destination: string, options: ReadOptions & {
23
+ replace: boolean;
24
+ cid?: string;
25
+ }): Promise<void>;
26
+ export declare function installModule(tree: ReadonlyFileTree, files: readonly string[], directory: string, options: ReadOptions): Promise<void>;
27
+ export declare function cleanup(directory: string): Promise<void>;
28
+ //# sourceMappingURL=delivery.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delivery.d.ts","sourceRoot":"","sources":["../src/delivery.ts"],"names":[],"mappings":"AAIA,OAAO,EAGL,KAAK,gBAAgB,EACrB,KAAK,WAAW,EACjB,MAAM,oCAAoC,CAAC;AAI5C,qBAAa,QAAS,SAAQ,KAAK;IAE/B,QAAQ,CAAC,IAAI,EAAE,MAAM;IAErB,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM;gBAFb,IAAI,EAAE,MAAM,EACrB,OAAO,EAAE,MAAM,EACN,IAAI,CAAC,EAAE,MAAM,YAAA;CAIzB;AAED,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAK1D;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAQpD;AAED,wBAAsB,IAAI,CAAC,IAAI,EAAE,MAAM,iDAOtC;AAED,2FAA2F;AAC3F,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAQ1D;AAED,wBAAsB,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB;;;GAyBhF;AAED,wBAAsB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,GAAE,WAAgB;;;;GAI9E;AAED,+FAA+F;AAC/F,wBAAsB,WAAW,CAC/B,MAAM,EAAE,cAAc,CAAC,UAAU,CAAC,EAClC,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,WAAW,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,GACxD,OAAO,CAAC,IAAI,CAAC,CA0Df;AAED,wBAAsB,aAAa,CACjC,IAAI,EAAE,gBAAgB,EACtB,KAAK,EAAE,SAAS,MAAM,EAAE,EACxB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,WAAW,iBAUrB;AAED,wBAAsB,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAS9D"}
@@ -0,0 +1,146 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { lstat, mkdir, realpath, rename, rm, unlink } from "node:fs/promises";
3
+ import { basename, dirname, join, relative, resolve, sep } from "node:path";
4
+ import { encodeTaskBundleCar, readTaskBundleCar } from "@silvermoon-work/task-builder/car";
5
+ import { fileTree, readBytes, } from "@silvermoon-work/task-builder/tree";
6
+ import { parseTaskBundleYaml, validateTaskBundle } from "@silvermoon-work/task-protocol";
7
+ import { directoryTree, writeStreamFile } from "./node.js";
8
+ export class CliError extends Error {
9
+ code;
10
+ data;
11
+ constructor(code, message, data) {
12
+ super(message);
13
+ this.code = code;
14
+ this.data = data;
15
+ }
16
+ }
17
+ export function inside(root, path) {
18
+ const distance = relative(root, path);
19
+ return (!distance || (distance !== ".." && !distance.startsWith(`..${sep}`) && !distance.includes(":")));
20
+ }
21
+ export function defaultCar(directory) {
22
+ const name = basename(directory);
23
+ if (!name || dirname(directory) === directory)
24
+ throw new CliError("INVALID_ARGUMENT", "Cannot derive a CAR name from the module directory; choose another directory or an explicit car --output.");
25
+ return join(dirname(directory), `${name}.car`);
26
+ }
27
+ export async function info(path) {
28
+ try {
29
+ return await lstat(path, { bigint: true });
30
+ }
31
+ catch (error) {
32
+ if (error instanceof Error && "code" in error && error.code === "ENOENT")
33
+ return undefined;
34
+ throw error;
35
+ }
36
+ }
37
+ /** Reject links in every existing component, including links inside output directories. */
38
+ export async function safePath(path) {
39
+ const parent = dirname(path);
40
+ if (parent !== path)
41
+ await safePath(parent);
42
+ const value = await info(path);
43
+ if (value?.isSymbolicLink())
44
+ throw new CliError("UNSAFE_PATH", "Paths must not contain symbolic links.");
45
+ if (value && (await realpath(path)).toLowerCase() !== resolve(path).toLowerCase())
46
+ throw new CliError("UNSAFE_PATH", "Paths must be canonical.");
47
+ }
48
+ export async function selectedModule(directory, options = {}) {
49
+ const input = await directoryTree(directory);
50
+ const bytes = await readBytes(input, "task.yaml", 65_536, options);
51
+ const declaration = validateTaskBundle(parseTaskBundleYaml(bytes));
52
+ const files = [...declaration.include];
53
+ const tree = fileTree(new Map(files.map((file) => [
54
+ file,
55
+ async (readOptions) => {
56
+ try {
57
+ return await input.read(file, readOptions);
58
+ }
59
+ catch (error) {
60
+ if (error instanceof Error && "code" in error && error.code === "ENOENT")
61
+ throw new CliError("MODULE_FILE_MISSING", `Declared module file is missing: ${file}. No CAR was delivered.`);
62
+ throw error;
63
+ }
64
+ },
65
+ ])));
66
+ return { tree, files };
67
+ }
68
+ export async function verifyModule(directory, options = {}) {
69
+ const selected = await selectedModule(directory, options);
70
+ const archive = await encodeTaskBundleCar(selected.tree, options);
71
+ return { ...selected, archive };
72
+ }
73
+ /** Complete and validate a sibling before atomic replacement; never truncate an old target. */
74
+ export async function deliverFile(stream, destination, options) {
75
+ const temporary = join(dirname(destination), `.task-builder-${randomUUID()}`);
76
+ let owned = false;
77
+ let installed = false;
78
+ try {
79
+ await safePath(destination);
80
+ const previous = await info(destination);
81
+ if (previous && !options.replace)
82
+ throw new CliError("OUTPUT_EXISTS", "Output already exists; nothing was overwritten.");
83
+ if (previous && !previous.isFile())
84
+ throw new CliError("INVALID_ARGUMENT", "Output must be a regular file.");
85
+ await writeStreamFile(stream, temporary, options);
86
+ owned = true;
87
+ if (options.cid) {
88
+ const tree = await directoryTree(dirname(temporary));
89
+ const archive = await readTaskBundleCar(await tree.read(basename(temporary), options), {
90
+ ...options,
91
+ expectedCid: options.cid,
92
+ });
93
+ await archive.close();
94
+ }
95
+ options.signal?.throwIfAborted();
96
+ await safePath(destination);
97
+ const current = await info(destination);
98
+ if (current?.ino !== previous?.ino ||
99
+ current?.dev !== previous?.dev ||
100
+ current?.mtimeNs !== previous?.mtimeNs)
101
+ throw new CliError("OUTPUT_CHANGED", "Output changed during delivery; it was not replaced.");
102
+ if (options.replace)
103
+ await rename(temporary, destination);
104
+ else {
105
+ // Exclusive creation remains mandatory even if another producer races this invocation.
106
+ const { link } = await import("node:fs/promises");
107
+ await link(temporary, destination);
108
+ installed = true;
109
+ await unlink(temporary);
110
+ }
111
+ owned = false;
112
+ }
113
+ catch (error) {
114
+ await stream.cancel().catch(() => undefined);
115
+ if (owned) {
116
+ try {
117
+ await unlink(temporary);
118
+ }
119
+ catch {
120
+ throw new CliError("DELIVERY_UNCERTAIN", "Delivery failed and temporary-file cleanup could not be confirmed.");
121
+ }
122
+ }
123
+ if (installed)
124
+ throw new CliError("DELIVERY_UNCERTAIN", "CAR was installed but delivery cleanup reported an error; verify the destination before retrying.");
125
+ throw error;
126
+ }
127
+ }
128
+ export async function installModule(tree, files, directory, options) {
129
+ await safePath(directory);
130
+ await mkdir(directory, { recursive: true });
131
+ for (const file of files) {
132
+ const target = join(directory, ...file.split("/"));
133
+ await safePath(target);
134
+ await mkdir(dirname(target), { recursive: true });
135
+ await deliverFile(await tree.read(file, options), target, { ...options, replace: true });
136
+ }
137
+ }
138
+ export async function cleanup(directory) {
139
+ try {
140
+ await rm(directory, { recursive: true, force: true });
141
+ }
142
+ catch {
143
+ throw new CliError("CLEANUP_UNCERTAIN", "Operation-owned staging cleanup could not be confirmed.");
144
+ }
145
+ }
146
+ //# sourceMappingURL=delivery.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"delivery.js","sourceRoot":"","sources":["../src/delivery.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAC9E,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,WAAW,CAAC;AAC5E,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,mCAAmC,CAAC;AAC3F,OAAO,EACL,QAAQ,EACR,SAAS,GAGV,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,mBAAmB,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACzF,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC;AAE3D,MAAM,OAAO,QAAS,SAAQ,KAAK;IAEtB;IAEA;IAHX,YACW,IAAY,EACrB,OAAe,EACN,IAAa;QAEtB,KAAK,CAAC,OAAO,CAAC,CAAC;QAJN,SAAI,GAAJ,IAAI,CAAQ;QAEZ,SAAI,GAAJ,IAAI,CAAS;IAGxB,CAAC;CACF;AAED,MAAM,UAAU,MAAM,CAAC,IAAY,EAAE,IAAY;IAC/C,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACtC,OAAO,CACL,CAAC,QAAQ,IAAI,CAAC,QAAQ,KAAK,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,KAAK,GAAG,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAChG,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,SAAiB;IAC1C,MAAM,IAAI,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC;IACjC,IAAI,CAAC,IAAI,IAAI,OAAO,CAAC,SAAS,CAAC,KAAK,SAAS;QAC3C,MAAM,IAAI,QAAQ,CAChB,kBAAkB,EAClB,2GAA2G,CAC5G,CAAC;IACJ,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,GAAG,IAAI,MAAM,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,IAAY;IACrC,IAAI,CAAC;QACH,OAAO,MAAM,KAAK,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAC7C,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QAC3F,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,2FAA2F;AAC3F,MAAM,CAAC,KAAK,UAAU,QAAQ,CAAC,IAAY;IACzC,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7B,IAAI,MAAM,KAAK,IAAI;QAAE,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC5C,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,KAAK,EAAE,cAAc,EAAE;QACzB,MAAM,IAAI,QAAQ,CAAC,aAAa,EAAE,wCAAwC,CAAC,CAAC;IAC9E,IAAI,KAAK,IAAI,CAAC,MAAM,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,WAAW,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC,WAAW,EAAE;QAC/E,MAAM,IAAI,QAAQ,CAAC,aAAa,EAAE,0BAA0B,CAAC,CAAC;AAClE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,SAAiB,EAAE,UAAuB,EAAE;IAC/E,MAAM,KAAK,GAAG,MAAM,aAAa,CAAC,SAAS,CAAC,CAAC;IAC7C,MAAM,KAAK,GAAG,MAAM,SAAS,CAAC,KAAK,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IACnE,MAAM,WAAW,GAAG,kBAAkB,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC;IACnE,MAAM,KAAK,GAAG,CAAC,GAAG,WAAW,CAAC,OAAO,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,QAAQ,CACnB,IAAI,GAAG,CACL,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QAClB,IAAI;QACJ,KAAK,EAAE,WAAwB,EAAE,EAAE;YACjC,IAAI,CAAC;gBACH,OAAO,MAAM,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,WAAW,CAAC,CAAC;YAC7C,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,KAAK,YAAY,KAAK,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ;oBACtE,MAAM,IAAI,QAAQ,CAChB,qBAAqB,EACrB,oCAAoC,IAAI,yBAAyB,CAClE,CAAC;gBACJ,MAAM,KAAK,CAAC;YACd,CAAC;QACH,CAAC;KACF,CAAC,CACH,CACF,CAAC;IACF,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC;AACzB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,SAAiB,EAAE,UAAuB,EAAE;IAC7E,MAAM,QAAQ,GAAG,MAAM,cAAc,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IAC1D,MAAM,OAAO,GAAG,MAAM,mBAAmB,CAAC,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAClE,OAAO,EAAE,GAAG,QAAQ,EAAE,OAAO,EAAE,CAAC;AAClC,CAAC;AAED,+FAA+F;AAC/F,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,MAAkC,EAClC,WAAmB,EACnB,OAAyD;IAEzD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,EAAE,iBAAiB,UAAU,EAAE,EAAE,CAAC,CAAC;IAC9E,IAAI,KAAK,GAAG,KAAK,CAAC;IAClB,IAAI,SAAS,GAAG,KAAK,CAAC;IACtB,IAAI,CAAC;QACH,MAAM,QAAQ,CAAC,WAAW,CAAC,CAAC;QAC5B,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC;QACzC,IAAI,QAAQ,IAAI,CAAC,OAAO,CAAC,OAAO;YAC9B,MAAM,IAAI,QAAQ,CAAC,eAAe,EAAE,iDAAiD,CAAC,CAAC;QACzF,IAAI,QAAQ,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE;YAChC,MAAM,IAAI,QAAQ,CAAC,kBAAkB,EAAE,gCAAgC,CAAC,CAAC;QAC3E,MAAM,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,CAAC,CAAC;QAClD,KAAK,GAAG,IAAI,CAAC;QACb,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;YAChB,MAAM,IAAI,GAAG,MAAM,aAAa,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC;YACrD,MAAM,OAAO,GAAG,MAAM,iBAAiB,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,CAAC,EAAE;gBACrF,GAAG,OAAO;gBACV,WAAW,EAAE,OAAO,CAAC,GAAG;aACzB,CAAC,CAAC;YACH,MAAM,OAAO,CAAC,KAAK,EAAE,CAAC;QACxB,CAAC;QACD,OAAO,CAAC,MAAM,EAAE,cAAc,EAAE,CAAC;QACjC,MAAM,QAAQ,CAAC,WAAW,CAAC,CAAC;QAC5B,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,CAAC;QACxC,IACE,OAAO,EAAE,GAAG,KAAK,QAAQ,EAAE,GAAG;YAC9B,OAAO,EAAE,GAAG,KAAK,QAAQ,EAAE,GAAG;YAC9B,OAAO,EAAE,OAAO,KAAK,QAAQ,EAAE,OAAO;YAEtC,MAAM,IAAI,QAAQ,CAAC,gBAAgB,EAAE,sDAAsD,CAAC,CAAC;QAC/F,IAAI,OAAO,CAAC,OAAO;YAAE,MAAM,MAAM,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;aACrD,CAAC;YACJ,uFAAuF;YACvF,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,kBAAkB,CAAC,CAAC;YAClD,MAAM,IAAI,CAAC,SAAS,EAAE,WAAW,CAAC,CAAC;YACnC,SAAS,GAAG,IAAI,CAAC;YACjB,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;QAC1B,CAAC;QACD,KAAK,GAAG,KAAK,CAAC;IAChB,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,MAAM,MAAM,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC;QAC7C,IAAI,KAAK,EAAE,CAAC;YACV,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;YAC1B,CAAC;YAAC,MAAM,CAAC;gBACP,MAAM,IAAI,QAAQ,CAChB,oBAAoB,EACpB,oEAAoE,CACrE,CAAC;YACJ,CAAC;QACH,CAAC;QACD,IAAI,SAAS;YACX,MAAM,IAAI,QAAQ,CAChB,oBAAoB,EACpB,mGAAmG,CACpG,CAAC;QACJ,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,IAAsB,EACtB,KAAwB,EACxB,SAAiB,EACjB,OAAoB;IAEpB,MAAM,QAAQ,CAAC,SAAS,CAAC,CAAC;IAC1B,MAAM,KAAK,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC5C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,IAAI,CAAC,SAAS,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;QACnD,MAAM,QAAQ,CAAC,MAAM,CAAC,CAAC;QACvB,MAAM,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAClD,MAAM,WAAW,CAAC,MAAM,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3F,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,SAAiB;IAC7C,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,SAAS,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACxD,CAAC;IAAC,MAAM,CAAC;QACP,MAAM,IAAI,QAAQ,CAChB,mBAAmB,EACnB,yDAAyD,CAC1D,CAAC;IACJ,CAAC;AACH,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type ReadOptions } from "@silvermoon-work/task-builder/tree";
1
+ import type { ReadOptions } from "@silvermoon-work/task-builder/tree";
2
2
  export { createCliTaskProject, type CliTaskProjectOptions } from "./project.js";
3
3
  export interface CliWriter {
4
4
  write(text: string): void | boolean;
@@ -7,7 +7,8 @@ export interface TaskBuilderCliOptions extends ReadOptions {
7
7
  readonly cwd?: string;
8
8
  readonly stdout?: CliWriter;
9
9
  readonly stderr?: CliWriter;
10
+ readonly signalExitCode?: () => 130 | 143;
10
11
  }
11
- /** Run one invocation without starting listeners, installing signal handlers or exiting the process. */
12
+ /** One noninteractive invocation: one JSON result, no process exit or signal listeners. */
12
13
  export declare function runTaskBuilderCli(argv: readonly string[], options?: TaskBuilderCliOptions): Promise<number>;
13
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAc,KAAK,WAAW,EAAE,MAAM,oCAAoC,CAAC;AAIlF,OAAO,EAAE,oBAAoB,EAAE,KAAK,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAEhF,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC;CACrC;AACD,MAAM,WAAW,qBAAsB,SAAQ,WAAW;IACxD,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;CAC7B;AAuBD,wGAAwG;AACxG,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,MAAM,CAAC,CAsNjB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAoB,MAAM,oCAAoC,CAAC;AAexF,OAAO,EAAE,oBAAoB,EAAE,KAAK,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAEhF,MAAM,WAAW,SAAS;IACxB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC;CACrC;AACD,MAAM,WAAW,qBAAsB,SAAQ,WAAW;IACxD,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;IAC5B,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;IAC5B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,GAAG,GAAG,CAAC;CAC3C;AAkOD,2FAA2F;AAC3F,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,MAAM,CAAC,CAgKjB"}