argsbarg 6.1.7 → 6.1.9
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/CHANGELOG.md +23 -1
- package/docs/README.md +2 -1
- package/docs/cli-program.md +5 -3
- package/docs/config-schema.md +12 -9
- package/docs/decisions.md +21 -2
- package/docs/http-server.md +3 -1
- package/docs/json-schema-subset.md +38 -12
- package/docs/logging.md +149 -0
- package/docs/output-schema.md +1 -1
- package/examples/full-example/docs/cli-schema.json +18 -18
- package/examples/full-example/docs/cli.md +18 -18
- package/examples/full-example/docs/http.md +10 -0
- package/index.d.ts +84 -14
- package/package.json +3 -2
- package/src/builtins/http.ts +1 -1
- package/src/builtins/mcp.ts +1 -1
- package/src/config/validate.test.ts +45 -1
- package/src/config/validate.ts +138 -203
- package/src/core/types.ts +17 -3
- package/src/docs/http-guide.ts +10 -0
- package/src/headless/tool-call.ts +1 -1
- package/src/hooks/run.ts +7 -2
- package/src/http/server.ts +19 -1
- package/src/index.ts +2 -0
- package/src/log/ecs.test.ts +68 -2
- package/src/log/ecs.ts +109 -14
- package/src/log/emitter.test.ts +95 -0
- package/src/log/emitter.ts +74 -11
- package/src/log/trace.test.ts +60 -0
- package/src/log/trace.ts +62 -0
- package/src/runtime/cli.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [6.1.9] - 2026-07-27
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **ECS Logging–compatible JSON logs** — default JSON output includes `ecs.version`, nested `labels`, canonical HTTP fields (`http.request.method`, `url.path`, `http.response.status_code`, `event.duration` in nanoseconds), and W3C `trace.id` / `span.id` when `traceparent` is present on HTTP requests.
|
|
15
|
+
- **`program.log.enrich`** — additive hook for custom JSON fields (cannot override ECS baseline keys).
|
|
16
|
+
- **`program.log.serialize`** — optional full-line JSON formatter that bypasses the built-in ECS formatter.
|
|
17
|
+
|
|
18
|
+
### Changed
|
|
19
|
+
|
|
20
|
+
- **HTTP trace propagation** — parses incoming `traceparent`, logs trace/span ids, and echoes an updated `traceparent` on responses.
|
|
21
|
+
- **CLI log-format help** — describes JSON as ECS Logging, not generic ECS.
|
|
22
|
+
- **Docs** — new [logging.md](logging.md) (`program.log`, `enrich`, `serialize`, examples); `http-server.md` links there.
|
|
23
|
+
|
|
24
|
+
## [6.1.8] - 2026-07-27
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- **JSON Schema validation** — `inputSchema` and `program.appConfig` validation uses `@cfworker/json-schema`; draft is resolved from each schema’s `$schema` (default Draft-07). Hand-written schemas may opt into 2019-09 or 2020-12 (`$defs` supported). Docs updated for multi-draft and Zod interop.
|
|
29
|
+
|
|
10
30
|
## [6.1.7] - 2026-07-24
|
|
11
31
|
|
|
12
32
|
### Changed
|
|
@@ -859,7 +879,9 @@ const cli = { ... } satisfies CliProgram; // or : CliProgram
|
|
|
859
879
|
- Migrate schemas: rename every `children` property to **`commands`**; move positional definitions to **`CliPositional`** objects on `positionals` and strip `positional` / `argMin` / `argMax` from flag definitions under `options` (flags only carry `name`, `description`, `kind`, and optional `shortName`).
|
|
860
880
|
- Imports: use `CliPositional` where needed; replace `CliOptionDef` with `CliOption` or `CliPositional` as appropriate.
|
|
861
881
|
|
|
862
|
-
[Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v6.1.
|
|
882
|
+
[Unreleased]: https://github.com/bdombro/bun-argsbarg/compare/v6.1.9...HEAD
|
|
883
|
+
[6.1.9]: https://github.com/bdombro/bun-argsbarg/releases/tag/v6.1.9
|
|
884
|
+
[6.1.8]: https://github.com/bdombro/bun-argsbarg/releases/tag/v6.1.8
|
|
863
885
|
[6.1.7]: https://github.com/bdombro/bun-argsbarg/releases/tag/v6.1.7
|
|
864
886
|
[6.1.6]: https://github.com/bdombro/bun-argsbarg/releases/tag/v6.1.6
|
|
865
887
|
[6.1.5]: https://github.com/bdombro/bun-argsbarg/releases/tag/v6.1.5
|
package/docs/README.md
CHANGED
|
@@ -8,9 +8,10 @@ Start here to pick the right guide.
|
|
|
8
8
|
| **Authoring a `CliProgram`** (humans or agents) | [cli-program.md](cli-program.md) — schema, formats, headless, `read*Flags` |
|
|
9
9
|
| **JSON stdout / `outputSchema`** | [output-schema.md](output-schema.md) — codegen pipeline, JSDoc, narrowing |
|
|
10
10
|
| **App config / `program.appConfig`** | [config-schema.md](config-schema.md) — flat JSON file, `ctx.appConfig`, codegen |
|
|
11
|
-
| **JSON Schema
|
|
11
|
+
| **JSON Schema validation** | [json-schema-subset.md](json-schema-subset.md) — Draft-07 / 2019-09 / 2020-12 (`$schema` on each schema; default Draft-07) |
|
|
12
12
|
| **Exposing MCP tools** | [mcp.md](mcp.md) — stdio server, `inputSchema`, varargs, `configure --sync` |
|
|
13
13
|
| **HTTP tool server** | [http-server.md](http-server.md) — `myapp http`, endpoints, curl examples |
|
|
14
|
+
| **Server logging** (`program.log`, `enrich`, `serialize`) | [logging.md](logging.md) — ECS JSON lines, trace headers, custom formats |
|
|
14
15
|
| **Shipping configure / agent artifacts** | [configure.md](configure.md) — Homebrew + `myapp configure --sync` |
|
|
15
16
|
| **Homebrew tap-from-repo distribution** | [distribution-homebrew.md](distribution-homebrew.md) — formula pattern, `argsbarg create` |
|
|
16
17
|
| **Bundling `myapp docs` topics** | [bundled-docs.md](bundled-docs.md) — consumer docgen vs framework docs |
|
package/docs/cli-program.md
CHANGED
|
@@ -41,6 +41,8 @@ const cli = {
|
|
|
41
41
|
|
|
42
42
|
`httpServer` and `mcpServer` are independent. Tool exposure uses the same rules (`mcpTool.enabled: false` hides from MCP and HTTP). See [http-server.md](http-server.md).
|
|
43
43
|
|
|
44
|
+
**Logging** — HTTP and MCP server logs go to stderr (JSON by default). Configure `program.log` on the program root; use **`enrich`** to add fields or **`serialize`** for a fully custom line. See **[logging.md](logging.md)**.
|
|
45
|
+
|
|
44
46
|
## Inline schema by default
|
|
45
47
|
|
|
46
48
|
ArgsBarg is **schema-first** — the program tree is the product. **Keep `CliProgram` and leaf fields inline** (`key`, `description`, `options`, `positionals`, `handler`) so a reader sees the full command contract in one place.
|
|
@@ -111,7 +113,7 @@ Descriptions and schemas are copied into MCP tools, HTTP OpenAPI, and generated
|
|
|
111
113
|
- For shape discovery: HTTP agents load **`docs openapi`** or `GET /openapi.json`; MCP agents use **`docs cli-schema`**; load full **`docs cli`** only when prose is needed.
|
|
112
114
|
- Skill **`reference.md`** uses a compact CLI guide (no inline `outputSchema` JSON) — see [bundled-docs.md](bundled-docs.md#agent-artifact-contract).
|
|
113
115
|
|
|
114
|
-
Validation
|
|
116
|
+
Validation: [json-schema-subset.md](json-schema-subset.md) (Draft-07 default; 2019-09 / 2020-12 when `$schema` is set — including Zod-generated schemas).
|
|
115
117
|
|
|
116
118
|
## Well-known option names
|
|
117
119
|
|
|
@@ -293,7 +295,7 @@ For nested tool bodies (e.g. invoice template data), declare a matching property
|
|
|
293
295
|
|
|
294
296
|
**Precedence:** if `--invoice` is set, the flag value wins and stdin is not read.
|
|
295
297
|
|
|
296
|
-
Use **`ctx.jsonOpt("invoice")`**, **`ctx.inputs`**, or **`ctx.inputsAs<MyInput>()`** — all synchronous. Argsbarg reads piped stdin before calling the handler when a `pipable` Json flag is omitted. When `leaf.inputSchema` is set, argsbarg validates merged inputs **before the handler runs** (same
|
|
298
|
+
Use **`ctx.jsonOpt("invoice")`**, **`ctx.inputs`**, or **`ctx.inputsAs<MyInput>()`** — all synchronous. Argsbarg reads piped stdin before calling the handler when a `pipable` Json flag is omitted. When `leaf.inputSchema` is set, argsbarg validates merged inputs **before the handler runs** (same engine as `program.appConfig`; draft from `$schema` on the schema object); `ctx.inputs` returns the cached result.
|
|
297
299
|
|
|
298
300
|
At most one `pipable` Json option per leaf. Json option names must appear in `inputSchema.properties` when a custom `inputSchema` is set.
|
|
299
301
|
|
|
@@ -321,7 +323,7 @@ When the entire tool body is JSON (no CLI flags), set **`kind: "json"`** on the
|
|
|
321
323
|
|
|
322
324
|
Example CLI: `jq '{format:"pdf", invoice:.}' data.json | myapp render-invoice`
|
|
323
325
|
|
|
324
|
-
See [output-schema.md](output-schema.md) for schemagen `inputType`, [http-server.md](http-server.md) for HTTP tool bodies, and [json-schema-subset.md](json-schema-subset.md) for
|
|
326
|
+
See [output-schema.md](output-schema.md) for schemagen `inputType`, [http-server.md](http-server.md) for HTTP tool bodies, and [json-schema-subset.md](json-schema-subset.md) for validation drafts, Zod interop, and keyword notes.
|
|
325
327
|
|
|
326
328
|
`CliLeafInputs` is intentionally untyped at the framework level. Narrow in your app (`read*Flags(ctx)` returning a typed struct) rather than expecting inference from `satisfies CliLeaf`.
|
|
327
329
|
|
package/docs/config-schema.md
CHANGED
|
@@ -48,7 +48,7 @@ await cli.run();
|
|
|
48
48
|
|
|
49
49
|
**`_bindings`** — reserved top-level metadata: `{ "_bindings": { "apiToken": "env" } }`. Set via wizard (Enter to use env), `configure set --from-env`, or `ctx.appConfig.set` (marks `file`). Optional keys can be bound to `skip`.
|
|
50
50
|
|
|
51
|
-
**Validation at runtime** — argsbarg validates the config file and `configure set` / `ctx.appConfig.set` against the effective JSON Schema ([
|
|
51
|
+
**Validation at runtime** — argsbarg validates the config file and `configure set` / `ctx.appConfig.set` against the effective JSON Schema ([validation](json-schema-subset.md)). Draft is chosen from `jsonSchema.$schema` (default Draft-07). Partial writes (bindings only, single-key updates) skip required-property checks.
|
|
52
52
|
|
|
53
53
|
See [cli-program.md — Configuration](cli-program.md#configuration-programappconfig) for resolution order, bootstrap timing, and `configure get`/`set`.
|
|
54
54
|
|
|
@@ -67,7 +67,7 @@ export interface CliAppConfigEntry {
|
|
|
67
67
|
|
|
68
68
|
export interface CliAppConfig {
|
|
69
69
|
commands?: boolean | { enabled?: boolean; mcpSet?: boolean };
|
|
70
|
-
jsonSchema?: Record<string, unknown>; //
|
|
70
|
+
jsonSchema?: Record<string, unknown>; // JSON Schema block; include $schema to opt into 2019-09 / 2020-12
|
|
71
71
|
entries: Record<string, CliAppConfigEntry>;
|
|
72
72
|
}
|
|
73
73
|
```
|
|
@@ -155,7 +155,7 @@ flowchart LR
|
|
|
155
155
|
end
|
|
156
156
|
subgraph runtime [Runtime]
|
|
157
157
|
Program["program.appConfig.jsonSchema"]
|
|
158
|
-
Validate["
|
|
158
|
+
Validate["@cfworker/json-schema (draft from $schema)"]
|
|
159
159
|
end
|
|
160
160
|
src --> Script --> Gen --> Json
|
|
161
161
|
Gen --> Index --> Program --> Validate
|
|
@@ -180,16 +180,19 @@ export interface AppConfig {
|
|
|
180
180
|
|
|
181
181
|
Wire on the program root: `import { AppConfigSchema } from "./config/__generated__"`.
|
|
182
182
|
|
|
183
|
-
### Supported AppConfig shapes (
|
|
183
|
+
### Supported AppConfig shapes (runtime validation)
|
|
184
184
|
|
|
185
|
-
|
|
185
|
+
Validation is [@cfworker/json-schema](https://www.npmjs.com/package/@cfworker/json-schema) with draft from `$schema` (default Draft-07). See [json-schema-subset.md](json-schema-subset.md) for drafts, Zod interop, and limits.
|
|
186
|
+
|
|
187
|
+
| Commonly used | Notes |
|
|
186
188
|
| --- | --- |
|
|
187
|
-
| `type`, `properties`, `required`, `additionalProperties` |
|
|
188
|
-
| `enum`, `const
|
|
189
|
-
| local `#/definitions` + `$ref`
|
|
190
|
-
| `anyOf` / `oneOf`
|
|
189
|
+
| `type`, `properties`, `required`, `additionalProperties` | |
|
|
190
|
+
| `enum`, `const` | `default` is not applied at validation time |
|
|
191
|
+
| local `#/definitions` + `$ref` (Draft-07) or `#/$defs` + `$ref` (2020-12) | remote `$ref` not supported |
|
|
192
|
+
| `anyOf` / `oneOf` / `allOf` | |
|
|
191
193
|
| `items`, `minItems`, `maxItems` | |
|
|
192
194
|
| `minimum`, `maximum`, `minLength`, `maxLength`, `pattern` | |
|
|
195
|
+
| `format` | includes argsbarg `comma-list` |
|
|
193
196
|
|
|
194
197
|
## Minimal example (no schemagen)
|
|
195
198
|
|
package/docs/decisions.md
CHANGED
|
@@ -33,8 +33,27 @@ Zod may actually cause more complexity and little/no gain for consumers.
|
|
|
33
33
|
Yes Zod implements some features we do custom for JSON-SCHEMA, but is opinionated, less flexible, and would actually explode complexity in some situations. Zod could be a win for consumers if they require substantial, complex validation -- but then that may not convert well to json-schema anyways and json-schema generation is a big win.
|
|
34
34
|
|
|
35
35
|
1. Argsbarg does a lot of schema patching/manipulation to create different schemas per target (cli-schema.json, MCP contract, openapi.json). This would be harder with Zod
|
|
36
|
-
2. Consumers can
|
|
36
|
+
2. Consumers can use Zod by converting to JSON Schema (`zod-to-json-schema`, `z.toJSONSchema()`) and passing the result to `inputSchema` / `appConfig.jsonSchema`. Argsbarg resolves the validator draft from each schema’s `$schema` (Draft-07 default; 2019-09 / 2020-12 when set).
|
|
37
37
|
3. For uses like API pass-through, proxy, dynamic schemas, Zod may actually explode complexity for consumers.
|
|
38
38
|
4. Our TS->json-schema approach is actually easier and better in many cases
|
|
39
39
|
- Just write plain typescript, done.
|
|
40
|
-
- Better intellisense -- substantially less abstraction/inference, much better control
|
|
40
|
+
- Better intellisense -- substantially less abstraction/inference, much better control
|
|
41
|
+
|
|
42
|
+
## Structured logging (ECS Logging)
|
|
43
|
+
|
|
44
|
+
Decision: **ECS Logging–compatible NDJSON to stderr** (default), with optional `enrich` / `serialize` hooks
|
|
45
|
+
|
|
46
|
+
### Context
|
|
47
|
+
|
|
48
|
+
- HTTP/MCP servers need access logs, error logs, and trace correlation in production log pipelines
|
|
49
|
+
- Consumers may need custom fields (team labels, vendor-specific shapes) without baking proprietary formats into argsbarg core
|
|
50
|
+
- OpenTelemetry log export is out of scope for the framework runtime (no third-party observability SDK dependency)
|
|
51
|
+
|
|
52
|
+
### Rationale
|
|
53
|
+
|
|
54
|
+
1. **ECS Logging** is an open standard (NDJSON, `ecs.version`, canonical field names) — works with Elasticsearch, Datadog, GCP log agents, etc.
|
|
55
|
+
2. **stderr + JSONL** keeps stdout free for CLI output and matches twelve-factor log collection
|
|
56
|
+
3. **W3C Trace Context** (`traceparent`) enables cross-service correlation without vendor-specific field layouts
|
|
57
|
+
4. **`program.log.enrich`** — additive hook for consumer-specific fields; cannot override the ECS baseline
|
|
58
|
+
5. **`program.log.serialize`** — escape hatch for full custom lines in consumer repos (argsbarg does not endorse that output as ECS)
|
|
59
|
+
6. **No Tyson / OTel SDK in core** — proprietary or heavy observability stacks belong in consumer config or infra (Fluent Bit remapping), not in the open-source framework
|
package/docs/http-server.md
CHANGED
|
@@ -49,7 +49,9 @@ Set `httpServer` on the **program root only**. Validation rejects `httpServer` o
|
|
|
49
49
|
|
|
50
50
|
`httpServer` and `mcpServer` are independent — enable either or both.
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
## Logging
|
|
53
|
+
|
|
54
|
+
Server logs (access lines, errors, startup) go to **stderr** as JSON by default. See **[logging.md](logging.md)** for `program.log`, **`enrich`**, **`serialize`**, trace headers, and examples.
|
|
53
55
|
|
|
54
56
|
## REST routes
|
|
55
57
|
|
|
@@ -1,39 +1,65 @@
|
|
|
1
|
-
# JSON Schema
|
|
1
|
+
# JSON Schema validation
|
|
2
2
|
|
|
3
|
-
Argsbarg validates `program.appConfig`, leaf `inputSchema`, and related documents with
|
|
3
|
+
Argsbarg validates `program.appConfig`, leaf `inputSchema`, and related documents with [**@cfworker/json-schema**](https://www.npmjs.com/package/@cfworker/json-schema). The validator draft is chosen from each schema’s `$schema` URI (default **Draft-07** when omitted). Use this page when authoring schemas for config files, `inputSchema` on leaves, or `@sg` schemagen output.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Schema draft
|
|
6
|
+
|
|
7
|
+
| `$schema` (examples) | Validator draft |
|
|
8
|
+
| --- | --- |
|
|
9
|
+
| *(omitted)* | Draft-07 (schemagen default) |
|
|
10
|
+
| `http://json-schema.org/draft-07/schema#` | Draft-07 |
|
|
11
|
+
| `https://json-schema.org/draft/2019-09/schema` | 2019-09 |
|
|
12
|
+
| `https://json-schema.org/draft/2020-12/schema` | 2020-12 |
|
|
13
|
+
|
|
14
|
+
Hand-written schemas may use **2020-12** with `$defs` and `#/$defs/...` `$ref`s. Schemagen (`ts-json-schema-generator`) still emits Draft-07 with `definitions`.
|
|
15
|
+
|
|
16
|
+
## From Zod (or other generators)
|
|
17
|
+
|
|
18
|
+
You can pass JSON Schema from **`zod-to-json-schema`**, Zod v4 **`z.toJSONSchema()`**, or any tool that emits a root schema with `$schema`:
|
|
19
|
+
|
|
20
|
+
| Source | Typical `$schema` | Works with argsbarg |
|
|
21
|
+
| --- | --- | --- |
|
|
22
|
+
| `zod-to-json-schema` (default) | Draft-07 | Yes — matches default when `$schema` omitted |
|
|
23
|
+
| `z.toJSONSchema()` (default) | 2020-12 | Yes — draft resolved from `$schema` |
|
|
24
|
+
| argsbarg schemagen | Draft-07 | Yes |
|
|
25
|
+
|
|
26
|
+
Set the result on `leaf.inputSchema` or `program.appConfig.jsonSchema`. Validation uses the schema object and its `$schema` URI; not every Zod feature survives JSON Schema conversion (refinements, transforms, etc.).
|
|
6
27
|
|
|
7
28
|
## Supported constructs
|
|
8
29
|
|
|
30
|
+
Validation uses [@cfworker/json-schema](https://www.npmjs.com/package/@cfworker/json-schema) for the draft declared by `$schema`. Schemas from schemagen and typical Zod exports commonly use:
|
|
31
|
+
|
|
9
32
|
| Feature | Notes |
|
|
10
33
|
| --- | --- |
|
|
11
34
|
| `type` | `object`, `array`, `string`, `integer`, `number`, `boolean`, `null` |
|
|
12
35
|
| `properties` / `required` | Object keys; `additionalProperties: false` enforced when set |
|
|
13
36
|
| `items` | Homogeneous arrays; comma-separated CLI strings coerced when `items` is a primitive |
|
|
14
37
|
| `enum` / `const` | Exact value checks |
|
|
15
|
-
| `anyOf` / `oneOf`
|
|
16
|
-
| `$ref` | **Local only** — `#/definitions/Name`
|
|
17
|
-
| `definitions` | Companion to local `$ref` |
|
|
18
|
-
| `format` | `date`, `date-time`, `duration
|
|
38
|
+
| `anyOf` / `oneOf` / `allOf` | Combinators (validator-native) |
|
|
39
|
+
| `$ref` | **Local only** — `#/definitions/Name` (Draft-07) or `#/$defs/Name` (2019-09 / 2020-12) |
|
|
40
|
+
| `definitions` / `$defs` | Companion to local `$ref` (draft-dependent) |
|
|
41
|
+
| `format` | Built-ins include `date`, `date-time`, `duration`; argsbarg registers `comma-list` |
|
|
19
42
|
| `minimum` / `maximum` | Numbers and integers |
|
|
20
43
|
| `minLength` / `maxLength` | Strings |
|
|
21
44
|
| `pattern` | String regex (ECMAScript) |
|
|
22
45
|
|
|
23
46
|
## Partial validation
|
|
24
47
|
|
|
25
|
-
`validateConfigDocumentPartial` validates **present keys only** —
|
|
48
|
+
`validateConfigDocumentPartial` validates **present keys only** — all `required` arrays are stripped before validation. Used for `configure set` partial writes and bootstrap flows.
|
|
26
49
|
|
|
27
50
|
Leaf `inputSchema` validation uses full validation (including `required`) before the handler runs.
|
|
28
51
|
|
|
29
|
-
##
|
|
52
|
+
## Argsbarg-specific behavior
|
|
53
|
+
|
|
54
|
+
- Framework keys (`_bindings`, etc.) are omitted before config validation when `additionalProperties: false`.
|
|
55
|
+
- CLI `configure set` still coerces comma-separated primitives, booleans, and numbers before validation (`parseConfigSetValue`).
|
|
56
|
+
|
|
57
|
+
## Not guaranteed
|
|
30
58
|
|
|
31
59
|
- Remote `$ref` (`http://…`, other files)
|
|
32
|
-
- `allOf`, conditional (`if`/`then`/`else`), `not`
|
|
33
|
-
- Unevaluated / dynamic references
|
|
34
60
|
- `default` application at validation time (defaults come from CLI option `default` or config bindings)
|
|
35
61
|
|
|
36
|
-
If schemagen emits
|
|
62
|
+
If schemagen emits keywords the validator rejects, simplify the TypeScript type or post-process the generated JSON Schema.
|
|
37
63
|
|
|
38
64
|
## Where validation runs
|
|
39
65
|
|
package/docs/logging.md
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
# Server logging
|
|
2
|
+
|
|
3
|
+
When you run `myapp http` or `myapp mcp`, Argsbarg writes **server logs to stderr** — not to stdout (handlers and CLI output stay on stdout).
|
|
4
|
+
|
|
5
|
+
By default each log line is **one JSON object** (NDJSON), shaped for the [ECS Logging](https://github.com/elastic/ecs-logging) convention. That plays nicely with Datadog, Elasticsearch, GCP Logging, and similar collectors.
|
|
6
|
+
|
|
7
|
+
For local development, switch to plain text with `--log-format text` or `program.log.format: "text"`.
|
|
8
|
+
|
|
9
|
+
## What gets logged
|
|
10
|
+
|
|
11
|
+
| Event | When | `event.action` |
|
|
12
|
+
| --- | --- | --- |
|
|
13
|
+
| Server start | HTTP/MCP process listens | `http.server.start` / `server.start` |
|
|
14
|
+
| Access | After each HTTP request or MCP JSON-RPC message | `http.access` / `mcp.access` |
|
|
15
|
+
| Invoke error | After `formatError` → `onError`, before the client sees the error | `invoke.error` |
|
|
16
|
+
|
|
17
|
+
Toggle access or error lines with `program.log.access` and `program.log.errors` (both default to `true`).
|
|
18
|
+
|
|
19
|
+
## Example line (default JSON)
|
|
20
|
+
|
|
21
|
+
After `GET /workspaces` returns 200 in 45ms:
|
|
22
|
+
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"@timestamp": "2026-07-27T14:00:00.000Z",
|
|
26
|
+
"log.level": "info",
|
|
27
|
+
"message": "GET /workspaces",
|
|
28
|
+
"ecs.version": "8.11.0",
|
|
29
|
+
"service.name": "myapp",
|
|
30
|
+
"service.version": "1.0.0",
|
|
31
|
+
"event.action": "http.access",
|
|
32
|
+
"http.request.method": "GET",
|
|
33
|
+
"url.path": "/workspaces",
|
|
34
|
+
"http.response.status_code": 200,
|
|
35
|
+
"event.duration": 45000000,
|
|
36
|
+
"labels": { "request_id": "…" }
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`event.duration` is in **nanoseconds** (ECS convention). Durations in hook callbacks (`durationMs`) stay in milliseconds.
|
|
41
|
+
|
|
42
|
+
When the client sends a W3C **`traceparent`** header, lines also include `trace.id` and `span.id`, and the response echoes an updated `traceparent`.
|
|
43
|
+
|
|
44
|
+
## `program.log` options
|
|
45
|
+
|
|
46
|
+
Set on the **program root** (same level as `httpServer` / `mcpServer`):
|
|
47
|
+
|
|
48
|
+
```typescript
|
|
49
|
+
const program = {
|
|
50
|
+
key: "myapp",
|
|
51
|
+
version: "1.0.0",
|
|
52
|
+
description: "…",
|
|
53
|
+
log: {
|
|
54
|
+
format: "json", // "text" for human-readable stderr
|
|
55
|
+
file: "server.log", // optional tee; relative paths → app config dir
|
|
56
|
+
access: true, // HTTP/MCP access lines
|
|
57
|
+
errors: true, // invoke error lines
|
|
58
|
+
},
|
|
59
|
+
// …
|
|
60
|
+
} satisfies CliProgram;
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
| Field | Default | Purpose |
|
|
64
|
+
| --- | --- | --- |
|
|
65
|
+
| `format` | `"json"` | `"json"` = ECS Logging NDJSON; `"text"` = `INFO [http.access]: GET /path` |
|
|
66
|
+
| `file` | — | Append the same lines to this path |
|
|
67
|
+
| `access` | `true` | Emit one line per HTTP request / MCP message |
|
|
68
|
+
| `errors` | `true` | Emit when a user command fails on HTTP/MCP |
|
|
69
|
+
| `enrich` | — | Add custom fields to each JSON line (see below) |
|
|
70
|
+
| `serialize` | — | Replace the built-in formatter entirely (see below) |
|
|
71
|
+
|
|
72
|
+
CLI overrides on `myapp http` and `myapp mcp serve`: `--log-format`, `--log-file`, `--no-access-log` (HTTP only), `--dev` (print full stacks to stderr on errors).
|
|
73
|
+
|
|
74
|
+
## `program.log.enrich` — add fields
|
|
75
|
+
|
|
76
|
+
Use **`enrich`** when you want **extra JSON fields** on top of the default ECS line — for example a team label, deployment cell, or a shape your log pipeline expects.
|
|
77
|
+
|
|
78
|
+
`enrich` is **additive only**. It cannot change `@timestamp`, `log.level`, `message`, `ecs.version`, `service.name`, `service.version`, or any field Argsbarg already set on that line.
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
import type { LogEnrichContext } from "argsbarg";
|
|
82
|
+
|
|
83
|
+
const program = {
|
|
84
|
+
// …
|
|
85
|
+
log: {
|
|
86
|
+
format: "json",
|
|
87
|
+
enrich: (ctx: LogEnrichContext) => {
|
|
88
|
+
// Always available:
|
|
89
|
+
// ctx.level, ctx.message, ctx.action, ctx.service.name, ctx.service.version
|
|
90
|
+
// ctx.requestId, ctx.traceId, ctx.spanId (when present)
|
|
91
|
+
// ctx.labels, ctx.error (on error lines)
|
|
92
|
+
|
|
93
|
+
// On access logs (http.access / mcp.access):
|
|
94
|
+
// ctx.http.method, ctx.http.path, ctx.http.status, ctx.http.durationMs, ctx.http.clientIp
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
deployment: "prod",
|
|
98
|
+
};
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
} satisfies CliProgram;
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
Return a flat object of field names → values. Argsbarg merges each key onto the log line unless that key is already set. To add team metadata inside ECS `labels`, put it in `event.labels` via hooks rather than fighting merge order — or use `serialize` for full control.
|
|
105
|
+
|
|
106
|
+
## `program.log.serialize` — own the whole line
|
|
107
|
+
|
|
108
|
+
Use **`serialize`** when the default ECS line is not what you need — for example a legacy JSON schema used only in your organization.
|
|
109
|
+
|
|
110
|
+
When `serialize` is set, Argsbarg **does not** run the ECS formatter. Your function receives the same `LogEnrichContext` as `enrich` and must return the **full line text without a trailing newline** (Argsbarg adds `\n`).
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
import type { LogEnrichContext } from "argsbarg";
|
|
114
|
+
|
|
115
|
+
const program = {
|
|
116
|
+
// …
|
|
117
|
+
log: {
|
|
118
|
+
format: "json",
|
|
119
|
+
serialize: (ctx: LogEnrichContext) =>
|
|
120
|
+
JSON.stringify({
|
|
121
|
+
message: ctx.message,
|
|
122
|
+
level: ctx.level.toUpperCase(),
|
|
123
|
+
contextMap: {
|
|
124
|
+
...(ctx.traceId ? { trace_id: ctx.traceId } : {}),
|
|
125
|
+
...(ctx.spanId ? { span_id: ctx.spanId } : {}),
|
|
126
|
+
},
|
|
127
|
+
}),
|
|
128
|
+
},
|
|
129
|
+
} satisfies CliProgram;
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Do **not** set both `enrich` and `serialize` expecting both to apply — `serialize` wins and `enrich` is ignored.
|
|
133
|
+
|
|
134
|
+
Prefer **`enrich`** when you only need a few extra fields. Reserve **`serialize`** for fully custom output.
|
|
135
|
+
|
|
136
|
+
## Trace correlation (HTTP)
|
|
137
|
+
|
|
138
|
+
If an upstream service (gateway, sidecar, mesh) sends a standard **`traceparent`** header:
|
|
139
|
+
|
|
140
|
+
1. Argsbarg parses it and logs `trace.id` / `span.id`.
|
|
141
|
+
2. The HTTP response includes an updated `traceparent` for this hop.
|
|
142
|
+
|
|
143
|
+
No configuration required. If the header is missing, Argsbarg does not invent a trace id.
|
|
144
|
+
|
|
145
|
+
## Related
|
|
146
|
+
|
|
147
|
+
- [http-server.md](http-server.md) — HTTP server setup and endpoints
|
|
148
|
+
- [mcp.md](mcp.md) — MCP server (same `program.log` applies)
|
|
149
|
+
- [decisions.md](decisions.md#structured-logging-ecs-logging) — why ECS Logging and hooks instead of a bundled observability SDK
|
package/docs/output-schema.md
CHANGED
|
@@ -29,7 +29,7 @@ export const status = {
|
|
|
29
29
|
|
|
30
30
|
**Set on the leaf only** — not under `mcpTool`.
|
|
31
31
|
|
|
32
|
-
**Draft version** — argsbarg
|
|
32
|
+
**Draft version** — for **`inputSchema`** and **`appConfig.jsonSchema`**, argsbarg validates using the draft declared in `$schema` (default Draft-07 when omitted). Schemas from schemagen, `zod-to-json-schema`, or `z.toJSONSchema()` may use Draft-07 or 2020-12. **`outputSchema`** is embedded in docs/MCP/OpenAPI as-is and is not runtime-validated.
|
|
33
33
|
|
|
34
34
|
See [cli-program.md — Structured stdout](cli-program.md#structured-stdout) for when to use `outputSchema` vs `notes`, and [mcp.md](mcp.md) for how MCP returns parsed JSON as `structuredContent`.
|
|
35
35
|
|
|
@@ -161,7 +161,7 @@
|
|
|
161
161
|
},
|
|
162
162
|
{
|
|
163
163
|
"name": "log-format",
|
|
164
|
-
"description": "Log format: json (ECS) or text.",
|
|
164
|
+
"description": "Log format: json (ECS Logging) or text.",
|
|
165
165
|
"kind": "enum",
|
|
166
166
|
"choices": [
|
|
167
167
|
"json",
|
|
@@ -215,7 +215,7 @@
|
|
|
215
215
|
},
|
|
216
216
|
{
|
|
217
217
|
"name": "log-format",
|
|
218
|
-
"description": "Log format: json (ECS) or text.",
|
|
218
|
+
"description": "Log format: json (ECS Logging) or text.",
|
|
219
219
|
"kind": "enum",
|
|
220
220
|
"choices": [
|
|
221
221
|
"json",
|
|
@@ -394,7 +394,7 @@
|
|
|
394
394
|
},
|
|
395
395
|
{
|
|
396
396
|
"name": "log-format",
|
|
397
|
-
"description": "Log format: json (ECS) or text.",
|
|
397
|
+
"description": "Log format: json (ECS Logging) or text.",
|
|
398
398
|
"kind": "enum",
|
|
399
399
|
"choices": [
|
|
400
400
|
"json",
|
|
@@ -448,7 +448,7 @@
|
|
|
448
448
|
},
|
|
449
449
|
{
|
|
450
450
|
"name": "log-format",
|
|
451
|
-
"description": "Log format: json (ECS) or text.",
|
|
451
|
+
"description": "Log format: json (ECS Logging) or text.",
|
|
452
452
|
"kind": "enum",
|
|
453
453
|
"choices": [
|
|
454
454
|
"json",
|
|
@@ -649,7 +649,7 @@
|
|
|
649
649
|
},
|
|
650
650
|
{
|
|
651
651
|
"name": "log-format",
|
|
652
|
-
"description": "Log format: json (ECS) or text.",
|
|
652
|
+
"description": "Log format: json (ECS Logging) or text.",
|
|
653
653
|
"kind": "enum",
|
|
654
654
|
"choices": [
|
|
655
655
|
"json",
|
|
@@ -703,7 +703,7 @@
|
|
|
703
703
|
},
|
|
704
704
|
{
|
|
705
705
|
"name": "log-format",
|
|
706
|
-
"description": "Log format: json (ECS) or text.",
|
|
706
|
+
"description": "Log format: json (ECS Logging) or text.",
|
|
707
707
|
"kind": "enum",
|
|
708
708
|
"choices": [
|
|
709
709
|
"json",
|
|
@@ -886,7 +886,7 @@
|
|
|
886
886
|
},
|
|
887
887
|
{
|
|
888
888
|
"name": "log-format",
|
|
889
|
-
"description": "Log format: json (ECS) or text.",
|
|
889
|
+
"description": "Log format: json (ECS Logging) or text.",
|
|
890
890
|
"kind": "enum",
|
|
891
891
|
"choices": [
|
|
892
892
|
"json",
|
|
@@ -940,7 +940,7 @@
|
|
|
940
940
|
},
|
|
941
941
|
{
|
|
942
942
|
"name": "log-format",
|
|
943
|
-
"description": "Log format: json (ECS) or text.",
|
|
943
|
+
"description": "Log format: json (ECS Logging) or text.",
|
|
944
944
|
"kind": "enum",
|
|
945
945
|
"choices": [
|
|
946
946
|
"json",
|
|
@@ -1119,7 +1119,7 @@
|
|
|
1119
1119
|
},
|
|
1120
1120
|
{
|
|
1121
1121
|
"name": "log-format",
|
|
1122
|
-
"description": "Log format: json (ECS) or text.",
|
|
1122
|
+
"description": "Log format: json (ECS Logging) or text.",
|
|
1123
1123
|
"kind": "enum",
|
|
1124
1124
|
"choices": [
|
|
1125
1125
|
"json",
|
|
@@ -1173,7 +1173,7 @@
|
|
|
1173
1173
|
},
|
|
1174
1174
|
{
|
|
1175
1175
|
"name": "log-format",
|
|
1176
|
-
"description": "Log format: json (ECS) or text.",
|
|
1176
|
+
"description": "Log format: json (ECS Logging) or text.",
|
|
1177
1177
|
"kind": "enum",
|
|
1178
1178
|
"choices": [
|
|
1179
1179
|
"json",
|
|
@@ -1356,7 +1356,7 @@
|
|
|
1356
1356
|
},
|
|
1357
1357
|
{
|
|
1358
1358
|
"name": "log-format",
|
|
1359
|
-
"description": "Log format: json (ECS) or text.",
|
|
1359
|
+
"description": "Log format: json (ECS Logging) or text.",
|
|
1360
1360
|
"kind": "enum",
|
|
1361
1361
|
"choices": [
|
|
1362
1362
|
"json",
|
|
@@ -1410,7 +1410,7 @@
|
|
|
1410
1410
|
},
|
|
1411
1411
|
{
|
|
1412
1412
|
"name": "log-format",
|
|
1413
|
-
"description": "Log format: json (ECS) or text.",
|
|
1413
|
+
"description": "Log format: json (ECS Logging) or text.",
|
|
1414
1414
|
"kind": "enum",
|
|
1415
1415
|
"choices": [
|
|
1416
1416
|
"json",
|
|
@@ -1589,7 +1589,7 @@
|
|
|
1589
1589
|
},
|
|
1590
1590
|
{
|
|
1591
1591
|
"name": "log-format",
|
|
1592
|
-
"description": "Log format: json (ECS) or text.",
|
|
1592
|
+
"description": "Log format: json (ECS Logging) or text.",
|
|
1593
1593
|
"kind": "enum",
|
|
1594
1594
|
"choices": [
|
|
1595
1595
|
"json",
|
|
@@ -1643,7 +1643,7 @@
|
|
|
1643
1643
|
},
|
|
1644
1644
|
{
|
|
1645
1645
|
"name": "log-format",
|
|
1646
|
-
"description": "Log format: json (ECS) or text.",
|
|
1646
|
+
"description": "Log format: json (ECS Logging) or text.",
|
|
1647
1647
|
"kind": "enum",
|
|
1648
1648
|
"choices": [
|
|
1649
1649
|
"json",
|
|
@@ -1822,7 +1822,7 @@
|
|
|
1822
1822
|
},
|
|
1823
1823
|
{
|
|
1824
1824
|
"name": "log-format",
|
|
1825
|
-
"description": "Log format: json (ECS) or text.",
|
|
1825
|
+
"description": "Log format: json (ECS Logging) or text.",
|
|
1826
1826
|
"kind": "enum",
|
|
1827
1827
|
"choices": [
|
|
1828
1828
|
"json",
|
|
@@ -1876,7 +1876,7 @@
|
|
|
1876
1876
|
},
|
|
1877
1877
|
{
|
|
1878
1878
|
"name": "log-format",
|
|
1879
|
-
"description": "Log format: json (ECS) or text.",
|
|
1879
|
+
"description": "Log format: json (ECS Logging) or text.",
|
|
1880
1880
|
"kind": "enum",
|
|
1881
1881
|
"choices": [
|
|
1882
1882
|
"json",
|
|
@@ -2055,7 +2055,7 @@
|
|
|
2055
2055
|
},
|
|
2056
2056
|
{
|
|
2057
2057
|
"name": "log-format",
|
|
2058
|
-
"description": "Log format: json (ECS) or text.",
|
|
2058
|
+
"description": "Log format: json (ECS Logging) or text.",
|
|
2059
2059
|
"kind": "enum",
|
|
2060
2060
|
"choices": [
|
|
2061
2061
|
"json",
|
|
@@ -2109,7 +2109,7 @@
|
|
|
2109
2109
|
},
|
|
2110
2110
|
{
|
|
2111
2111
|
"name": "log-format",
|
|
2112
|
-
"description": "Log format: json (ECS) or text.",
|
|
2112
|
+
"description": "Log format: json (ECS Logging) or text.",
|
|
2113
2113
|
"kind": "enum",
|
|
2114
2114
|
"choices": [
|
|
2115
2115
|
"json",
|