stitchkit 0.90.3 → 0.90.4
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 +74 -0
- package/dist/agent-runtime-coding-tools.js +3 -3
- package/dist/agent-runtime-harness.js +4 -4
- package/dist/agent-runtime-sandbox.js +3 -3
- package/dist/agent-runtime.js +7 -7
- package/dist/cli.d.ts +14 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +359 -11
- package/dist/{index-gbqjt8jz.js → index-19ryv24q.js} +3 -3
- package/dist/{index-2hrfpw2c.js → index-32vjke6q.js} +2 -2
- package/dist/{index-f475yj00.js → index-6atvfjc1.js} +1 -1
- package/dist/{index-79hb1wyh.js → index-7t0wq6j5.js} +3 -3
- package/dist/{index-j7q0xj6d.js → index-8crm1srv.js} +1 -1
- package/dist/{index-kc6h6hg0.js → index-8z9we758.js} +1 -1
- package/dist/{index-r159gjwy.js → index-fenaekmk.js} +94 -8
- package/dist/{index-wb15909q.js → index-kzxpsf8y.js} +3 -3
- package/dist/{index-44ht2790.js → index-vcnfwrtr.js} +1 -1
- package/dist/{index-1923shw9.js → index-y47z614h.js} +214 -7
- package/dist/testing.js +1 -1
- package/dist/tool-invoker.js +4 -4
- package/dist/tools/cli-args.d.ts +22 -0
- package/dist/tools/cli-args.d.ts.map +1 -1
- package/dist/tools/cli-installer.d.ts +29 -0
- package/dist/tools/cli-installer.d.ts.map +1 -0
- package/dist/tools/cli-manifest.d.ts +83 -0
- package/dist/tools/cli-manifest.d.ts.map +1 -0
- package/dist/tools/cli-profile.d.ts +40 -0
- package/dist/tools/cli-profile.d.ts.map +1 -0
- package/dist/tools/cli-update.d.ts +64 -0
- package/dist/tools/cli-update.d.ts.map +1 -0
- package/dist/tools/cli-view.d.ts +12 -0
- package/dist/tools/cli-view.d.ts.map +1 -0
- package/dist/tools/cli.d.ts.map +1 -1
- package/dist/tools/connections/index.d.ts +2 -1
- package/dist/tools/connections/index.d.ts.map +1 -1
- package/dist/tools/connections/index.js +37 -10
- package/dist/tools/connections/mcp.d.ts +2 -1
- package/dist/tools/connections/mcp.d.ts.map +1 -1
- package/dist/tools/connections/mount.d.ts.map +1 -1
- package/dist/tools/connections/openapi.d.ts +2 -1
- package/dist/tools/connections/openapi.d.ts.map +1 -1
- package/dist/tools/connections/runtime.d.ts +17 -0
- package/dist/tools/connections/runtime.d.ts.map +1 -1
- package/dist/tools/connections/types.d.ts +18 -1
- package/dist/tools/connections/types.d.ts.map +1 -1
- package/dist/tools/json-schema-dialect.d.ts +17 -0
- package/dist/tools/json-schema-dialect.d.ts.map +1 -0
- package/dist/tools/presentation.d.ts +8 -1
- package/dist/tools/presentation.d.ts.map +1 -1
- package/dist/tools.js +8 -8
- package/llms-full.txt +203 -3
- package/package.json +1 -1
package/llms-full.txt
CHANGED
|
@@ -7489,6 +7489,10 @@ same Zod schema an HTTP or MCP call does.
|
|
|
7489
7489
|
| `--quiet` | Suppress non-essential stderr output |
|
|
7490
7490
|
| `--dry-run` | Print the resolved call without executing |
|
|
7491
7491
|
| `--help`, `-h` | Usage — top-level or per-command flag table |
|
|
7492
|
+
| `--count-by <field>` | Count records per distinct value — see [Aggregate views](#aggregate-views) |
|
|
7493
|
+
| `--sum <f> [--by <g>]`| Total a numeric field, optionally grouped |
|
|
7494
|
+
| `--top <n> --by <f>` | Keep only the n largest groups |
|
|
7495
|
+
| `--table <a,b>` | Render named fields as an aligned table |
|
|
7492
7496
|
|
|
7493
7497
|
stdout carries the result; structured errors and progress go to stderr. With
|
|
7494
7498
|
`--json`, a success or structured failure is exactly one compact,
|
|
@@ -7603,6 +7607,172 @@ myapp generate "a fox" --wait --output-dir ./out
|
|
|
7603
7607
|
myapp generate "a fox" --wait --json > result.json &
|
|
7604
7608
|
```
|
|
7605
7609
|
|
|
7610
|
+
## Aggregate views
|
|
7611
|
+
|
|
7612
|
+
The CLI's audience is agents, scripts and `jq`, so output is JSON. That settles
|
|
7613
|
+
the *encoding*; it does not settle whether the answer to "how many items per
|
|
7614
|
+
status" should be every item. Measured on a live server, one ordinary question:
|
|
7615
|
+
|
|
7616
|
+
| call | characters returned |
|
|
7617
|
+
|---|---|
|
|
7618
|
+
| the listing (98 records) | 34 750 |
|
|
7619
|
+
| `--count-by status` | ~90 |
|
|
7620
|
+
|
|
7621
|
+
An agent pays for every one of those characters in its context window, and `|
|
|
7622
|
+
jq` does not help: the bytes have been read into the conversation by the time
|
|
7623
|
+
`jq` sees them. So the aggregate is computed on the result, before anything is
|
|
7624
|
+
written.
|
|
7625
|
+
|
|
7626
|
+
```bash
|
|
7627
|
+
myapp item_list --count-by status # { "active": 33, "idle": 33, "stopped": 32 }
|
|
7628
|
+
myapp item_list --count-by status --top 2 # the two largest groups
|
|
7629
|
+
myapp item_list --sum messages # 4753
|
|
7630
|
+
myapp item_list --sum messages --by status # one total per status
|
|
7631
|
+
myapp item_list --top 5 --by status # same view, written the other way round
|
|
7632
|
+
myapp item_list --table id,status # the one human-facing shape
|
|
7633
|
+
```
|
|
7634
|
+
|
|
7635
|
+
`--by` always names the **grouping** field, in every form it appears in, so the
|
|
7636
|
+
grammar has one meaning rather than two. Groups are ordered largest first, which
|
|
7637
|
+
is what makes `--top` a defined slice rather than an arbitrary one.
|
|
7638
|
+
|
|
7639
|
+
Three rules worth knowing before you rely on them:
|
|
7640
|
+
|
|
7641
|
+
- **A field the result does not carry is an argument error.** A group of zero
|
|
7642
|
+
over a misspelled field is indistinguishable from a true empty answer, and the
|
|
7643
|
+
caller reads it as data. The message names the fields that *are* there.
|
|
7644
|
+
- **An aggregate needs a collection** — the result itself when it is an array,
|
|
7645
|
+
or the single array field of a result object. An aggregate over a scalar, or
|
|
7646
|
+
over an object with two array fields, is refused rather than guessed.
|
|
7647
|
+
- **Without a view flag the output is byte-for-byte what it was.** The flags are
|
|
7648
|
+
reserved CLI behaviour like `--json`; they never reach a tool argument.
|
|
7649
|
+
|
|
7650
|
+
A failed call still reports its own error and exit code. An aggregate over an
|
|
7651
|
+
error is not an answer to the question that was asked.
|
|
7652
|
+
|
|
7653
|
+
## Named profiles
|
|
7654
|
+
|
|
7655
|
+
A CLI that talks to a deployed server needs an address and a credential per
|
|
7656
|
+
environment, and the way a person picks one is a name: `--profile prod`.
|
|
7657
|
+
`globalOptions` gives the flag a home and `resolveAuth(globals)` gives it a
|
|
7658
|
+
resolution point. The rule that makes the mechanism safe is easy to write the
|
|
7659
|
+
wrong way round, because the unsafe version reads as kindness:
|
|
7660
|
+
|
|
7661
|
+
> the named profile does not exist, but exactly one profile is configured — use it.
|
|
7662
|
+
|
|
7663
|
+
That is correct exactly while a single profile exists. The day a second appears
|
|
7664
|
+
it is a command run against the wrong deployment, with nothing in the output to
|
|
7665
|
+
say so. **A profile named explicitly and not found is a refusal, never a
|
|
7666
|
+
substitution.** Substitution survives only where it cannot be wrong: no name was
|
|
7667
|
+
given at all and exactly one profile exists — and even then it is announced on
|
|
7668
|
+
stderr. The distinction has to be drawn at resolution; one step later, "prod" and
|
|
7669
|
+
"prod by default" are the same string.
|
|
7670
|
+
|
|
7671
|
+
`createCliProfileStore` is that rule, plus the twenty lines every consumer of
|
|
7672
|
+
this shape writes:
|
|
7673
|
+
|
|
7674
|
+
```ts
|
|
7675
|
+
import { createCliProfileStore } from 'stitchkit/cli'
|
|
7676
|
+
import { homedir } from 'node:os'
|
|
7677
|
+
import { join } from 'node:path'
|
|
7678
|
+
import { z } from 'zod'
|
|
7679
|
+
|
|
7680
|
+
const profiles = createCliProfileStore({
|
|
7681
|
+
directory: join(homedir(), '.config/myapp/profiles'),
|
|
7682
|
+
schema: z.object({ url: z.url(), token: z.string().min(1) }),
|
|
7683
|
+
createHint: (name, path) => `write ${path} with {"url","token"} for "${name}"`,
|
|
7684
|
+
})
|
|
7685
|
+
|
|
7686
|
+
await createCli({
|
|
7687
|
+
name: 'myapp',
|
|
7688
|
+
version,
|
|
7689
|
+
globalOptions: z.object({ profile: z.string().optional() }),
|
|
7690
|
+
resolveAuth: (globals) => profiles.resolve(globals.profile).value,
|
|
7691
|
+
services,
|
|
7692
|
+
})
|
|
7693
|
+
```
|
|
7694
|
+
|
|
7695
|
+
Files are written `0600` in a `0700` directory — and a profile file other users
|
|
7696
|
+
can read is refused with the `chmod` that fixes it, because it holds a
|
|
7697
|
+
credential.
|
|
7698
|
+
|
|
7699
|
+
## Distribution and self-update
|
|
7700
|
+
|
|
7701
|
+
`createCli` ships no executable, and that is right — but the step after the
|
|
7702
|
+
executable is not application logic either. It is the same problem for every
|
|
7703
|
+
consumer, with the same three traps:
|
|
7704
|
+
|
|
7705
|
+
1. **The installer cannot parse the manifest.** A `curl … | sh` runs on a
|
|
7706
|
+
machine where nothing is installed yet, including `jq`. So the installer is
|
|
7707
|
+
generated *from* the manifest, server-side, with the URL and digest already
|
|
7708
|
+
substituted — it parses no JSON at all.
|
|
7709
|
+
2. **Replacing a running binary is a rename, not a write.** Anything else can
|
|
7710
|
+
leave a half-written executable on someone's PATH when the connection drops.
|
|
7711
|
+
3. **The digest covers the decompressed bytes** — the file that will actually be
|
|
7712
|
+
executed, not the archive that was transferred.
|
|
7713
|
+
|
|
7714
|
+
The framework owns the manifest shape, the installer generation and the update
|
|
7715
|
+
primitive. The application owns where the assets live, which platforms it
|
|
7716
|
+
publishes and who may download them.
|
|
7717
|
+
|
|
7718
|
+
```ts
|
|
7719
|
+
import {
|
|
7720
|
+
CliBuildManifestSchema, assertCliPublishable, renderCliInstaller,
|
|
7721
|
+
selectCliBuildAsset, checkCliUpdate, applyCliUpdate,
|
|
7722
|
+
} from 'stitchkit/cli'
|
|
7723
|
+
|
|
7724
|
+
// Publishing: refuse to republish one version from a different commit —
|
|
7725
|
+
// otherwise everyone who already installed it never receives the fix.
|
|
7726
|
+
assertCliPublishable(previous, next)
|
|
7727
|
+
|
|
7728
|
+
// Serving: one generated script per target, no JSON on the wire.
|
|
7729
|
+
renderCliInstaller({ manifest, asset: selectCliBuildAsset(manifest, target)!, binaryName: 'myapp' })
|
|
7730
|
+
|
|
7731
|
+
// Checking: bounded, at most once per interval, silent on any failure.
|
|
7732
|
+
const check = await checkCliUpdate({ manifestUrl, currentVersion, lastCheckedAt })
|
|
7733
|
+
if (check.status === 'outdated' && check.asset) {
|
|
7734
|
+
// Replacing is always an explicit command, never a side effect of a check.
|
|
7735
|
+
await applyCliUpdate({ asset: check.asset })
|
|
7736
|
+
}
|
|
7737
|
+
```
|
|
7738
|
+
|
|
7739
|
+
`checkCliUpdate` has **four** answers, not three: `skipped`, `current`,
|
|
7740
|
+
`outdated` and `unknown`. "Could not ask" is not "up to date" — collapsing them
|
|
7741
|
+
is how a tool goes quiet about its own staleness for months. It never throws,
|
|
7742
|
+
and a command still exits with the code it earned.
|
|
7743
|
+
|
|
7744
|
+
Carry the build stamp inside the binary (`CliBuildStampSchema`,
|
|
7745
|
+
`formatCliBuildStamp`) so the tool can say what it is rather than leaving the
|
|
7746
|
+
reader to infer it from behaviour.
|
|
7747
|
+
|
|
7748
|
+
## Commands discovered from a running server
|
|
7749
|
+
|
|
7750
|
+
A CLI compiled from contracts carries the surface of the build it was compiled
|
|
7751
|
+
from. One built from discovery carries the surface the server has *right now* —
|
|
7752
|
+
which matters, because a long-lived MCP client freezes schemas at connect time
|
|
7753
|
+
and then refuses the server's own newer fields:
|
|
7754
|
+
|
|
7755
|
+
```ts
|
|
7756
|
+
const discovered = await mountConnections([
|
|
7757
|
+
defineMcpClientConnection({
|
|
7758
|
+
name: 'api',
|
|
7759
|
+
transport: { url },
|
|
7760
|
+
token: () => key,
|
|
7761
|
+
transports: ['CLI'], // the opt-in: this server's tools are commands
|
|
7762
|
+
}),
|
|
7763
|
+
])
|
|
7764
|
+
await createCli({ name: 'myapp', version, runtimeTools: discovered, commands: [...] })
|
|
7765
|
+
```
|
|
7766
|
+
|
|
7767
|
+
`transports` is where the opt-in belongs: a whole server becomes a set of
|
|
7768
|
+
commands, and a connection without it still contributes nothing to the CLI —
|
|
7769
|
+
exposure stays explicit, as it is everywhere else in the framework.
|
|
7770
|
+
|
|
7771
|
+
One unconvertible schema no longer takes the connection down with it. The tool
|
|
7772
|
+
is skipped and **named** (`onSkippedTool`, or a stderr line by default), so a
|
|
7773
|
+
surface of two hundred tools is not lost to one.
|
|
7774
|
+
|
|
7775
|
+
|
|
7606
7776
|
## Auth parity
|
|
7607
7777
|
|
|
7608
7778
|
A scoped command is guarded by the same `createAuthHook` your HTTP server uses —
|
|
@@ -16710,13 +16880,16 @@ and approval path.
|
|
|
16710
16880
|
| `ConnectionTokenProvider` | _type_ | `() => string \| undefined \| Promise<string \| undefined>` — the lazily resolved credential |
|
|
16711
16881
|
| `ConnectionBudget` | _type_ | `{ maxTools? }` ceiling on one `mountConnections` call |
|
|
16712
16882
|
| `ConnectionDefinition` | _type_ | either a defined MCP client or OpenAPI connection |
|
|
16713
|
-
| `ConnectionMountOptions` | _type_ | shared `{ lifecycle?, budget? }` mount policy |
|
|
16883
|
+
| `ConnectionMountOptions` | _type_ | shared `{ lifecycle?, budget?, onSkippedTool? }` mount policy |
|
|
16884
|
+
| `ConnectionToolSkipReporter` | _type_ | `(skipped) => void` — called per discovered tool the mount could not build; defaults to a stderr line, and the rest of the surface still mounts |
|
|
16885
|
+
| `SkippedConnectionTool` | _type_ | `{ connection, tool, reason }` — which tool was not mounted, and why |
|
|
16886
|
+
| `RuntimeToolTransport` | _type_ | `'MCP' \| 'AGENT' \| 'CLI'` — the surfaces a connection's `transports` may name |
|
|
16714
16887
|
| `McpClientConnection` | _type_ | a defined MCP client connection |
|
|
16715
|
-
| `McpClientConnectionConfig` | _type_ | name, transport, tool filter, token provider, instance key
|
|
16888
|
+
| `McpClientConnectionConfig` | _type_ | name, transport, tool filter, token provider, instance key, allowed hosts and `transports` — naming `['CLI']` makes the whole server's discovered tools commands, with no per-definition rewriting |
|
|
16716
16889
|
| `McpConnectionTransport` | _type_ | `{ url, headers? }` for one MCP endpoint |
|
|
16717
16890
|
| `McpToolFilter` | _type_ | `{ allow?, block? }` discovered-tool filter |
|
|
16718
16891
|
| `OpenApiConnection` | _type_ | a defined OpenAPI connection |
|
|
16719
|
-
| `OpenApiConnectionConfig` | _type_ | name, spec, base URL, token provider, instance key
|
|
16892
|
+
| `OpenApiConnectionConfig` | _type_ | name, spec, base URL, token provider, instance key, allowed hosts and `transports` |
|
|
16720
16893
|
|
|
16721
16894
|
---
|
|
16722
16895
|
|
|
@@ -17032,6 +17205,33 @@ SDK nor the `ai` peer.
|
|
|
17032
17205
|
| `createCli` | function | build and run a CLI from contracts — [guide](../guide/cli.md) |
|
|
17033
17206
|
| `defineCliCommand` | function | define one Zod-typed CLI-only executable command with optional validated-result presentation/exit policy |
|
|
17034
17207
|
| `parseCliArgs` | function | argv → typed tool args against a schema (advanced) |
|
|
17208
|
+
| `routeCliArgv` | function | select the command out of argv without duplicating the global-option grammar (advanced) |
|
|
17209
|
+
| `extractCliGlobalOptions` | function | lift the application's own global options out of argv before routing (advanced) |
|
|
17210
|
+
| `coerceJsonArgs` | function | the second half of `parseCliArgs` — parse array/object values a consumer sends itself, without the `stitchkit/tools` barrel |
|
|
17211
|
+
| `CliArgumentError` | class | the refusal `parseCliArgs` and the view flags raise; a CLI reports it and exits `2` |
|
|
17212
|
+
| `renderCliView` | function | compute an aggregate over a result (`--count-by`, `--sum`, `--top`, `--table`) — [guide](../guide/cli.md#aggregate-views) |
|
|
17213
|
+
| `createCliProfileStore` | function | named `0600` credential profiles resolved by name, with the never-substitute rule built in — [guide](../guide/cli.md#named-profiles) |
|
|
17214
|
+
| `CliProfileError` | class | an `AppError` refusing a named profile that is missing, unreadable or shared between users |
|
|
17215
|
+
| `renderCliInstaller` | function | generate the one-line installer from a build manifest, with the URL and digest substituted — [guide](../guide/cli.md#distribution-and-self-update) |
|
|
17216
|
+
| `checkCliUpdate` | function | bounded, interval-limited, never-throwing check for a newer published build |
|
|
17217
|
+
| `applyCliUpdate` | function | download, verify the decompressed digest and replace the binary by rename |
|
|
17218
|
+
| `assertCliPublishable` | function | refuse republishing one version from a different commit |
|
|
17219
|
+
| `selectCliBuildAsset` | function | the asset for one target, or `undefined` |
|
|
17220
|
+
| `currentCliBuildTarget` | function | `{ platform, arch }` of the running process |
|
|
17221
|
+
| `formatCliBuildStamp` | function | one line saying what the running build is |
|
|
17222
|
+
| `compareCliVersions` | function | compare two versions, or `undefined` when they are not comparable |
|
|
17223
|
+
| `CliBuildManifestSchema` / `CliBuildManifest` | schema / _type_ | name, version, commit, build time and assets |
|
|
17224
|
+
| `CliBuildAssetSchema` / `CliBuildAsset` | schema / _type_ | one download; `size` and `sha256` describe the **decompressed** bytes |
|
|
17225
|
+
| `CliBuildTargetSchema` / `CliBuildTarget` | schema / _type_ | `{ platform, arch }` |
|
|
17226
|
+
| `CliBuildStampSchema` / `CliBuildStamp` | schema / _type_ | the version/commit/build time carried inside a binary |
|
|
17227
|
+
| `CliInstallerConfig` | _type_ | manifest, asset, binary name and default install directory |
|
|
17228
|
+
| `CliUpdateCheckConfig` / `CliUpdateCheck` | _type_ | check inputs, and its four answers — `skipped`, `current`, `outdated`, `unknown` |
|
|
17229
|
+
| `CliUpdateApplyConfig` / `AppliedCliUpdate` | _type_ | apply inputs and the replaced path, byte count and digest |
|
|
17230
|
+
| `CliProfileStore` / `CliProfileStoreConfig` / `ResolvedCliProfile` | _type_ | the profile store, its directory/schema/hint config, and one resolution |
|
|
17231
|
+
| `CliResultView` | _type_ | the requested aggregate — `count`, `sum` or `table` |
|
|
17232
|
+
| `CliViewOutput` | _type_ | a JSON value, or the one human-facing text shape |
|
|
17233
|
+
| `CliGlobalOptionsParse` | _type_ | `{ argv, globals }` returned by `extractCliGlobalOptions` |
|
|
17234
|
+
| `CliArgvRoute` | _type_ | `{ command, commandArgv, topLevelHelp, version, error? }` returned by `routeCliArgv` |
|
|
17035
17235
|
| `pollUntilDone` | function | the generic `--wait` poller (advanced) |
|
|
17036
17236
|
| `emitResult` | function | write a pretty or compact `ToolResult` record to stdout/stderr + exit code (advanced) |
|
|
17037
17237
|
| `DEFAULT_EXIT_CODES` | const | the default `ToolResult.code` → exit-code map |
|
package/package.json
CHANGED