nylorun 0.17.0-beta → 0.18.0-beta

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 CHANGED
@@ -1,5 +1,34 @@
1
1
  # nylorun
2
2
 
3
+ ## 0.18.0-beta
4
+
5
+ ### Minor Changes
6
+
7
+ - eeb6294: Session usage as one ledger (protocol 18). The usage ledger keeps each model call's cost by token kind (uncached input, cache read, cache write, output) at the prices the adapter applied (migration 0037). For a protocol 18 client, `GET /v1/sessions/{id}/calls/model` returns each call's `startedAt`, `endedAt` and `usage.costSplit` (null for a call recorded before), and `GET /v1/sessions/{id}/usage` adds `costSplit` with `splitCalls`, and the calls' durations added up as `modelTimeMs` with `timedCalls`. A protocol 16 or 17 client reads usage as before. Studio's Usage tab is rebuilt on them: a Session card with one tokens bar and a ledger of tokens and cost by kind adding up to a total row, then the model calls grouped by turn with the same columns (cached, uncached, output, total, time, cost), and a row menu whose View event opens the call's event in the Events tab.
8
+
9
+ ### Patch Changes
10
+
11
+ - ed08ba8: Public text names no internal roadmap codes: the OpenAPI documents' descriptions, the type comments of `@nylorun/core`, `@nylorun/agents` and `@nylorun/admin`, and the CLI's help, README and generated compose file. A Runtime test fails when either OpenAPI document names one.
12
+ - ed08ba8: `nylo` and the deprecated `@nylorun/cli` name the release that removes them, nylorun 0.19, where they said "one release": `nylo`'s message ends "nylorun 0.19 removes nylo.", and `@nylorun/cli`'s stderr line, description and README say nylorun 0.19 removes both.
13
+ - ed08ba8: `--help` (or `-h`) works on every command and subcommand: it prints that command's own usage on stdout and exits 0, where `nylorun migrate v8 --help`, `nylorun tenant budgets --help` and others exited 2 with "Unknown option --help", and `nylorun tenant settings --help` printed the usage of `budgets`, `tool-policy` and `coverage` too. `nylorun --help` lists every `agents` command (`pin`, `track`, `restart`, `triggers`, `runs` and `memories` were missing) and `migrate v8 --name`, and the README lists `agents pin`, `track`, `restart`, `put --dry-run`, `fork --label` and `list --outdated`.
14
+ - ed08ba8: Webhook URLs name the Runtime's public URL. A trigger's `webhookUrl` is at `NYLORUN_PUBLIC_URL`, else the origin the request reached, so behind a reverse proxy that rewrites `Host` to `localhost:<port>` it is the proxy's URL, not `http://localhost:<port>`; the Runtime API's `401` challenges name it too. `nylorun start` passes `NYLORUN_PUBLIC_URL` from its shell to the runtime container (default `http://localhost:<port>`).
15
+ - Pin core to the tested release.
16
+ - Pin agents to the tested release.
17
+ - Pin admin to the tested release.
18
+ - Pin runtime to the tested release.
19
+ - Pin studio to the tested release.
20
+ - Updated dependencies [ed08ba8]
21
+ - Updated dependencies [ed08ba8]
22
+ - Updated dependencies [ed08ba8]
23
+ - Updated dependencies [ed08ba8]
24
+ - Updated dependencies [eeb6294]
25
+ - Updated dependencies [ed08ba8]
26
+ - Updated dependencies
27
+ - Updated dependencies
28
+ - @nylorun/core@0.25.0-beta
29
+ - @nylorun/agents@0.25.0-beta
30
+ - @nylorun/admin@0.19.1-beta
31
+
3
32
  ## 0.17.0-beta
4
33
 
5
34
  ### Minor Changes
@@ -11,7 +40,7 @@
11
40
 
12
41
  The SDK has `client.agentManifests`, `client.agents` and `composeManifest(dir)` for three-file authoring; the CLI has `nylorun apply`, `nylorun agent-manifests`, `nylorun agents` and `nylorun migrate v8`; Studio has an Agents screen, an Agent page and an Agent manifests screen. See MIGRATION.md.
13
42
 
