agent-first-data 0.17.3 → 0.19.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/README.md +5 -5
- package/assets/skills/agent-first-data/SKILL.md +160 -14
- package/assets/skills/agent-first-data/references/rules.md +36 -26
- package/dist/cli.d.ts +125 -14
- package/dist/cli.js +179 -31
- package/dist/format.d.ts +44 -17
- package/dist/format.js +146 -135
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/skill.d.ts +3 -3
- package/dist/skill.js +2 -2
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@ npm install agent-first-data
|
|
|
5
5
|
```
|
|
6
6
|
|
|
7
7
|
```typescript
|
|
8
|
-
import { jsonResult,
|
|
8
|
+
import { jsonResult, render } from "agent-first-data";
|
|
9
9
|
|
|
10
10
|
const event = jsonResult({
|
|
11
11
|
api_key_secret: "sk-123",
|
|
@@ -13,11 +13,11 @@ const event = jsonResult({
|
|
|
13
13
|
db_url: "postgres://user:p@ss@db/app?token_secret=abc",
|
|
14
14
|
}).build();
|
|
15
15
|
|
|
16
|
-
console.log(
|
|
17
|
-
console.log(
|
|
16
|
+
console.log(render(event.toJSON(), "json"));
|
|
17
|
+
console.log(render(event.toJSON(), "plain"));
|
|
18
18
|
```
|
|
19
19
|
|
|
20
|
-
Useful names use TypeScript casing: `
|
|
20
|
+
Useful names use TypeScript casing: `render`, `outputOptionsForPolicy`, `redactedValue`, `redactUrlSecrets`, `normalizeUtcOffset`, `isValidRfc3339Date`, `isValidRfc3339Time`, `isValidRfc3339`, `isValidBcp47`, `decodeProtocolEvent`, `cliParseOutput`, `cliParseLogFilters`, `buildCliError`, `buildCliVersion`, and `cliHandleVersionOrContinue`.
|
|
21
21
|
|
|
22
22
|
Skill admin and stream redirection are not re-exported from the package root; import them from their own subpaths:
|
|
23
23
|
|
|
@@ -30,7 +30,7 @@ import { installStreamRedirectFromRawArgs } from "agent-first-data/stream-redire
|
|
|
30
30
|
|
|
31
31
|
- Default redaction replaces every `_secret` or configured secret-name subtree with `***`, including objects and arrays.
|
|
32
32
|
- `_url` fields scrub userinfo passwords and secret-named query parameters; surrounding whitespace is trimmed and internal whitespace redacts the whole field.
|
|
33
|
-
- YAML
|
|
33
|
+
- YAML keeps original keys and values (structure-preserving, like JSON), sorting keys by UTF-16 code unit order and quoting/escaping unsafe keys and string scalars. Plain strips formatting suffixes, formats values, sorts the same way, and renders nested objects/arrays as canonical JSON.
|
|
34
34
|
- Logging records use `kind:"log"` with a nested `log` payload and a separate `level` field, so error-level logs are not terminal protocol errors.
|
|
35
35
|
- `buildCliError(message, hint?)` returns a strict-ready CLI error with `error.retryable:false` and `trace:{}`.
|
|
36
36
|
- Use `cliHandleVersionOrContinue()` before argument parsing so bare `--version` stays conventional and `--version --output json|yaml|plain` stays structured.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: agent-first-data
|
|
3
|
-
description: Apply and review the formal Agent-First Data specification using the Rust/Go/Python/TypeScript libraries or afdata CLI for naming, redaction, formatting, protocol envelopes, logging, linting, and
|
|
3
|
+
description: Apply and review the formal Agent-First Data specification using the Rust/Go/Python/TypeScript libraries or afdata CLI for naming, redaction, formatting, protocol envelopes, logging, linting, validation, and reading or safely editing structured JSON/TOML/YAML/dotenv/INI documents (and the +++/--- frontmatter block of a Markdown page) by dot-path. Use proactively when writing or reviewing structured data, configs, logs, transport payloads, database/wire fields, CLI output, compatibility-sensitive public and persistent field names, or when reading or editing a config/document/Markdown-frontmatter file from the shell.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
<!-- Canonical source: skills/agent-first-data/SKILL.md.
|
|
@@ -47,22 +47,21 @@ go get github.com/agentfirstkit/agent-first-data/go
|
|
|
47
47
|
```
|
|
48
48
|
|
|
49
49
|
```rust
|
|
50
|
-
use agent_first_data::{json_result,
|
|
50
|
+
use agent_first_data::{json_result, render, OutputFormat, OutputOptions};
|
|
51
51
|
use serde_json::json;
|
|
52
52
|
|
|
53
53
|
let event = json_result(json!({
|
|
54
54
|
"latency_ms": 1280,
|
|
55
55
|
"api_key_secret": "sk-live-example",
|
|
56
|
-
})).build()
|
|
57
|
-
let rendered =
|
|
56
|
+
})).build();
|
|
57
|
+
let rendered = render(event.as_value(), OutputFormat::Json, &OutputOptions::default());
|
|
58
58
|
assert!(rendered.contains("\"api_key_secret\":\"***\""));
|
|
59
|
-
# Ok::<(), agent_first_data::BuildError>(())
|
|
60
59
|
```
|
|
61
60
|
|
|
62
61
|
For a finite CLI execution implemented in Rust, create one `CliEmitter`, select
|
|
63
62
|
`OutputFormat` through `cli_parse_output`, enable strict protocol, and emit one
|
|
64
63
|
terminal result or error. Handle version before clap with
|
|
65
|
-
`cli_handle_version_or_continue
|
|
64
|
+
`cli_handle_version_or_continue(raw_args, name, version)`.
|
|
66
65
|
For non-CLI HTTP/MCP/SSE serialization, call `redacted_value()` or
|
|
67
66
|
`redact_url_secrets()` explicitly at the boundary.
|
|
68
67
|
|
|
@@ -94,11 +93,11 @@ working in that runtime.
|
|
|
94
93
|
|
|
95
94
|
## Redaction behavior contract
|
|
96
95
|
|
|
97
|
-
Fields named `_secret` or `_url` and values passing through AFDATA output
|
|
96
|
+
Fields named `_secret` or `_url` and values passing through AFDATA output rendering (`render`, in any format) are automatically redacted: secrets become `***`, URL userinfo passwords and suffix-named query parameters are scrubbed. When serializing outside output formatters (HTTP, MCP, SSE), call `redacted_value()` or `redact_url_secrets()` at the serialization boundary. PII redaction, domain-specific privacy policies (header allowlists, API scope sensitivity), and per-field secret naming are each spore's responsibility; the library provides field-name-based mechanics only.
|
|
98
97
|
|
|
99
98
|
## Logging behavior contract
|
|
100
99
|
|
|
101
|
-
One-time CLI output uses `json_log()` or `CliEmitter` for a single event; serialization via `
|
|
100
|
+
One-time CLI output uses `json_log()` or `CliEmitter` for a single event; serialization via `render()` applies output formatting. Construct the emitter in finite mode for a one-shot command (`result`→stdout, `error`/`progress`/`log`→stderr) or stream mode for an interleaved event stream (all events on one destination) — see the Output stream contract. Long-running services or processes depending on structured logging (tonic, sqlx, hyper, etc. via tracing) initialize with `afdata_tracing::try_init()` (Rust only) to wire process-wide logging; other languages emit log events explicitly via builders or integrate their own structured logging.
|
|
102
101
|
|
|
103
102
|
## CLI workflow
|
|
104
103
|
|
|
@@ -114,21 +113,168 @@ Use it for deterministic checks:
|
|
|
114
113
|
```bash
|
|
115
114
|
afdata lint payload.json
|
|
116
115
|
afdata validate events.jsonl
|
|
117
|
-
afdata
|
|
116
|
+
afdata render payload.json --output yaml
|
|
118
117
|
afdata skill validate skills/example-skill
|
|
119
118
|
```
|
|
120
119
|
|
|
121
|
-
-
|
|
120
|
+
- Every command's first positional is its input; `-` reads stdin explicitly.
|
|
121
|
+
There is no implicit stdin fallback, and omitting the input is always a
|
|
122
|
+
usage error that names the fix — never a hang.
|
|
123
|
+
- Run `afdata lint` for ordinary JSON/JSONL, JSON Schema, MCP input/output
|
|
124
|
+
schemas, or a structured document (`--input-format`/extension detection
|
|
125
|
+
picks TOML/YAML/dotenv/INI; JSON/JSONL stays the default dual-mode input).
|
|
122
126
|
- Run `afdata validate` for AFDATA protocol events or finite event streams.
|
|
123
|
-
-
|
|
124
|
-
`plain
|
|
125
|
-
|
|
127
|
+
- Run `afdata render` to apply AFDATA redaction/formatting to arbitrary JSON
|
|
128
|
+
or JSONL. `--output` accepts `json`, `yaml`, or `plain`; JSON and YAML keep
|
|
129
|
+
original keys/values (structure-preserving), Plain is the lossy human
|
|
130
|
+
renderer.
|
|
131
|
+
- Treat findings as contract issues unless the owning tool intentionally
|
|
132
|
+
documents a non-AFDATA field.
|
|
133
|
+
|
|
134
|
+
## Output stream contract
|
|
135
|
+
|
|
136
|
+
AFDATA CLIs route protocol events by consumption mode, not by event shape.
|
|
137
|
+
|
|
138
|
+
- A **finite one-shot command** (the default) puts `kind:"result"` on `stdout`
|
|
139
|
+
and `kind:"error"`/`progress`/`log` on `stderr`, so `x=$(tool …)` never
|
|
140
|
+
captures a failure as data, `tool … >/dev/null` never swallows a diagnostic,
|
|
141
|
+
and `tool … | next` never pipes an error envelope in as input. The exit
|
|
142
|
+
code — not the stream — is set by the terminal kind; routing follows `kind`,
|
|
143
|
+
not the exit code.
|
|
144
|
+
- An **event stream** (interleaved events consumed in order) keeps every event,
|
|
145
|
+
including `error`, on one stream so ordering survives.
|
|
146
|
+
- `afdata` and any CLI built on the library expose `--output-to
|
|
147
|
+
<split|stdout|stderr>` (default `split`). `stdout`/`stderr` collapse the whole
|
|
148
|
+
stream — errors included — onto one destination, for a consumer that would
|
|
149
|
+
rather read one stream and branch on `kind`. `--output` picks the format,
|
|
150
|
+
`--output-to` picks the destination. Send an event stream to a file by
|
|
151
|
+
collapsing it (`--output-to stdout`) and redirecting (`--stdout-file PATH`).
|
|
152
|
+
- The raw-scalar readers (`value`/`paths`/`keys`) are intrinsically split (raw
|
|
153
|
+
data on `stdout`, error envelope on `stderr`) and reject a non-default
|
|
154
|
+
`--output-to`.
|
|
155
|
+
- When building a CLI on the library, choose the mode by how the output is
|
|
156
|
+
consumed — do not reflexively pick finite. A plain one-shot shell command uses
|
|
157
|
+
**finite** mode (errors → stderr, so shell capture and pipelines stay safe). A
|
|
158
|
+
tool whose output is itself an event stream (interleaved `progress`/`log` then
|
|
159
|
+
a terminal `result`/`error` — e.g. streaming query rows), or that is
|
|
160
|
+
agent-facing and wants the caller to read a single structured channel and
|
|
161
|
+
branch on `kind`, uses **stream/unified** mode (every event, errors included,
|
|
162
|
+
on `stdout`). A stream's terminal error MUST stay on the stream, so such a tool
|
|
163
|
+
keeps even its usage errors on `stdout` for one consistent channel. Never write
|
|
164
|
+
to `stderr` ad hoc — route every diagnostic through the emitter so redaction
|
|
165
|
+
and framing still apply.
|
|
166
|
+
- Emit a terminal event with the emitter's `finish` / `finish_result` helpers,
|
|
167
|
+
which map the write outcome to a broken-pipe-safe process exit code (success →
|
|
168
|
+
the given code, a hung-up reader → 0, any other write failure → a nonzero
|
|
169
|
+
output-failure code) instead of hand-rolling that dance — they work in either
|
|
170
|
+
mode (the emitter's mode decides the stream; `finish` only maps the outcome).
|
|
171
|
+
Construct an error with the error builder (`json_error(code, message)` with
|
|
172
|
+
`hint`/`retryable`/extra fields as needed) — that builder *is* the error type —
|
|
173
|
+
and pass the built event to `finish`; there is no separate error-emitting
|
|
174
|
+
convenience.
|
|
175
|
+
|
|
176
|
+
## Document workflow
|
|
177
|
+
|
|
178
|
+
Use `get`/`value`/`paths`/`keys` to read a JSON, TOML, YAML, dotenv, or INI
|
|
179
|
+
document and `set`/`add`/`remove`/`unset` to edit one in place, instead of
|
|
180
|
+
`sed`, regular expressions, or a generic reserializer — those can reorder
|
|
181
|
+
keys, drop comments, or change quoting/anchors in the rest of the file. Every
|
|
182
|
+
document command's first positional is `FILE` (or `-` for stdin on a read
|
|
183
|
+
command); mutation commands never read stdin and reject `-` as a usage error.
|
|
184
|
+
To edit only the metadata block of a Markdown page, pass `--input-format
|
|
185
|
+
toml-frontmatter` (a `+++` block) or `yaml-frontmatter` (a `---` block): the
|
|
186
|
+
same read/mutate commands then address dot-paths inside the frontmatter and
|
|
187
|
+
leave the Markdown body byte-for-byte untouched. Frontmatter is never
|
|
188
|
+
auto-detected — the format must be named explicitly, since extension detection
|
|
189
|
+
only ever resolves to a whole-file format — so reach for it deliberately
|
|
190
|
+
instead of `sed`-ing a page's `+++`/`---` header.
|
|
191
|
+
|
|
192
|
+
- `get FILE` returns the whole document as one AFDATA record
|
|
193
|
+
(`code:"document"`); `get FILE KEY` narrows it to one dot-path value in the
|
|
194
|
+
same shape. Pass every known sensitive legacy field name with
|
|
195
|
+
`--secret-name FIELD` (an exact field name, not a dot-path). A secret-named
|
|
196
|
+
leaf (`_secret`/`_SECRET`, or an exact `--secret-name` match) is still
|
|
197
|
+
redacted to `"***"` even when directly targeted — use `value
|
|
198
|
+
--reveal-secret` when the real value is genuinely needed.
|
|
199
|
+
- `value FILE KEY` writes only the raw scalar to stdout, with no AFDATA
|
|
200
|
+
envelope and no forced trailing newline, for direct use in shell
|
|
201
|
+
substitution: `value=$(afdata value config.toml server.host)`. Only scalars
|
|
202
|
+
are supported; a container errors (`document_not_scalar`) — use `get` for a
|
|
203
|
+
subtree. A secret-named leaf errors (`document_secret_redacted`) unless
|
|
204
|
+
`--reveal-secret` is passed. On any failure `value`'s stdout is always
|
|
205
|
+
empty — the error envelope goes to stderr instead — so
|
|
206
|
+
`port=$(afdata value config.toml server.port)` never captures a JSON error
|
|
207
|
+
as data. `--default VAL` prints `VAL` instead of erroring when the path is
|
|
208
|
+
absent or the value is `null` (an empty string is a real value and does not
|
|
209
|
+
trigger it), collapsing the common "read or fall back" shell idiom into one
|
|
210
|
+
call with no `2>/dev/null` guard needed.
|
|
211
|
+
- `paths FILE [KEY]` and `keys FILE [KEY]` enumerate a container's immediate
|
|
212
|
+
children, one per line, with the same empty-stdout-on-failure contract as
|
|
213
|
+
`value`. **`paths` output feeds back into afdata** (full grammar-escaped
|
|
214
|
+
dot-paths from the root — pipe straight into `value`/`get`/`unset` or
|
|
215
|
+
extend with `"$p.field"`); **`keys` output goes to external tools** (raw,
|
|
216
|
+
unescaped key names/array indices — what a package manager or another CLI
|
|
217
|
+
expects). Never use `paths`'s output as a literal name, or `keys`'s output
|
|
218
|
+
as a dot-path — they only diverge on a key containing a dot or space, so
|
|
219
|
+
the wrong one works until it silently doesn't. A scalar target errors
|
|
220
|
+
(`document_not_container`); `--missing-ok` turns a missing KEY specifically
|
|
221
|
+
into empty output + exit 0 (other errors still fail). Path grammar does not
|
|
222
|
+
escape spaces or other IFS characters, so the only correct consumption
|
|
223
|
+
shape is a `while IFS= read -r` loop, never `for x in $(...)`:
|
|
224
|
+
|
|
225
|
+
```sh
|
|
226
|
+
while IFS= read -r p; do
|
|
227
|
+
afdata value config.toml "$p.slug"
|
|
228
|
+
done < <(afdata paths config.toml extra.tools)
|
|
229
|
+
```
|
|
230
|
+
- Escape a literal dot as `\.` and a literal backslash as `\\` in a dot-path;
|
|
231
|
+
every command shares this grammar (`paths`'s escaped output re-parses
|
|
232
|
+
through the exact same rules it was emitted from), so an unrecognized
|
|
233
|
+
escape or empty segment is always an error, never a guess.
|
|
234
|
+
- `add`/`remove` operate on a keyed list — an array of objects addressed by a
|
|
235
|
+
slug — and always require an explicit `--slug-field FIELD`; the CLI never
|
|
236
|
+
infers an identity field. Keyed editing is implemented for JSON and YAML
|
|
237
|
+
only; TOML, dotenv, and INI return a structured "not implemented" error, so
|
|
238
|
+
edit their list entries by writing the whole value another way. Adding a
|
|
239
|
+
slug that already exists, or removing/unsetting one that does not, is
|
|
240
|
+
always an error (`document_slug_exists`/`document_slug_not_found`/
|
|
241
|
+
`document_path_not_found`) — never a silent no-op; wrap the call yourself
|
|
242
|
+
when idempotence is wanted.
|
|
243
|
+
- `set`'s bare VALUE (and `add`'s `FIELD=VALUE`) is always a plain string —
|
|
244
|
+
no shape-guessing, so a leading-zero ID or SHA never gets reinterpreted as
|
|
245
|
+
a number. Writing an exact type goes through `--value-type
|
|
246
|
+
string|number|bool|null|json` instead; `json` is the only entry point for
|
|
247
|
+
an array or object. Overwriting an *existing* scalar of a different kind
|
|
248
|
+
with a bare VALUE is a usage error that names both escape hatches (keep the
|
|
249
|
+
type with `--value-type <kind>`, or convert explicitly with `--value-type
|
|
250
|
+
string`) — a brand-new key never needs `--value-type`.
|
|
251
|
+
- For `set`, choose `--secret-from stdin|prompt|fd:<N>|env:<VAR>` instead of a
|
|
252
|
+
positional value when the value must not land in shell history or `ps`:
|
|
253
|
+
`stdin` (pipe), `prompt` (human, terminal echo off), `fd:N` (Unix
|
|
254
|
+
automation that needs stdin for something else), or `env:VAR` (already in
|
|
255
|
+
the process environment). There is no inline `--secret-from=VALUE`-style
|
|
256
|
+
argv source — that would defeat the point.
|
|
257
|
+
- Mutations preserve the rest of the source document — comments, key order,
|
|
258
|
+
unrelated formatting — and refuse to write through a symlink or (on Unix) a
|
|
259
|
+
multi-link hardlink target. Treat a failed mutation as leaving the original
|
|
260
|
+
file untouched rather than assuming a partial write, and reread before
|
|
261
|
+
retrying. A format backend that isn't compiled in, or input outside the
|
|
262
|
+
documented dotenv/INI dialect, is a structured error to report, not a cue to
|
|
263
|
+
hand-edit the file. A successful mutation's result carries the `path`
|
|
264
|
+
actually written, for confirming which file changed when scripting several.
|
|
265
|
+
- Branch on `error.code`, not the message text: document errors use stable
|
|
266
|
+
codes (`document_path_not_found`, `document_type_mismatch`,
|
|
267
|
+
`document_slug_not_found`, `document_slug_exists`, `document_parse_failed`,
|
|
268
|
+
`document_io_failed`, and a few narrower ones), while a malformed
|
|
269
|
+
invocation (`--input-format`, `--value-type`, `FIELD=VALUE`, a mutation
|
|
270
|
+
given `-`, …) is always `document_usage_error` at exit 2 — distinct from
|
|
271
|
+
every runtime document error, which exits 1.
|
|
126
272
|
|
|
127
273
|
## Review checklist
|
|
128
274
|
|
|
129
275
|
1. Numeric fields with units carry the right suffix and safe integer/string representation.
|
|
130
276
|
2. Timestamps use `_epoch_s`, `_epoch_ms`, decimal-string `_epoch_ns`, or `_rfc3339`.
|
|
131
277
|
3. Secrets use `_secret` or configured exact secret names; no fallback path leaks them.
|
|
132
|
-
4. CLI output uses AFDATA helpers for JSON/YAML/plain formatting and structured errors.
|
|
278
|
+
4. CLI output uses AFDATA helpers for JSON/YAML/plain formatting and structured errors, and routes by consumption mode (finite: `result`→stdout, `error`→stderr; stream: one destination) rather than writing to stderr ad hoc.
|
|
133
279
|
5. Logs use AFDATA structured logging or equivalent field-name redaction.
|
|
134
280
|
6. Transport payloads preserve the protocol envelope when AFDATA compliance is claimed.
|
|
@@ -41,6 +41,8 @@ Integer vs string is not uniform across siblings: `_epoch_s` and `_epoch_ms` are
|
|
|
41
41
|
|
|
42
42
|
`_rfc3339` needs a **mandatory** offset: `YYYY-MM-DDThh:mm:ss[.fff]` followed by `Z` or `±HH:MM`. A bare `2026-02-14T10:30:00` (no offset), a space instead of `T`, or a trailing IANA name (`...Asia/Shanghai`) is rejected by `afdata lint`. `afdata lint` also type-checks the numeric suffixes (durations `_s`/`_ms`/…, currency `_cents`/`_micro`/`_jpy`) and rejects a `_url` with internal whitespace or bare `user:pass@host` credentials.
|
|
43
43
|
|
|
44
|
+
A `null` value is exempt from every suffix type constraint above: `null` means the field is absent/unset, not present-with-the-wrong-type, so `law_repeal_at_epoch_s: null` is not a `suffix_type_mismatch`. Absence may be written as an omitted key or as an explicit `null` — both are valid. The constraint applies only once a value is present and non-null.
|
|
45
|
+
|
|
44
46
|
### Strict string formats
|
|
45
47
|
|
|
46
48
|
| Suffix | Format | Example |
|
|
@@ -107,7 +109,7 @@ Sub-cent precision — `_{code}_micro`, integer millionths (10⁻⁶) of the maj
|
|
|
107
109
|
| `_secret` | redact the entire value/subtree to `***` | `api_key_secret: "sk-or-v1-abc..."` |
|
|
108
110
|
| `_url` | scrub secrets *inside* the URL value, keep the rest | `callback_url: "https://h/cb?code_secret=..."` |
|
|
109
111
|
|
|
110
|
-
All CLI output formats (JSON, YAML, Plain) automatically redact `_secret` fields. Matching recognizes `_secret` and `_SECRET` only — no mixed case. The entire `_secret` value/subtree becomes `***`, including objects and arrays. For legacy fields that cannot be renamed, configure `OutputOptions.redaction` with `secret_names`/`secretNames` such as `["api_key", "authorization"]`; names match exact field names at any nesting level; no trim, case folding, hyphen/underscore normalization, globs, regex, or substring matching. AFDATA does not define named redaction profiles; use the default
|
|
112
|
+
All CLI output formats (JSON, YAML, Plain) automatically redact `_secret` fields. Matching recognizes `_secret` and `_SECRET` only — no mixed case. The entire `_secret` value/subtree becomes `***`, including objects and arrays. For legacy fields that cannot be renamed, configure `OutputOptions.redaction` with `secret_names`/`secretNames` such as `["api_key", "authorization"]`; names match exact field names at any nesting level; no trim, case folding, hyphen/underscore normalization, globs, regex, or substring matching. AFDATA does not define named redaction profiles; use the default policy `All`, `secret_names`, `TraceOnly`, or `Off` deliberately at the serialization boundary. YAML is always schema-preserving, like JSON. Callers that need schema-preserving Plain rendering can pass `OutputOptions` with the `Raw` output style.
|
|
111
113
|
|
|
112
114
|
`***` means only one thing: AFDATA redacted a sensitive value. Do not use it for serialization failure, truncation, unsupported types, or “maybe secret” guesses.
|
|
113
115
|
|
|
@@ -158,15 +160,17 @@ ORM struct fields preserve the suffix: `duration_ms: i64`, not `duration: i64`.
|
|
|
158
160
|
|
|
159
161
|
## Part 2: Output Processing
|
|
160
162
|
|
|
161
|
-
Three output formats via `
|
|
163
|
+
Three output formats via one entry point `render(value, format, options)` (`format` = json/yaml/plain). JSON and YAML are both **structure-preserving**: original keys, scalar types, and numeric semantics survive after redaction. Plain is the one **lossy/human** renderer — it strips formatting suffixes and reformats values for scanning. `options` is an optional keyword arg in Python/TS; Rust/Go pass `OutputOptions` explicitly.
|
|
162
164
|
|
|
163
165
|
### Formats
|
|
164
166
|
|
|
165
167
|
- **JSON** — single-line, original keys, raw values, no sorting (machine-readable), secrets redacted
|
|
166
|
-
- **YAML** — multi-line,
|
|
168
|
+
- **YAML** — multi-line, original keys, raw values (same semantics as JSON), keys sorted, secrets redacted by default
|
|
167
169
|
- **Plain** — single-line logfmt, formatting suffixes stripped, values formatted, secrets redacted by default
|
|
168
170
|
|
|
169
|
-
|
|
171
|
+
Rust gates YAML output behind the `yaml` Cargo feature (on by default, along with the rest of the default feature set). With `default-features = false` and `yaml` not re-enabled, `OutputFormat::Yaml` does not exist and `--output yaml` is rejected with a feature-requirement error instead of silently falling back to another format.
|
|
172
|
+
|
|
173
|
+
### Key stripping (Plain only)
|
|
170
174
|
|
|
171
175
|
Remove recognized formatting suffix from key. Longest match first, exact lowercase or uppercase only:
|
|
172
176
|
|
|
@@ -176,9 +180,9 @@ Remove recognized formatting suffix from key. Longest match first, exact lowerca
|
|
|
176
180
|
4. `_msats`, `_sats`, `_bytes`, `_percent`, `_secret`
|
|
177
181
|
5. `_jpy`, `_ns`, `_us`, `_ms`, `_s`
|
|
178
182
|
|
|
179
|
-
`_bcp47`, `_utc_offset`, `_rfc3339_date`, and `_rfc3339_time` are NOT stripped (pass through). If two keys collide after stripping, both revert to original key AND raw value (no formatting). Redaction runs before collision handling, so fallback never restores a secret.
|
|
183
|
+
`_bcp47`, `_utc_offset`, `_rfc3339_date`, and `_rfc3339_time` are NOT stripped (pass through). If two keys collide after stripping, both revert to original key AND raw value (no formatting). Redaction runs before collision handling, so fallback never restores a secret. JSON and YAML never strip suffixes — every key renders exactly as written.
|
|
180
184
|
|
|
181
|
-
### Value formatting (
|
|
185
|
+
### Value formatting (Plain only)
|
|
182
186
|
|
|
183
187
|
- `_ms` with absolute value < 1000 → `{n}ms`; absolute value ≥ 1000 → seconds (`1280` → `1.28s`, `-1500` → `-1.5s`)
|
|
184
188
|
- `_s`, `_ns`, `_us` → append unit (`3600s`, `450000ns`, `830μs`)
|
|
@@ -192,7 +196,7 @@ Remove recognized formatting suffix from key. Longest match first, exact lowerca
|
|
|
192
196
|
- `_secret` → `***` (the redaction phase already replaced the subtree)
|
|
193
197
|
- `_bcp47`, `_utc_offset`, `_rfc3339_date`, `_rfc3339_time` → pass through unchanged
|
|
194
198
|
|
|
195
|
-
**Type constraints**: `_bytes`/`_epoch_*` require integer. `_usd_cents`/`_eur_cents`/`_jpy`/`_{code}_cents`/`_{code}_micro` require non-negative integer. Duration/Bitcoin/`_percent` accept any number. Wrong type → raw value + original key.
|
|
199
|
+
**Type constraints**: `_bytes`/`_epoch_*` require integer. `_usd_cents`/`_eur_cents`/`_jpy`/`_{code}_cents`/`_{code}_micro` require non-negative integer. Duration/Bitcoin/`_percent` accept any number. Wrong type → raw value + original key. JSON and YAML never format values — a value renders exactly as its JSON type (numbers stay numbers, strings stay quoted strings, no unit/date/currency formatting).
|
|
196
200
|
|
|
197
201
|
### Plain logfmt details
|
|
198
202
|
|
|
@@ -204,7 +208,11 @@ Remove recognized formatting suffix from key. Longest match first, exact lowerca
|
|
|
204
208
|
|
|
205
209
|
### Key ordering
|
|
206
210
|
|
|
207
|
-
YAML
|
|
211
|
+
YAML sorts keys by UTF-16 code unit order (JCS, RFC 8785) without stripping suffixes. Plain sorts keys the same way, but after stripping. For ASCII keys this equals byte-order sorting.
|
|
212
|
+
|
|
213
|
+
### `PlainStyle` (Plain only)
|
|
214
|
+
|
|
215
|
+
`PlainStyle` (`Readable`/`Raw`) only affects Plain: `Readable` (default) strips suffixes and formats values; `Raw` keeps original keys/values (still redacted). YAML ignores `PlainStyle` entirely — it is always structure-preserving, matching JSON.
|
|
208
216
|
|
|
209
217
|
---
|
|
210
218
|
|
|
@@ -219,22 +227,24 @@ Every protocol event uses `kind` plus one same-named payload:
|
|
|
219
227
|
| `"result"` | Terminal success |
|
|
220
228
|
| `"error"` | Terminal error with non-empty `error.code` and `error.message` |
|
|
221
229
|
|
|
222
|
-
Channel policy:
|
|
223
|
-
- `stdout`
|
|
224
|
-
-
|
|
225
|
-
- `stderr`
|
|
230
|
+
Channel policy — the stream follows the consumption mode, not the event shape:
|
|
231
|
+
- **Finite one-shot command (default):** `result` → `stdout`; `error`/`progress`/`log` → `stderr`. Routing follows `kind`, not the exit code, so `stdout` carries only successful payloads (`x=$(tool …)`, `tool | next`, and `tool >/dev/null` all stay safe).
|
|
232
|
+
- **Event stream (interleaved events consumed in order):** every event, including `error`, stays on one stream so ordering survives; do not split it across `stdout`/`stderr`.
|
|
233
|
+
- `--output-to <split|stdout|stderr>` (default `split`) selects the mode: `stdout`/`stderr` collapse the whole stream onto one destination. `--output` selects format, `--output-to` selects destination.
|
|
234
|
+
- Raw-scalar readers (`value`/`paths`/`keys`) are intrinsically split and reject a non-default `--output-to`.
|
|
235
|
+
- Choose the mode by how the output is consumed, not reflexively: a plain one-shot shell command uses **finite**; a tool whose output is itself an event stream (streaming rows, interleaved progress/log) or that is agent-facing and wants one structured channel uses **stream/unified** (all on `stdout`, errors included — a stream's terminal error must stay on the stream). When building a CLI on the library, emit terminal events with the emitter's `finish`/`finish_result` helpers (success → the given code, broken pipe → 0, other write failure → a nonzero output-failure code); build errors with the `json_error` builder (the error *type*, carrying hint/retryable/fields) and pass the event to `finish` — the helpers work in either mode.
|
|
226
236
|
|
|
227
237
|
Optional stream redirection:
|
|
228
|
-
- use `--stdout-file <PATH>` to redirect
|
|
229
|
-
-
|
|
230
|
-
-
|
|
231
|
-
- do not convert stderr diagnostics to JSON; Rust panics, Python tracebacks, and runtime errors stay native stderr bytes
|
|
238
|
+
- use `--stdout-file <PATH>` / `--stderr-file <PATH>` to redirect a stream's bytes to a file (process-level, beneath the channel routing above)
|
|
239
|
+
- send an event stream to a file by collapsing it (`--output-to stdout`) and redirecting (`--stdout-file <PATH>`); there is no separate events-file flag
|
|
240
|
+
- native panics/tracebacks stay raw `stderr` bytes; in finite mode `stderr` also carries the formatted AFDATA `error`/`progress`/`log` envelopes, so a reader parses JSON lines and tolerates interleaved native bytes
|
|
232
241
|
- treat this as stream destination control, not a second protocol stream and not stream copying
|
|
233
242
|
- do not implement application-level rotation
|
|
234
243
|
|
|
235
244
|
Recommended enforcement:
|
|
236
|
-
-
|
|
237
|
-
-
|
|
245
|
+
- route every `stderr` write through the emitter's diagnostic/error path; no ad-hoc stderr that bypasses the formatter in runtime code
|
|
246
|
+
- Rust: keep native panic output as the only unstructured `stderr` (no stray `eprintln!` / `std::io::stderr`)
|
|
247
|
+
- Go/Python/TypeScript: source-policy tests that fail on ad-hoc stderr APIs in runtime code, with the emitter's own sink as the sanctioned exception
|
|
238
248
|
|
|
239
249
|
### Templates
|
|
240
250
|
|
|
@@ -257,7 +267,7 @@ Startup payload fields are tool-defined; `config` is recommended, while `version
|
|
|
257
267
|
| MCP tool | JSON |
|
|
258
268
|
| SSE stream | JSONL |
|
|
259
269
|
|
|
260
|
-
All use `kind` plus the same-named payload and optional `trace`.
|
|
270
|
+
All use `kind` plus the same-named payload and optional `trace`. An event stream keeps its events on one stream (never split); a finite one-shot command splits by `kind` (`result` → stdout, `error` → stderr) — see Channel policy.
|
|
261
271
|
|
|
262
272
|
Base validators enforce mandatory envelope/error/lifecycle rules. Strict validators additionally require object-valued `trace` on every event and `retryable` on every error. Log and progress payloads remain tool-defined in both profiles. Use strict validation before claiming compliance with all recommendations.
|
|
263
273
|
|
|
@@ -269,17 +279,17 @@ Use the local language README for installation and the full overview/spec for AP
|
|
|
269
279
|
|
|
270
280
|
Required cross-language behavior to rely on:
|
|
271
281
|
|
|
272
|
-
-
|
|
273
|
-
- Use `redacted_value()` for raw HTTP/MCP/SSE serialization paths that bypass `
|
|
282
|
+
- The single output entry point `render(value, format, options)` redacts before formatting (with optional `options` in Python/TS).
|
|
283
|
+
- Use `redacted_value()` for raw HTTP/MCP/SSE serialization paths that bypass `render()`.
|
|
274
284
|
- Use `redact_url_secrets()` for URLs embedded in log messages or prose before interpolating them into output.
|
|
275
|
-
- Use `cli_parse_output()`, `cli_parse_log_filters()`, `
|
|
285
|
+
- Use `cli_parse_output()`, `cli_parse_log_filters()`, `render()`, `build_cli_error()`, and the version helper for CLI tools instead of custom parsing/error envelopes.
|
|
276
286
|
- `build_cli_error(message, hint?)` returns a strict-ready CLI error with `error.retryable:false` and `trace:{}`.
|
|
277
|
-
- Rust CLIs should call `cli_handle_version_or_continue()` before clap parsing so `--version --output json|yaml|plain` emits a structured `kind:"result"` event with `result.version` instead of clap's plain text.
|
|
287
|
+
- Rust CLIs should call `cli_handle_version_or_continue(raw_args, name, version)` before clap parsing so `--version --output json|yaml|plain` emits a structured `kind:"result"` event with `result.code == "version"` and `result.version` instead of clap's plain text. Bare `--version` always stays human `<name> <version>` text; explicit `--output` is always structured — there is no configuration.
|
|
278
288
|
- Use the protocol reader `decode_protocol_event(text)` to parse and strict-validate a single JSON text line, receiving a typed decoded event.
|
|
279
289
|
|
|
280
290
|
## AFDATA Logging
|
|
281
291
|
|
|
282
|
-
Long-running services and processes depending on structured logging (tonic, sqlx, hyper, etc. via tracing) use a logging init function to capture the full process. One-time CLI output uses `json_log()` + `
|
|
292
|
+
Long-running services and processes depending on structured logging (tonic, sqlx, hyper, etc. via tracing) use a logging init function to capture the full process. One-time CLI output uses `json_log()` + `render()` or `CliEmitter`.
|
|
283
293
|
|
|
284
294
|
### Init (Rust only; pick one format per process)
|
|
285
295
|
|
|
@@ -312,7 +322,7 @@ Log redaction is **by field name** (the same `_secret`/`_url` rule as all output
|
|
|
312
322
|
CLI tools that use AFDATA should support output and logging flags:
|
|
313
323
|
|
|
314
324
|
```
|
|
315
|
-
--output json|yaml|plain # default is tool-defined (
|
|
325
|
+
--output json|yaml|plain # default is tool-defined (structured/scripting → json or yaml, human scanning → plain)
|
|
316
326
|
--log startup,request,progress,retry,redirect
|
|
317
327
|
--verbose # shorthand for all log categories
|
|
318
328
|
```
|
|
@@ -345,4 +355,4 @@ Use this checklist when the skill is evaluating a change, not just documenting c
|
|
|
345
355
|
7. Environment variables follow `UPPER_SNAKE_CASE` with the same suffixes
|
|
346
356
|
8. One-time CLI output uses `json_log()`/`CliEmitter` + output helpers; long-running services use `afdata_tracing::try_init()` (Rust) to capture via tracing; other languages emit log events explicitly via builders
|
|
347
357
|
9. Database columns use AFDATA suffixes on generic types (`duration_ms INTEGER`, not `duration INTEGER`); native types like `TIMESTAMPTZ` don't need suffixes
|
|
348
|
-
10. CLI flag parsing uses `cli_parse_output()`/`cli_parse_log_filters()`/`build_cli_error()`/version helpers — not custom reimplementations; uses `try_parse()` not `parse()` in Rust so clap
|
|
358
|
+
10. CLI flag parsing uses `cli_parse_output()`/`cli_parse_log_filters()`/`build_cli_error()`/version helpers — not custom reimplementations; uses `try_parse()` not `parse()` in Rust so a clap/usage error becomes a structured `kind:"error"` envelope (routed to stderr under the default split, exit 2), never raw clap text
|
package/dist/cli.d.ts
CHANGED
|
@@ -21,18 +21,22 @@ export declare class LogFilters {
|
|
|
21
21
|
constructor(entries: readonly string[]);
|
|
22
22
|
/**
|
|
23
23
|
* Check if an event matches the filter set.
|
|
24
|
-
*
|
|
25
|
-
*
|
|
24
|
+
*
|
|
25
|
+
* An empty filter set returns false (filtering is opt-in). The single
|
|
26
|
+
* wildcard word "all" returns true ("*" is not special — one wildcard
|
|
27
|
+
* spelling, not two). Otherwise returns true iff event.toLowerCase() starts
|
|
28
|
+
* with any filter (prefix match); a mistyped filter simply matches nothing
|
|
29
|
+
* and silently emits no output.
|
|
26
30
|
*
|
|
27
31
|
* @example
|
|
28
32
|
* const lf = new LogFilters(["query", "error"]);
|
|
29
33
|
* lf.enabled("QueryStarted") // → true (starts with "query")
|
|
30
34
|
* lf.enabled("debug") // → false
|
|
31
35
|
*
|
|
32
|
-
* const
|
|
33
|
-
*
|
|
36
|
+
* const none = new LogFilters([]);
|
|
37
|
+
* none.enabled("anything") // → false (empty)
|
|
34
38
|
*
|
|
35
|
-
* const wild = new LogFilters(["
|
|
39
|
+
* const wild = new LogFilters(["all"]);
|
|
36
40
|
* wild.enabled("anything") // → true
|
|
37
41
|
*/
|
|
38
42
|
enabled(event: string): boolean;
|
|
@@ -48,25 +52,118 @@ export declare class LogFilters {
|
|
|
48
52
|
*/
|
|
49
53
|
export declare function cliParseLogFilters(entries: string[]): LogFilters;
|
|
50
54
|
/**
|
|
51
|
-
*
|
|
52
|
-
* JSON
|
|
55
|
+
* Single public render entry point: dispatch a value or Event to
|
|
56
|
+
* JSON/YAML/plain text by OutputFormat, with optional explicit redaction and
|
|
57
|
+
* style. `options.style` (PlainStyle) affects plain (logfmt) output only;
|
|
58
|
+
* JSON and YAML are structure-preserving and ignore it.
|
|
53
59
|
*
|
|
54
60
|
* @example
|
|
55
|
-
*
|
|
61
|
+
* render(jsonResult({ ok: true }).build(), "plain") // → "kind=result result.ok=true"
|
|
56
62
|
*/
|
|
57
|
-
export declare function
|
|
63
|
+
export declare function render(value: JsonValue | Event, format: OutputFormat, options?: OutputOptions): string;
|
|
58
64
|
export type CliEventWriter = (line: string) => void;
|
|
59
|
-
/**
|
|
65
|
+
/**
|
|
66
|
+
* Where a CliEmitter sends its events, selected by `--output-to`.
|
|
67
|
+
*
|
|
68
|
+
* The stream an event lands on follows the program's consumption mode, not the
|
|
69
|
+
* event's shape (see the spec's CLI Event Framing):
|
|
70
|
+
*
|
|
71
|
+
* - "split" (the default) is finite one-shot mode: `result` → stdout, while
|
|
72
|
+
* `error`/`progress`/`log` → stderr. stdout therefore carries only successful
|
|
73
|
+
* payloads, so a shell capture or pipe never mistakes a failure for data.
|
|
74
|
+
* - "stdout" / "stderr" are event-stream mode: every event, including `error`,
|
|
75
|
+
* is collapsed onto that one stream so a consumer reading it in order
|
|
76
|
+
* (branching on `kind`) sees preserved ordering.
|
|
77
|
+
*/
|
|
78
|
+
export type OutputTo = "split" | "stdout" | "stderr";
|
|
79
|
+
/**
|
|
80
|
+
* Parse an `--output-to` value into an OutputTo: "split" (default), "stdout", or
|
|
81
|
+
* "stderr". Throws on unknown values; catch and pass the message to
|
|
82
|
+
* buildCliError.
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* parseOutputTo("split") // → "split"
|
|
86
|
+
* parseOutputTo("both") // throws Error
|
|
87
|
+
*/
|
|
88
|
+
export declare function parseOutputTo(value: string): OutputTo;
|
|
89
|
+
/**
|
|
90
|
+
* Stateful emitter for structured CLI executions.
|
|
91
|
+
*
|
|
92
|
+
* Routing follows the consumption mode (OutputTo):
|
|
93
|
+
*
|
|
94
|
+
* - Finite one-shot (`CliEmitter.finite` / `finiteWith` /
|
|
95
|
+
* `fromOutputTo("split")`): `result` → the primary writer (stdout);
|
|
96
|
+
* `error`/`progress`/`log` → the diagnostic writer (stderr). The recommended
|
|
97
|
+
* default for a one-shot CLI, so shell capture and pipelines never treat a
|
|
98
|
+
* failure as data.
|
|
99
|
+
* - Event stream (`new CliEmitter(...)` / `CliEmitter.stream` /
|
|
100
|
+
* `fromOutputTo("stdout"|"stderr")`): every event, including `error`, goes to
|
|
101
|
+
* the single writer, preserving interleaved ordering.
|
|
102
|
+
*/
|
|
60
103
|
export declare class CliEmitter {
|
|
61
104
|
private readonly writer;
|
|
62
105
|
private readonly format;
|
|
63
106
|
private readonly outputOptions;
|
|
64
107
|
private terminalEmitted;
|
|
65
108
|
private logFieldsProvider?;
|
|
109
|
+
private diagnostic?;
|
|
110
|
+
/**
|
|
111
|
+
* Create an event-stream emitter: every event, including `error`, goes to the
|
|
112
|
+
* single `writer`. Use CliEmitter.finite for a one-shot command that should
|
|
113
|
+
* split `result`/`error` across stdout/stderr.
|
|
114
|
+
*/
|
|
66
115
|
constructor(writer: CliEventWriter, format: OutputFormat, outputOptions?: OutputOptions);
|
|
116
|
+
/**
|
|
117
|
+
* Create an event-stream emitter: every event goes to `writer`, preserving
|
|
118
|
+
* interleaved ordering. Pick this when the consumer reads one ordered stream
|
|
119
|
+
* and branches on `kind`. Alias for the constructor's unified form.
|
|
120
|
+
*/
|
|
121
|
+
static stream(writer: CliEventWriter, format: OutputFormat, outputOptions?: OutputOptions): CliEmitter;
|
|
122
|
+
/**
|
|
123
|
+
* Create a finite one-shot emitter with explicit sinks: `result` goes to
|
|
124
|
+
* `resultWriter`, while `error`/`progress`/`log` go to `diagnostic`.
|
|
125
|
+
*/
|
|
126
|
+
static finiteWith(resultWriter: CliEventWriter, diagnostic: CliEventWriter, format: OutputFormat, outputOptions?: OutputOptions): CliEmitter;
|
|
127
|
+
/**
|
|
128
|
+
* Create a finite one-shot emitter wired to the process streams: `result` →
|
|
129
|
+
* stdout, `error`/`progress`/`log` → stderr. The recommended default for a
|
|
130
|
+
* one-shot CLI.
|
|
131
|
+
*/
|
|
132
|
+
static finite(format: OutputFormat, outputOptions?: OutputOptions): CliEmitter;
|
|
133
|
+
/**
|
|
134
|
+
* Build an emitter from a parsed OutputTo selector, wired to the process
|
|
135
|
+
* streams: "split" is finite mode (`result` → stdout, everything else →
|
|
136
|
+
* stderr); "stdout"/"stderr" are event-stream mode onto that one stream.
|
|
137
|
+
*/
|
|
138
|
+
static fromOutputTo(selector: OutputTo, format: OutputFormat, outputOptions?: OutputOptions): CliEmitter;
|
|
67
139
|
withLogFields(provider: () => Record<string, JsonValue>): this;
|
|
140
|
+
/**
|
|
141
|
+
* Select the sink for an event by `kind`. Finite mode (a diagnostic sink is
|
|
142
|
+
* present) keeps `result` on the primary writer (stdout) and routes
|
|
143
|
+
* `error`/`progress`/`log` to the diagnostic writer (stderr). Event-stream
|
|
144
|
+
* mode (no diagnostic sink) keeps every event on the single writer.
|
|
145
|
+
*/
|
|
146
|
+
private sinkFor;
|
|
68
147
|
emit(event: Event): void;
|
|
69
148
|
emitValidatedValue(value: JsonValue): void;
|
|
149
|
+
private static isBrokenPipe;
|
|
150
|
+
/**
|
|
151
|
+
* Emit `event` as the terminal event and resolve the outcome to a process
|
|
152
|
+
* exit code, so a one-shot CLI need not hand-roll the emit-then-exit dance.
|
|
153
|
+
*
|
|
154
|
+
* A successful write returns `successCode`; a broken pipe (the reader hung up)
|
|
155
|
+
* returns `0`; any other write or validation failure returns `4`. A library
|
|
156
|
+
* never calls process.exit itself — return this code and let the caller exit.
|
|
157
|
+
*/
|
|
158
|
+
finish(event: Event, successCode: number): number;
|
|
159
|
+
/**
|
|
160
|
+
* Convenience over finish: emit a `result` payload and return `0` on success.
|
|
161
|
+
*
|
|
162
|
+
* Errors — simple or rich — go through the builder instead: construct the event
|
|
163
|
+
* with jsonError(code, message).hint(...)…build() (the builder is the error
|
|
164
|
+
* "type", so it scales to `retryable`/extra fields) and call finish(event, code).
|
|
165
|
+
*/
|
|
166
|
+
finishResult(payload: JsonValue): number;
|
|
70
167
|
}
|
|
71
168
|
/** Build a standard CLI version value. */
|
|
72
169
|
export declare function buildCliVersion(version: string): Event;
|
|
@@ -79,16 +176,30 @@ export declare function cliRenderVersion(name: string, version: string, format?:
|
|
|
79
176
|
/**
|
|
80
177
|
* Render version output if --version/-V is present; otherwise return undefined.
|
|
81
178
|
* Throws for malformed version requests, for example `--version --output xml`.
|
|
179
|
+
*
|
|
180
|
+
* One blessed behavior, not configurable: a bare version request renders
|
|
181
|
+
* conventional "<name> <version>" text; `--json` or `--output <fmt>` /
|
|
182
|
+
* `--output=<fmt>` alongside it selects the structured AFDATA payload
|
|
183
|
+
* instead.
|
|
184
|
+
*
|
|
185
|
+
* Only a top-level version request is recognized: scanning stops at the first
|
|
186
|
+
* positional argument (the subcommand) or `--`, so `tool sub --version <value>`
|
|
187
|
+
* leaves `--version` for the subcommand's parser rather than printing the
|
|
188
|
+
* tool version.
|
|
82
189
|
*/
|
|
83
|
-
export declare function cliHandleVersionOrContinue(rawArgs: string[], name: string, version: string
|
|
190
|
+
export declare function cliHandleVersionOrContinue(rawArgs: string[], name: string, version: string): string | undefined;
|
|
84
191
|
/**
|
|
85
|
-
* Build a standard CLI parse error value.
|
|
192
|
+
* Build a standard CLI parse error value. This function cannot fail.
|
|
86
193
|
* Use when flag parsing fails or a flag value is invalid.
|
|
87
|
-
* Print with
|
|
194
|
+
* Print with render and exit with code 2.
|
|
195
|
+
*
|
|
196
|
+
* Always returns a strict-valid `kind:"error"` event with code `"cli_error"`.
|
|
197
|
+
* An empty `message` is replaced with a generic placeholder so the returned
|
|
198
|
+
* event stays strict-valid without throwing.
|
|
88
199
|
*
|
|
89
200
|
* @example
|
|
90
201
|
* const err = buildCliError("--output: invalid value 'xml'");
|
|
91
|
-
* console.log(
|
|
202
|
+
* console.log(render(err, "json"));
|
|
92
203
|
* process.exit(2);
|
|
93
204
|
*/
|
|
94
205
|
export declare function buildCliError(message: string, hint?: string): Event;
|