14
- - 4fa160d: Agent memory, storage and API (track T5, D72). An agent manifest's `harness.memory` (`access`: `read_write` or `read_only`; `instructions`, at most 4,096 characters) turns on the Agent's memory: Markdown files under `/memories` it keeps across sessions, wakes and upgrades. The Runtime keeps them in Postgres (`memories`, `memory_versions`, migration 0036) with a version per change and who made it. `GET /v1/agents/{id}/memories` lists them (`pathPrefix`), `GET`, `PUT` (whole content, with an optional `contentSha256` precondition: `409 memory_conflict`) and `DELETE /v1/agents/{id}/memories/{path}` read, write and delete one (or a folder), and `GET /v1/agents/{id}/memory_versions` and `.../memory_versions/{versionId}` page the versions. A fork copies the memories (the fork plan adds `memories`); a delete removes them. `@nylorun/core` adds `memoryPathOf`, `memoryDescriptionOf` and the memory contracts; new Runtime limits `memoryBytes` (100 KB) and `memoriesPerAgent` (10,000); new error code `memory_conflict`. The SDK adds `client.agents.memories`; the CLI adds `nylorun agents memories ls|cat|put|rm|versions`. The model's `memory` tool arrives in the next steps of T5.
43
+ - 4fa160d: Agent memory, storage and API (track T5, D72). An agent manifest's `harness.memory` (`access`: `read_write` or `read_only`; `instructions`, at most 4,096 characters) turns on the Agent's memory: Markdown files under `/memories` it keeps across sessions, wakes and upgrades. The Runtime keeps them in Postgres (`memories`, `memory_versions`, migration 0036) with a version per change and who made it. `GET /v1/agents/{id}/memories` lists them (`pathPrefix`), `GET`, `PUT` (whole content, with an optional `contentSha256` precondition: `409 memory_conflict`) and `DELETE /v1/agents/{id}/memories/{path}` read, write and delete one (or a folder), and `GET /v1/agents/{id}/memory_versions` and `.../memory_versions/{versionId}` page the versions. A fork copies the memories (the fork plan adds `memories`); a delete removes them. `@nylorun/core` adds `memoryPathOf`, `memoryDescriptionOf` and the memory contracts; new Runtime limits `memoryBytes` (100 KiB) and `memoriesPerAgent` (10,000); new error code `memory_conflict`. The SDK adds `client.agents.memories`; the CLI adds `nylorun agents memories ls|cat|put|rm|versions`. The model's `memory` tool arrives in the next steps of T5.
15
44
  - 9e60e40: Protocol 17, a queue per session (track T4, D76). A message sent while the session's turn runs or waits is queued (`{status: "queued", queuedMessageId, position}`, `message.queued`) and starts the next turn when this one ends, with every queued message, each its own user message (`queue.taken`); a cancel ends the turn, not the queue. `delivery: "reject"` keeps the refusal, now `409 session_busy`, and a protocol 16 client's messages default to it, so the Host serves protocol 16 and 17. The queue holds at most the Runtime limit `sessionQueuedMessages` (`429 queue_full`), and session reads count `queuedMessages`. Our engine takes `{kind: "messages", messages}` as a turn's input. The SDK's `input` and `inputParts` take `delivery`, and the CLI's `sessions send` takes `--reject-if-busy`. See MIGRATION.md.
16
45
  - f85483d: Triggers (track T4, D74). An agent manifest's `runtime.triggers` declares `schedule` (cron with an IANA `timezone`) and `at` triggers, each with the session its runs go to (a session id, opened on the first run, or `new` for a session per run) and its prompt. They start active with a new Agent, fire on a durable timer per trigger with Postgres as the truth (one late fire after downtime, never a backfill, a fixed jitter per trigger), and each fire is a run (`started`, `queued`, `skipped` or `failed`, with a reason) at `GET /v1/agents/{id}/runs`. A schedule adds at most one queued message to a busy session. `GET /v1/agents/{id}/triggers` and `POST .../triggers/{triggerId}/pause`, `/resume` and `/run` (with an `Idempotency-Key`); Agent reads add `nextWakeAt`, and the Agent's stream adds `agent.run`, `agent.trigger_paused` and `agent.trigger_resumed`. New Runtime limits: `triggersPerAgent`, `triggerMinIntervalMs` and `triggerRunRetentionMs` (with floors), `triggerRunsPerHour`, `triggerJitterPercent`, `triggerJitterMaxMs`, `triggerPromptChars`; Host tuning `triggers.lateGraceMs` and `triggers.intervalCheckFires`. The SDK's `client.agents` adds `triggers`, `pauseTrigger`, `resumeTrigger`, `runTrigger` and `runs`; the CLI adds `nylorun agents triggers` and `nylorun agents runs`.
17
46
  - ae54de7: Webhook triggers (track T4, D75). A `webhook: "standard-webhooks"` trigger has an endpoint, `POST /v1/webhooks/{endpointId}`, an anonymous, unversioned route that takes any body up to the new Runtime limit `webhookBodyBytes` and verifies Standard Webhooks (`webhook-id`, `webhook-timestamp` within the Host's `triggers.webhookToleranceMs`, `webhook-signature`) in the keys service, which alone opens the trigger's sealed secret; anything else is `401` and writes nothing. A delivery runs the trigger once per `webhook-id` (`202 {runId, status}`); a paused trigger answers a signed one `409 trigger_paused`. `POST /v1/agents/{id}/triggers/{triggerId}/secret` makes or replaces the `whsec_` secret, shown once (`agent.trigger_secret_rotated`); trigger reads show `webhookUrl` and `secretSet`. The payload reaches the model under the trigger's prompt in a labelled `<trigger-payload>` block. The SDK adds `agents.rotateWebhookSecret`, and the CLI `nylorun agents triggers new-secret`.
package/README.md CHANGED
@@ -38,7 +38,8 @@ npx nylorun doctor # Node 24+, Docker, Compose v2, and the Tenan
38
38
  ## Commands
39
39
 
40
40
  The installation's lifecycle is a handful of top-level commands; everything else
41
- is in a group that follows the API it uses, and each group has its own `--help`.
41
+ is in a group that follows the API it uses. Every group and command takes `--help`
42
+ (or `-h`), which prints its own usage and exits 0.
42
43
 
43
44
  ```sh
44
45
  nylorun up|start [--tenant <name>] [--no-link] [--no-studio] [--allow-downgrade] [--studio-embed-origin <origin>]... [--studio-embed-origin-reset] [--restate-ui]
@@ -56,16 +57,19 @@ nylorun apply [<dir>...] [--dry-run] [--if-match <n>] # plan, then save each f
56
57
  nylorun migrate v8 <agent.json> [--runtime <environment-manifest.json>] [--name <name>] [--out <dir>] # the three files from a v7 manifest; reads no Runtime
57
58
  nylorun agent-manifests list [--json] # name, latest version, runtime kind, harness, Agents
58
59
  nylorun agent-manifests get <name>[@<n>] | versions <name> | rm <name>
59
- nylorun agents list [--state <s>] [--manifest <name>] [--label <key=value>]... [--behind] [--limit <n>] [--cursor <c>] [--json] # --behind: on an older manifest version
60
+ nylorun agents list [--state <s>] [--manifest <name>] [--label <key=value>]... [--outdated] [--limit <n>] [--cursor <c>] [--json] # --outdated (or --behind): moving to another manifest version
60
61
  nylorun agents get|stop|reset|rm|snapshot|versions <id>
61
- nylorun agents put <id> --manifest <name[@n]> [--owner <user>] [--label <key=value>]... # name tracks its latest version, name@n pins one
62
- nylorun agents fork <id> <new-id> [--manifest <name[@n]>] [--disk <latest|n|empty>] [--secrets <copy|none>] [--dry-run] # onto a new manifest version
62
+ nylorun agents put <id> --manifest <name[@n]> [--owner <user>] [--label <key=value>]... [--dry-run] # name tracks its latest version, name@n pins one; --dry-run prints the move
63
+ nylorun agents pin <id> <version> # move it to that manifest version, and keep it there
64
+ nylorun agents track <id> # move it to its manifest's latest version, and with each save
65
+ nylorun agents restart <id> [--retry] # apply the version it waits to start on now; --retry: the one that failed to start
66
+ nylorun agents fork <id> <new-id> [--manifest <name[@n]>] [--disk <latest|n|empty>] [--secrets <copy|none>] [--label <key=value>]... [--dry-run] # onto a new manifest version
63
67
  nylorun agents restore <id> <version|latest>
64
68
  nylorun agents delete-version <id> <version>
65
69
  nylorun agents events <id> [--from <n>] [--types <type,…>] [--since <time>] [--until <time>]
66
70
  nylorun agents triggers list <id> | pause|resume <id> <trigger> | run <id> <trigger> [--idempotency-key <k>] | new-secret <id> <trigger>
67
71
  nylorun agents runs <id> [--trigger <id>] [--limit <n>] [--cursor <c>] # what each trigger fire came to
68
- nylorun agents memories ls|cat|put|rm|versions <id> [<path>] [<file|->] # its memories (track T5); put seeds one
72
+ nylorun agents memories ls|cat|put|rm|versions <id> [<path>] [<file|->] # its memories; put seeds one
69
73
  nylorun agents secrets list|get|put|rm <id> [<name>] [<secret.json|->] # put without a file: a slot's value on stdin
70
74
  nylorun sessions list [--agent <id>] [--manifest <name>] [--status <s>] [--owner <user>] [--limit <n>] [--cursor <c>] [--json]
71
75
  nylorun sessions create --agent <id> --owner <user> [--id <id>] [--info <json>] # a session on Agent <id>
@@ -126,8 +130,8 @@ apply`; `nylorun configure` is `nylorun tenant models set`. `nylorun dev`,
126
130
  are gone.
127
131
 
128
132
  The package's second command, `nylo`, was the Runtime client until nylorun took
129
- its commands. For one release it names the `nylorun` command that replaced each
130
- of its own and exits 2:
133
+ its commands. Until nylorun 0.19, which removes it, it names the `nylorun`
134
+ command that replaced each of its own and exits 2:
131
135
 
132
136
  | `nylo` | `nylorun` |
133
137
  | --- | --- |
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import "./baseline.js";
3
3
  import { baselineEnv } from "./baseline.js";
4
4
  import { refuseNativeWindows, runCommand } from "./command.js";
5
- import { CliError } from "./errors.js";
5
+ import { CliError, HelpRequest, isHelp } from "./errors.js";
6
6
  import { defaultStackDeps, stackCommand, stackUsage, studioCommand } from "./stack/index.js";
7
7
  const usage = `nylorun <command>
8
8
 
@@ -16,15 +16,16 @@ Agent manifests:
16
16
  apply [<dir>...] [--dry-run] [--if-match <n>]
17
17
  save the agent manifest each folder's agent.manifest.json
18
18
  describes, with the files it names (Runtime API)
19
- migrate v8 <agent.json> [--runtime <environment-manifest.json>] [--out <dir>]
19
+ migrate v8 <agent.json> [--runtime <environment-manifest.json>] [--name <name>] [--out <dir>]
20
20
  write the three manifest files from an agent manifest v7
21
21
  and an environment manifest v1 (reads no Runtime)
22
22
 
23
23
  Groups, each with its own --help:
24
24
  agent-manifests list | get | versions | rm
25
25
  the Tenant's agent manifests (Runtime API)
26
- agents list | get | put | fork | stop | reset | rm | snapshot | restore
27
- versions | delete-version | events | secrets
26
+ agents list | get | put | pin | track | restart | fork | stop | reset | rm |
27
+ snapshot | restore | versions | delete-version | events | secrets |
28
+ triggers | runs | memories
28
29
  the Tenant's Agents, made from saved manifests; sessions
29
30
  run on them (Runtime API)
30
31
  sessions list | create | get | send | approve | respond | cancel | watch | history
@@ -183,6 +184,8 @@ async function main() {
183
184
  const [topic, ...options] = args;
184
185
  if (topic === "sandbox") {
185
186
  const usage = "Usage: nylorun doctor sandbox [--tenant <name>] [--json]";
187
+ if (options.some(isHelp))
188
+ throw new HelpRequest(usage);
186
189
  const tenantAt = options.indexOf("--tenant");
187
190
  const tenant = tenantAt < 0 ? undefined : options[tenantAt + 1];
188
191
  const rest = options.filter((_, index) => index !== tenantAt && index !== tenantAt + 1);
@@ -195,8 +198,11 @@ async function main() {
195
198
  });
196
199
  return;
197
200
  }
201
+ const usage = "Usage: nylorun doctor [--json] | nylorun doctor sandbox [--tenant <name>] [--json]";
202
+ if (args.some(isHelp))
203
+ throw new HelpRequest(usage);
198
204
  if (args.some((option) => option !== "--json"))
199
- throw usageError("Usage: nylorun doctor [--json] | nylorun doctor sandbox [--tenant <name>] [--json]");
205
+ throw usageError(usage);
200
206
  const { doctorStack } = await import("./doctor.js");
201
207
  process.exitCode = await doctorStack({ json: args.includes("--json"), env });
202
208
  return;
@@ -1,4 +1,4 @@
1
- import type { StackDeps } from "../stack/commands.js";
1
+ import { type StackDeps } from "../stack/commands.js";
2
2
  export declare const agentsUsage: string;
3
3
  /** `nylorun agents <command> ...` */
4
4
  export declare function agentsCommand(deps: StackDeps, args: readonly string[]): Promise<number>;
@@ -1,4 +1,6 @@
1
1
  import { parseAgentManifestRef } from "@nylorun/core/contracts";
2
+ import { HelpRequest, isHelp } from "../errors.js";
3
+ import { usageLinesOf } from "../stack/commands.js";
2
4
  import { readSecret, readText } from "./io.js";
3
5
  import { integerFlag, labelsOf, parseFlags, positional, printCursor, printJson, printTable, readJson, runtimeCall, runtimeClient, runtimeFlagsUsage, usageError, } from "./runtime.js";
4
6
  export const agentsUsage = `nylorun agents <command>
@@ -56,7 +58,7 @@ NYLORUN_TOKEN).
56
58
  what each fire came to (started, queued, skipped,
57
59
  failed), newest first, as JSON
58
60
  memories ls <id> [--prefix <folder>] [--limit <n>] [--cursor <c>]
59
- its memories (track T5) by path, as JSON
61
+ its memories by path, as JSON
60
62
  memories cat <id> <path> one memory's content
61
63
  memories put <id> <path> <file|-> write a memory whole (seed it), from a file or stdin
62
64
  memories rm <id> <path> delete a memory, or every memory in a folder
@@ -292,6 +294,8 @@ async function events(deps, args) {
292
294
  }
293
295
  async function secrets(deps, args) {
294
296
  const [verb, ...rest] = args;
297
+ if (isHelp(verb))
298
+ throw new HelpRequest(usageLinesOf(agentsUsage, "secrets"));
295
299
  if (verb === "list" || verb === "ls") {
296
300
  const usage = "nylorun agents secrets list <id>";
297
301
  const flags = parseFlags(rest, {}, usage);
@@ -342,6 +346,8 @@ async function secrets(deps, args) {
342
346
  /** `nylorun agents triggers <list|pause|resume|run> …` (track T4). */
343
347
  async function triggers(deps, args) {
344
348
  const [verb, ...rest] = args;
349
+ if (isHelp(verb))
350
+ throw new HelpRequest(usageLinesOf(agentsUsage, "triggers"));
345
351
  if (verb === "list" || verb === "ls") {
346
352
  const usage = "nylorun agents triggers list <id>";
347
353
  const flags = parseFlags(rest, {}, usage);
@@ -380,6 +386,8 @@ async function triggers(deps, args) {
380
386
  /** `nylorun agents memories <ls|cat|put|rm|versions> …` (track T5). */
381
387
  async function memories(deps, args) {
382
388
  const [verb, ...rest] = args;
389
+ if (isHelp(verb))
390
+ throw new HelpRequest(usageLinesOf(agentsUsage, "memories"));
383
391
  const pageOf = (flags) => {
384
392
  const limit = flags.values.get("--limit");
385
393
  if (limit !== undefined && !/^[1-9]\d*$/.test(limit))
@@ -8,7 +8,7 @@
8
8
  * removed in protocol 10, and says so.
9
9
  */
10
10
  import { PROTOCOL_HEADER, PROTOCOL_VERSION } from "@nylorun/core/compatibility";
11
- import { CliError } from "../errors.js";
11
+ import { CliError, HelpRequest, isHelp } from "../errors.js";
12
12
  import { parseStackFlags } from "../stack/commands.js";
13
13
  import { connect } from "./target.js";
14
14
  export const mcpUsage = ` mcp inspect <url> [--server <name>] [--agent <id>] [--sse] [--tenant <name>] [--json]
@@ -151,6 +151,8 @@ function oneLine(text) {
151
151
  /** `nylorun tenant mcp <inspect|connect> ...` */
152
152
  export async function mcpCommand(deps, args) {
153
153
  const [sub, ...rest] = args;
154
+ if (isHelp(sub))
155
+ throw new HelpRequest(`Usage:\n${mcpUsage}`);
154
156
  if (sub === "inspect")
155
157
  return inspect(deps, rest);
156
158
  if (sub === "connect")
@@ -5,7 +5,7 @@
5
5
  * for CI. A key is never an argument.
6
6
  */
7
7
  import { randomUUID } from "node:crypto";
8
- import { CliError } from "../errors.js";
8
+ import { CliError, HelpRequest, isHelp } from "../errors.js";
9
9
  import { parseStackFlags } from "../stack/commands.js";
10
10
  import { printJson, printTable, readSecret } from "./io.js";
11
11
  import { connect, managementClient } from "./target.js";
@@ -140,6 +140,8 @@ async function select(deps, args) {
140
140
  /** `nylorun tenant models <verb> ...` */
141
141
  export async function modelsCommand(deps, args) {
142
142
  const [verb, ...rest] = args;
143
+ if (isHelp(verb))
144
+ throw new HelpRequest(`Usage:\n${modelsUsage}`);
143
145
  if (verb === "catalog" || verb === "providers" || verb === "get")
144
146
  return read(deps, verb, rest);
145
147
  if (verb === "set")
@@ -10,7 +10,7 @@ export declare const RUNTIME_FLAGS: {
10
10
  export declare const runtimeFlagsUsage: string;
11
11
  export declare const usageError: (message: string) => CliError;
12
12
  export type Flags = ReturnType<typeof parseStackFlags>;
13
- /** `args` parsed with the Runtime flags added to `allowed`. */
13
+ /** `args` parsed with the Runtime flags added to `allowed`; `--help` names them too. */
14
14
  export declare function parseFlags(args: readonly string[], allowed: {
15
15
  booleans?: readonly string[];
16
16
  values?: readonly string[];
@@ -5,16 +5,18 @@
5
5
  */
6
6
  import { readFile } from "node:fs/promises";
7
7
  import { AgentsClient, RuntimeError, SUBJECT_SCOPES, } from "@nylorun/agents";
8
- import { CliError } from "../errors.js";
9
- import { parseStackFlags } from "../stack/commands.js";
8
+ import { CliError, HelpRequest, isHelp } from "../errors.js";
9
+ import { parseStackFlags, usageText } from "../stack/commands.js";
10
10
  import { connect } from "./target.js";
11
11
  /** Flags every Runtime API command takes. */
12
12
  export const RUNTIME_FLAGS = { values: ["--tenant", "--as"], lists: ["--scope"] };
13
13
  export const runtimeFlagsUsage = `Every command takes --tenant <name>, and --as <subject> [--scope <scope>]... to act for a
14
14
  person (default scope sessions:own; scopes: ${SUBJECT_SCOPES.join(", ")}).`;
15
15
  export const usageError = (message) => new CliError(message, 2);
16
- /** `args` parsed with the Runtime flags added to `allowed`. */
16
+ /** `args` parsed with the Runtime flags added to `allowed`; `--help` names them too. */
17
17
  export function parseFlags(args, allowed, usage) {
18
+ if (args.some(isHelp))
19
+ throw new HelpRequest(`${usageText(usage)}\n\n${runtimeFlagsUsage}`);
18
20
  return parseStackFlags(args, {
19
21
  booleans: allowed.booleans ?? [],
20
22
  values: [...RUNTIME_FLAGS.values, ...(allowed.values ?? [])],
@@ -258,9 +258,9 @@ async function trace(deps, args) {
258
258
  async function exec(deps, args) {
259
259
  const usage = "nylorun sessions exec <id> [--timeout <s>] -- <command>...";
260
260
  const split = args.indexOf("--");
261
+ const flags = parseFlags(split < 0 ? args : args.slice(0, split), { values: ["--timeout"] }, usage);
261
262
  if (split < 0 || split === args.length - 1)
262
263
  throw usageError(`Usage: ${usage}`);
263
- const flags = parseFlags(args.slice(0, split), { values: ["--timeout"] }, usage);
264
264
  const [id] = positional(flags, 1, usage);
265
265
  const timeout = integerFlag(flags, "--timeout");
266
266
  const command = args.slice(split + 1).join(" ");
@@ -1,6 +1,6 @@
1
1
  import { PutModelBudgetsRequestSchema, PutTenantArtifactsRequestSchema, PutTenantEnvironmentsRequestSchema, PutTenantLimitsRequestSchema, PutTenantSkillsRequestSchema, PutToolPolicyRequestSchema, } from "@nylorun/core/contracts";
2
- import { CliError } from "../errors.js";
3
- import { parseStackFlags } from "../stack/commands.js";
2
+ import { CliError, HelpRequest, isHelp } from "../errors.js";
3
+ import { parseStackFlags, usageLinesOf } from "../stack/commands.js";
4
4
  import { printJson, printTable, readBody } from "./io.js";
5
5
  import { connect, managementClient } from "./target.js";
6
6
  export const settingsUsage = ` budgets [get | put <file|->] [--tenant <name>]
@@ -77,6 +77,8 @@ export const toolPolicyCommand = (deps, args) => documentCommand(deps, toolPolic
77
77
  /** `nylorun tenant settings sandbox | artifacts | skills | limits ...` */
78
78
  export async function settingsCommand(deps, args) {
79
79
  const [which, ...rest] = args;
80
+ if (isHelp(which))
81
+ throw new HelpRequest(usageLinesOf(settingsUsage, "settings"));
80
82
  if (which === "sandbox")
81
83
  return documentCommand(deps, sandbox, rest);
82
84
  if (which === "artifacts")
@@ -85,7 +87,7 @@ export async function settingsCommand(deps, args) {
85
87
  return documentCommand(deps, skillSettings, rest);
86
88
  if (which === "limits")
87
89
  return documentCommand(deps, limitSettings, rest);
88
- throw usageError();
90
+ throw usageError(usageLinesOf(settingsUsage, "settings"));
89
91
  }
90
92
  const COVERAGE_USAGE = "nylorun tenant coverage --agent <id> [--tenant <name>] [--json]";
91
93
  /**
@@ -3,7 +3,7 @@
3
3
  * tokens, through the Management API. (Subject tokens, the access policy, browser keys and
4
4
  * revocations left the Runtime in protocol 7.)
5
5
  */
6
- import { CliError } from "../errors.js";
6
+ import { CliError, HelpRequest, isHelp } from "../errors.js";
7
7
  import { parseStackFlags } from "../stack/commands.js";
8
8
  import { connect, managementClient } from "./target.js";
9
9
  export const signingKeysUsage = ` signing-keys list [--tenant <name>]
@@ -15,6 +15,8 @@ export const signingKeysUsage = ` signing-keys list [--tenant <name>]
15
15
  const usageError = (message = `Usage:\n${signingKeysUsage}`) => new CliError(message, 2);
16
16
  export async function signingKeysCommand(deps, input) {
17
17
  const [action, ...args] = input;
18
+ if (isHelp(action))
19
+ throw new HelpRequest(`Usage:\n${signingKeysUsage}`);
18
20
  const flags = parseStackFlags(args, { booleans: action === "rotate" ? ["--force"] : [], values: ["--tenant"] }, signingKeysUsage);
19
21
  const arity = action === "revoke" ? 1 : 0;
20
22
  if (!["list", "rotate", "revoke"].includes(action ?? "") || flags.rest.length !== arity)
@@ -1,4 +1,4 @@
1
- import { CliError } from "../errors.js";
1
+ import { CliError, HelpRequest, isHelp } from "../errors.js";
2
2
  import { parseStackFlags } from "../stack/commands.js";
3
3
  import { keyCommand, keyUsage } from "../stack/keys.js";
4
4
  import { printJson, printTable } from "./io.js";
@@ -101,6 +101,8 @@ async function reset(deps, args) {
101
101
  * (application keys only), else on the local Tenant through `nylorun-operate`.
102
102
  */
103
103
  async function keys(deps, args) {
104
+ if (isHelp(args[0]))
105
+ throw new HelpRequest(`Usage:\n${keyUsage}`);
104
106
  if (args.includes("--tenant") || !remoteConnection(deps.env, "management"))
105
107
  return keyCommand(deps, args);
106
108
  const [verb, ...rest] = args;
package/dist/command.d.ts CHANGED
@@ -3,6 +3,6 @@ export declare function refuseNativeWindows(): void;
3
3
  /**
4
4
  * Run a command (`nylorun` or `nylo`) and exit once stdout and stderr are flushed: with
5
5
  * `process.exitCode` (0 when unset), or after printing a failure's message with its
6
- * `CliError` exit code (else 1).
6
+ * `CliError` exit code (else 1); a `HelpRequest`'s usage on stdout, exiting 0.
7
7
  */
8
8
  export declare function runCommand(main: () => Promise<void>): void;
package/dist/command.js CHANGED
@@ -1,4 +1,4 @@
1
- import { CliError } from "./errors.js";
1
+ import { CliError, HelpRequest } from "./errors.js";
2
2
  /** Both commands refuse native Windows before acting; WSL2 runs them. */
3
3
  export function refuseNativeWindows() {
4
4
  if (process.platform === "win32")
@@ -7,10 +7,14 @@ export function refuseNativeWindows() {
7
7
  /**
8
8
  * Run a command (`nylorun` or `nylo`) and exit once stdout and stderr are flushed: with
9
9
  * `process.exitCode` (0 when unset), or after printing a failure's message with its
10
- * `CliError` exit code (else 1).
10
+ * `CliError` exit code (else 1); a `HelpRequest`'s usage on stdout, exiting 0.
11
11
  */
12
12
  export function runCommand(main) {
13
13
  void main().then(() => finish(process.exitCode === undefined ? 0 : Number(process.exitCode)), (error) => {
14
+ if (error instanceof HelpRequest) {
15
+ console.log(error.message);
16
+ return finish(0);
17
+ }
14
18
  console.error(error instanceof Error ? error.message : String(error));
15
19
  return finish(error instanceof CliError ? error.exitCode : 1);
16
20
  });
package/dist/errors.d.ts CHANGED
@@ -3,3 +3,9 @@ export declare class CliError extends Error {
3
3
  readonly exitCode: number;
4
4
  constructor(message: string, exitCode?: number);
5
5
  }
6
+ /** `--help` or `-h`, which every command and group takes. */
7
+ export declare const isHelp: (arg: string | undefined) => boolean;
8
+ /** A command's usage, asked for with `--help`: printed on stdout, exit 0. */
9
+ export declare class HelpRequest extends CliError {
10
+ constructor(usage: string);
11
+ }
package/dist/errors.js CHANGED
@@ -7,3 +7,12 @@ export class CliError extends Error {
7
7
  this.name = "CliError";
8
8
  }
9
9
  }
10
+ /** `--help` or `-h`, which every command and group takes. */
11
+ export const isHelp = (arg) => arg === "--help" || arg === "-h";
12
+ /** A command's usage, asked for with `--help`: printed on stdout, exit 0. */
13
+ export class HelpRequest extends CliError {
14
+ constructor(usage) {
15
+ super(usage, 0);
16
+ this.name = "HelpRequest";
17
+ }
18
+ }
package/dist/nylo.js CHANGED
@@ -3,8 +3,8 @@ import "./baseline.js";
3
3
  import { runCommand } from "./command.js";
4
4
  import { CliError } from "./errors.js";
5
5
  /**
6
- * `nylo` became part of `nylorun`. For one release this command names the `nylorun` command that
7
- * replaces each of its own and exits 2; a later release removes it.
6
+ * `nylo` became part of `nylorun`. Until nylorun 0.19, which removes it, this command names the
7
+ * `nylorun` command that replaces each of its own and exits 2.
8
8
  */
9
9
  const REPLACEMENTS = {
10
10
  status: "nylorun status",
@@ -16,9 +16,9 @@ const REPLACEMENTS = {
16
16
  };
17
17
  function nyloMessage(argv) {
18
18
  const replacement = REPLACEMENTS[argv[0] ?? ""];
19
- return replacement
19
+ return `${replacement
20
20
  ? `nylo is part of nylorun now: nylo ${argv[0]} is ${replacement}.`
21
- : "nylo is part of nylorun now: run nylorun --help.";
21
+ : "nylo is part of nylorun now: run nylorun --help."} nylorun 0.19 removes nylo.`;
22
22
  }
23
23
  runCommand(async () => {
24
24
  throw new CliError(nyloMessage(process.argv.slice(2)), 2);
@@ -51,6 +51,17 @@ interface Flags {
51
51
  /** Options that may repeat, in order. */
52
52
  lists: Map<string, string[]>;
53
53
  }
54
+ /** A command's `usage` line (or block, when it starts indented), as `--help` prints it. */
55
+ export declare const usageText: (usage: string) => string;
56
+ /**
57
+ * The lines of a group's usage block that start with command `name` (`secrets`, `settings`),
58
+ * with the lines that continue them, as `--help` prints them.
59
+ */
60
+ export declare function usageLinesOf(usage: string, name: string): string;
61
+ /**
62
+ * `args` as the options `allowed` names, and the rest. `--help` or `-h` anywhere is a
63
+ * `HelpRequest` for `usage`; an option `allowed` does not name is a usage error.
64
+ */
54
65
  export declare function parseStackFlags(args: readonly string[], allowed: {
55
66
  booleans?: readonly string[];
56
67
  values?: readonly string[];
@@ -3,7 +3,7 @@ import { mkdir, readFile, rm } from "node:fs/promises";
3
3
  import { basename, resolve } from "node:path";
4
4
  import { compareVersions } from "@nylorun/core/compatibility";
5
5
  import { findProjectRoot } from "@nylorun/core/project";
6
- import { CliError } from "../errors.js";
6
+ import { CliError, HelpRequest, isHelp } from "../errors.js";
7
7
  import { credentialsPath, readCredentialsFile, readProjectCredentials, readProjectLink, writeProjectCredentials, writeProjectLink, } from "../project/link.js";
8
8
  import { seedTenant } from "../project/seed.js";
9
9
  import { readTelemetry, STUDIO_ANALYTICS_ID, TELEMETRY_NOTICE, telemetryDecision, writeTelemetry, } from "../telemetry.js";
@@ -154,6 +154,27 @@ function requireStackFiles(ctx) {
154
154
  if (!existsSync(ctx.paths.compose) || !existsSync(ctx.paths.env))
155
155
  throw new CliError(`No Tenant ${ctx.name} under ${ctx.paths.root}. Run "nylorun start" first.`, 3);
156
156
  }
157
+ /** A command's `usage` line (or block, when it starts indented), as `--help` prints it. */
158
+ export const usageText = (usage) => (/^\s/.test(usage) ? `Usage:\n${usage}` : `Usage: ${usage}`);
159
+ /**
160
+ * The lines of a group's usage block that start with command `name` (`secrets`, `settings`),
161
+ * with the lines that continue them, as `--help` prints them.
162
+ */
163
+ export function usageLinesOf(usage, name) {
164
+ let keep = false;
165
+ const lines = usage.split("\n").filter((line) => {
166
+ if (/^ {2}\S/.test(line))
167
+ keep = line === ` ${name}` || line.startsWith(` ${name} `);
168
+ else if (!/^ {3}/.test(line))
169
+ keep = false;
170
+ return keep;
171
+ });
172
+ return `Usage:\n${lines.join("\n")}`;
173
+ }
174
+ /**
175
+ * `args` as the options `allowed` names, and the rest. `--help` or `-h` anywhere is a
176
+ * `HelpRequest` for `usage`; an option `allowed` does not name is a usage error.
177
+ */
157
178
  export function parseStackFlags(args, allowed, usage) {
158
179
  const booleans = new Set();
159
180
  const values = new Map();
@@ -161,6 +182,8 @@ export function parseStackFlags(args, allowed, usage) {
161
182
  const rest = [];
162
183
  for (let index = 0; index < args.length; index += 1) {
163
184
  const raw = args[index];
185
+ if (isHelp(raw))
186
+ throw new HelpRequest(usageText(usage));
164
187
  const arg = allowed.aliases?.[raw] ?? raw;
165
188
  // `-` alone is an argument: standard input.
166
189
  if (arg === "-" || !arg.startsWith("-")) {
@@ -143,7 +143,7 @@ ${healthcheck("restate")} restart: unless-stopped
143
143
  # Its reachability is in the Tenant's status (GET /v1/tenant, streams.reachable).
144
144
  restart: unless-stopped
145
145
 
146
- rustfs: # the Object store (D35); not published, only the runtime and the gateway hold its credential
146
+ rustfs: # the Object store; not published, only the runtime and the gateway hold its credential
147
147
  image: ${PINNED_IMAGES.rustfs}
148
148
  container_name: ${project}-rustfs
149
149
  labels: *tenant
@@ -222,7 +222,9 @@ ${healthcheck("gateway")} restart: unless-stopped
222
222
  # Host headers the Runtime accepts: its name on the Compose network, and the
223
223
  # published port as clients on this machine address it.
224
224
  NYLORUN_ALLOWED_HOSTS: runtime:4000,localhost:\${NYLORUN_PORT},127.0.0.1:\${NYLORUN_PORT}
225
- NYLORUN_PUBLIC_URL: http://localhost:\${NYLORUN_PORT}
225
+ # The URL clients use: webhook URLs and the OAuth resource. Behind a reverse proxy, its URL
226
+ # (\`NYLORUN_PUBLIC_URL=https://runtime.example.com nylorun start\`).
227
+ NYLORUN_PUBLIC_URL: \${NYLORUN_PUBLIC_URL:-http://localhost:\${NYLORUN_PORT}}
226
228
  NYLORUN_DATABASE_URL: postgres://nylorun:\${NYLORUN_POSTGRES_PASSWORD}@postgres:5432/nylorun
227
229
  NYLORUN_RESTATE_INGRESS_URL: http://restate:8080
228
230
  NYLORUN_RESTATE_ADMIN_URL: http://restate:9070
@@ -243,7 +245,7 @@ ${healthcheck("gateway")} restart: unless-stopped
243
245
  NYLORUN_HARNESS_LISTEN_PORT: "4200"
244
246
  NYLORUN_HARNESS_ALLOWED_HOSTS: runtime:4200${sandboxes ? SANDBOXES_HARNESS_HOST : ""}
245
247
  NYLORUN_HARNESS_TOKEN: \${NYLORUN_HARNESS_TOKEN:?run nylorun start}
246
- # Traces (R7.2): from the shell that runs \`nylorun start\`. Unset, they are
248
+ # Traces: from the shell that runs \`nylorun start\`. Unset, they are
247
249
  # empty and the Runtime exports nothing. A collector on this machine is host.docker.internal.
248
250
  OTEL_EXPORTER_OTLP_ENDPOINT: \${OTEL_EXPORTER_OTLP_ENDPOINT:-}
249
251
  OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: \${OTEL_EXPORTER_OTLP_TRACES_ENDPOINT:-}
package/dist/telemetry.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { randomBytes } from "node:crypto";
2
2
  import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
3
3
  import { join } from "node:path";
4
- import { CliError } from "./errors.js";
4
+ import { CliError, HelpRequest, isHelp } from "./errors.js";
5
5
  /**
6
6
  * Studio's anonymous usage analytics. Studio's dashboard reports page views,
7
7
  * with every id replaced by `:id`, to this Google Analytics property unless the
@@ -62,6 +62,8 @@ export function telemetryDecision(env, file) {
62
62
  const USAGE = "Usage: nylorun local telemetry [status|enable|disable]";
63
63
  /** `nylorun local telemetry [status|enable|disable]`. */
64
64
  export async function telemetryCommand(args, context) {
65
+ if (args.some(isHelp))
66
+ throw new HelpRequest(USAGE);
65
67
  const [action = "status", ...rest] = args;
66
68
  if (rest.length || !["status", "enable", "disable"].includes(action))
67
69
  throw new CliError(USAGE, 2);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nylorun",
3
- "version": "0.17.0-beta",
3
+ "version": "0.18.0-beta",
4
4
  "type": "module",
5
5
  "license": "Apache-2.0",
6
6
  "description": "Set up and run local Nylorun Tenants (Runtime and Studio) with Docker Compose, and act on a Tenant through its Runtime and Management APIs",
@@ -30,9 +30,9 @@
30
30
  "prepack": "npm run build"
31
31
  },
32
32
  "dependencies": {
33
- "@nylorun/admin": "0.19.0-beta",
34
- "@nylorun/agents": "0.24.0-beta",
35
- "@nylorun/core": "0.24.0-beta"
33
+ "@nylorun/admin": "0.19.1-beta",
34
+ "@nylorun/agents": "0.25.0-beta",
35
+ "@nylorun/core": "0.25.0-beta"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@earendil-works/pi-ai": "1.2.0",
@@ -46,7 +46,7 @@
46
46
  "nylo": "dist/nylo.js"
47
47
  },
48
48
  "nylorun": {
49
- "runtime": "0.28.0-beta",
50
- "studio": "0.27.0-beta"
49
+ "runtime": "0.29.0-beta",
50
+ "studio": "0.28.0-beta"
51
51
  }
52
52
  }