nexus-ai-pro 2.20.2 → 2.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/API_STABILITY.md +30 -0
- package/CHANGELOG.md +76 -1
- package/README.md +7 -6
- package/dist/adapter-kit/gather.d.ts +76 -0
- package/dist/adapter-kit/gather.js +147 -0
- package/dist/adapter-kit/index.d.ts +1 -0
- package/dist/adapter-kit/index.js +1 -0
- package/dist/adapter-kit/record.d.ts +3 -1
- package/dist/adapter-kit/record.js +20 -5
- package/dist/cli/adapters.d.ts +4 -3
- package/dist/cli/adapters.js +71 -2
- package/dist/cli/doctor.js +3 -0
- package/dist/cli.js +3 -1
- package/dist/core/config-builder.d.ts +1 -0
- package/dist/core/config-builder.js +1 -0
- package/dist/core/defaults.d.ts +34 -0
- package/dist/core/defaults.js +39 -0
- package/dist/decisions/completion.d.ts +10 -7
- package/dist/decisions/completion.js +80 -6
- package/dist/decisions/core.d.ts +88 -43
- package/dist/decisions/core.js +115 -28
- package/dist/decisions/errors.d.ts +25 -0
- package/dist/decisions/errors.js +12 -0
- package/dist/decisions/index.d.ts +6 -5
- package/dist/decisions/index.js +2 -1
- package/dist/decisions/integrations.d.ts +14 -5
- package/dist/decisions/integrations.js +16 -1
- package/dist/decisions/mock.d.ts +5 -3
- package/dist/decisions/mock.js +4 -1
- package/dist/decisions/pictures.d.ts +43 -0
- package/dist/decisions/pictures.js +103 -0
- package/dist/decisions/typesafe.d.ts +25 -5
- package/dist/decisions/typesafe.js +101 -7
- package/dist/doctor/index.d.ts +6 -9
- package/dist/doctor/index.js +41 -0
- package/dist/operations/adapters.d.ts +1 -0
- package/dist/operations/adapters.js +3 -1
- package/dist/protocols/a2a.js +22 -3
- package/dist/providers/openai.js +13 -0
- package/dist/server/deployments.d.ts +3 -2
- package/dist/server/deployments.js +6 -1
- package/dist/tracing/stores.d.ts +2 -0
- package/dist/tracing/stores.js +1 -0
- package/dist/tracing/tracer.d.ts +5 -2
- package/dist/tracing/tracer.js +6 -3
- package/dist/types/tracing.d.ts +6 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist-cjs/adapter-kit/gather.d.ts +76 -0
- package/dist-cjs/adapter-kit/gather.js +152 -0
- package/dist-cjs/adapter-kit/index.d.ts +1 -0
- package/dist-cjs/adapter-kit/index.js +4 -1
- package/dist-cjs/adapter-kit/record.d.ts +3 -1
- package/dist-cjs/adapter-kit/record.js +20 -5
- package/dist-cjs/cli/adapters.d.ts +4 -3
- package/dist-cjs/cli/adapters.js +70 -1
- package/dist-cjs/cli/doctor.js +3 -0
- package/dist-cjs/core/config-builder.d.ts +1 -0
- package/dist-cjs/core/config-builder.js +5 -1
- package/dist-cjs/core/defaults.d.ts +34 -0
- package/dist-cjs/core/defaults.js +44 -0
- package/dist-cjs/decisions/completion.d.ts +10 -7
- package/dist-cjs/decisions/completion.js +79 -5
- package/dist-cjs/decisions/core.d.ts +88 -43
- package/dist-cjs/decisions/core.js +134 -46
- package/dist-cjs/decisions/errors.d.ts +25 -0
- package/dist-cjs/decisions/errors.js +16 -0
- package/dist-cjs/decisions/index.d.ts +6 -5
- package/dist-cjs/decisions/index.js +5 -1
- package/dist-cjs/decisions/integrations.d.ts +14 -5
- package/dist-cjs/decisions/integrations.js +16 -1
- package/dist-cjs/decisions/mock.d.ts +5 -3
- package/dist-cjs/decisions/mock.js +4 -1
- package/dist-cjs/decisions/pictures.d.ts +43 -0
- package/dist-cjs/decisions/pictures.js +109 -0
- package/dist-cjs/decisions/typesafe.d.ts +25 -5
- package/dist-cjs/decisions/typesafe.js +106 -12
- package/dist-cjs/doctor/index.d.ts +6 -9
- package/dist-cjs/doctor/index.js +41 -0
- package/dist-cjs/operations/adapters.d.ts +1 -0
- package/dist-cjs/operations/adapters.js +3 -1
- package/dist-cjs/protocols/a2a.js +22 -3
- package/dist-cjs/providers/openai.js +13 -0
- package/dist-cjs/server/deployments.d.ts +3 -2
- package/dist-cjs/server/deployments.js +6 -1
- package/dist-cjs/tracing/stores.d.ts +2 -0
- package/dist-cjs/tracing/stores.js +1 -0
- package/dist-cjs/tracing/tracer.d.ts +5 -2
- package/dist-cjs/tracing/tracer.js +6 -3
- package/dist-cjs/types/tracing.d.ts +6 -0
- package/dist-cjs/version.d.ts +1 -1
- package/dist-cjs/version.js +1 -1
- package/package.json +1 -1
package/API_STABILITY.md
CHANGED
|
@@ -67,6 +67,36 @@ graduation:
|
|
|
67
67
|
| Upgrade | A migration test from the previous minor |
|
|
68
68
|
| Soak | A long-running test with flat memory and no leaked handles |
|
|
69
69
|
|
|
70
|
+
## 2.21 stage (2.21.0)
|
|
71
|
+
|
|
72
|
+
Everything 2.21 adds is stable from the start and additive, unless this section says otherwise.
|
|
73
|
+
|
|
74
|
+
New and stable, in `nexus-ai-pro/decisions`: `BoxQuestion`, `BoxAnswer`, `DecisionBox`, `boxOverlap()`,
|
|
75
|
+
`statePictures()`, `readPicture()`, `DecisionPicture`, `ReadDecisionPicture`, the question type `box`
|
|
76
|
+
and its `DecisionDistribution`, `DecisionProviderCapabilities.images`, `answeredRight()`'s box and
|
|
77
|
+
`overlap`, `DecisionEvaluatorOptions.where` and `overlap`, `DecisionConformanceOptions.picture`,
|
|
78
|
+
`DecisionConformanceReport.skipped`, and `CompletionDecisionsOptions.images`; in
|
|
79
|
+
`nexus-ai-pro/decisions/typesafe`, `TypeSafeDecisionProviderOptions.media`; in `nexus-ai-pro/adapter-kit`,
|
|
80
|
+
`gatherRecords()`, `GatherRecordsOptions`, `GatherReport`, `GatheredSource`, `GatherOutcome`,
|
|
81
|
+
`RecordSource`, and `ADAPTER_KEYWORD`; the command `nexus adapters gather`; a package's
|
|
82
|
+
`nexus.compatibility` field, the URL of its record; in `nexus-ai-pro/config`, `useDefaults()`,
|
|
83
|
+
`nexusDefaults()`, `NexusDefaults`, `DEFAULT_CHANGES`, and `DefaultChange`, and the `NEXUS_DEFAULTS`
|
|
84
|
+
variable; `DoctorOptions.upgrade` and `nexus doctor --upgrade`; and `TraceStore.durable`. The
|
|
85
|
+
preview turns on exactly what 3.0 will; should 3.0 change its defaults again before it ships, the
|
|
86
|
+
preview follows, and `DEFAULT_CHANGES` says so. A box from TypeSafe AI rests
|
|
87
|
+
on what it marks as experimental, its pictures and boxes, whose request format may change between its
|
|
88
|
+
releases.
|
|
89
|
+
|
|
90
|
+
Changed on purpose: TypeSafe AI's 400 and 413 are `DECISION_INVALID`, not `DECISION_FAILED`; neither
|
|
91
|
+
was retried before or is now. A provider of your own that reads pictures declares `images: true`;
|
|
92
|
+
without it, a decision about a picture is not sent to it. A union over `DecisionQuestionType` or
|
|
93
|
+
`DecisionAnswer` that handled three kinds now meets a fourth, `box`. `compatibilityPage()` writes a
|
|
94
|
+
repository that is not an `http` or `https` address as text, not a link, and escapes a record's
|
|
95
|
+
Markdown. A doctor report has one more check, `upgrade`, skipped unless asked for. With `tenants`, an
|
|
96
|
+
A2A handler answers `/{tenant}/.well-known/agent-card.json` with that tenant's card, where it answered
|
|
97
|
+
the card itself; without `tenants`, nothing changes. A model on OpenAI's Responses API is sent
|
|
98
|
+
`include` and `top_logprobs` when a request asks for log probabilities, which it was not before.
|
|
99
|
+
|
|
70
100
|
## 2.20 stage (2.20.0)
|
|
71
101
|
|
|
72
102
|
Everything 2.20 adds is stable from the start and additive, unless this section says otherwise.
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,80 @@ Notable changes to this project are recorded here. The format follows [Keep a Ch
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [2.21.0] - 2026-10-26
|
|
8
|
+
|
|
9
|
+
Typed decisions about pictures, compatibility records gathered from community adapters' own CI, and
|
|
10
|
+
3.0's defaults behind one switch today; with log probabilities from OpenAI's Responses API, and a card
|
|
11
|
+
of its own for each tenant of an A2A agent.
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- **Decisions about pictures.** A decision's state may hold pictures, as image parts or image assets
|
|
16
|
+
anywhere in it, and a question names each by its path in backticks, such as `photo` or
|
|
17
|
+
`frames[2].frame`. Every question type may ask about one, and a new `box` question, `BoxQuestion`,
|
|
18
|
+
asks where something is: its `BoxAnswer` is a `DecisionBox` of four edges, each a fraction of the
|
|
19
|
+
picture's height or width, with a confidence. `boxOverlap()` measures how far two boxes overlap,
|
|
20
|
+
`answeredRight()` counts a box right at half or more, `calibrate()` chooses a box's threshold as any
|
|
21
|
+
answer's, and `decisionEvaluator()` scores a box against `where` the thing is. `statePictures()`
|
|
22
|
+
lists a state's pictures and `readPicture()` reads one with the media family's readers.
|
|
23
|
+
- TypeSafe AI's Jev is sent a state's pictures as the API's assets, each under a new reference, with
|
|
24
|
+
the `TypeSafe-Danger-Zone-Images` header that pictures and boxes require, and answers boxes on Jev 1.14
|
|
25
|
+
and later. A picture it would refuse, outside PNG or JPEG, animated, over 16 MiB, or outside 280 to
|
|
26
|
+
1,568 pixels a side, or a request over 32 MiB, is refused first, naming the picture's path.
|
|
27
|
+
`TypeSafeDecisionProviderOptions.media` reads pictures held by file, URL, or stored asset.
|
|
28
|
+
- `completionDecisions()` shows a vision model the pictures a state holds, each labelled by its path,
|
|
29
|
+
and reads a box as the median of several samples, its confidence how far they agree; `images: false`
|
|
30
|
+
marks a text-only model.
|
|
31
|
+
- `DecisionProviderCapabilities.images`: a provider that does not read pictures is refused a decision
|
|
32
|
+
about one, and passed over as a fallback. The mock reads pictures and answers boxes.
|
|
33
|
+
- `DecisionConformanceOptions.picture` runs the conformance checks of pictures and boxes, and
|
|
34
|
+
`DecisionConformanceReport.skipped` lists the checks that could not run, rather than passing them.
|
|
35
|
+
- **Compatibility records from community CI.** An adapter's own CI publishes its sealed record at the
|
|
36
|
+
URL its package names as `nexus.compatibility`, beside the keyword `nexus-ai-pro-adapter`.
|
|
37
|
+
`nexus adapters gather`, and `gatherRecords()` in `nexus-ai-pro/adapter-kit`, find those packages on
|
|
38
|
+
npm, fetch each record, and keep only those whose digest matches and that name their own package,
|
|
39
|
+
from HTTPS; a source that is down keeps its last record, and a package that no longer names one
|
|
40
|
+
leaves the page. Nothing an adapter publishes is run. A daily `Community compatibility` workflow
|
|
41
|
+
writes the page, with the adapters this repository ships, to the `compatibility` branch, where a
|
|
42
|
+
record that says its adapter fails turns it red. `compatibility/sources.json` lists adapters not
|
|
43
|
+
on npm.
|
|
44
|
+
|
|
45
|
+
- **A preview of 3.0's defaults.** `useDefaults('3.0')`, from `nexus-ai-pro/config`, or
|
|
46
|
+
`NEXUS_DEFAULTS=3.0`, turns on the three settings 2.x keeps opt-in, as 3.0 will: incremental tracing on
|
|
47
|
+
a durable trace store, the Redis operation store's dispatch index where the client can keep it, and
|
|
48
|
+
`hash-v2` for a deployment first recorded. An option set explicitly wins, and without the switch
|
|
49
|
+
nothing changes. `DEFAULT_CHANGES` lists each, with what to do first; `nexusDefaults()` says which
|
|
50
|
+
defaults are in effect; and `nexus doctor --upgrade 3.0`, `DoctorOptions.upgrade`, says what 3.0
|
|
51
|
+
would change for a deployment. `TraceStore.durable` marks a store whose runs live only in the
|
|
52
|
+
process, as `MemoryTraceStore`'s.
|
|
53
|
+
|
|
54
|
+
- OpenAI's models on the Responses API, such as `gpt-5.5-pro`, report token log probabilities when a
|
|
55
|
+
request asks for them, as its chat models do: the request includes `message.output_text.logprobs`
|
|
56
|
+
with up to 20 alternatives a token, and each text part's are read back. A typed decision asked of
|
|
57
|
+
one is read from them in one call, rather than sampled; a model that refuses them is still sampled.
|
|
58
|
+
|
|
59
|
+
- With `tenants`, an A2A handler serves each tenant a card of its own at
|
|
60
|
+
`/{tenant}/.well-known/agent-card.json`, every interface naming the tenant, so a client that reads it,
|
|
61
|
+
such as the A2A project's reference client, calls as that tenant over each binding. The nightly
|
|
62
|
+
interoperability run drives a tenant through `@a2a-js/sdk` over JSON-RPC, HTTP+JSON, and gRPC, and
|
|
63
|
+
checks that a tenant it may not act for, and a call naming none, are refused.
|
|
64
|
+
|
|
65
|
+
### Fixed
|
|
66
|
+
|
|
67
|
+
- The compatibility page writes a record as text: an adapter links to its repository only at an
|
|
68
|
+
`http` or `https` address, so a record naming `javascript:` makes no link on the HTML page; a
|
|
69
|
+
record's line breaks, pipes, brackets, and stars no longer break or add to the Markdown; and the
|
|
70
|
+
HTML page colours each row by its state.
|
|
71
|
+
|
|
72
|
+
### Changed
|
|
73
|
+
|
|
74
|
+
- TypeSafe AI's 400 and 413 refuse a decision as `DECISION_INVALID`, as a 422 does, rather than
|
|
75
|
+
failing it as `DECISION_FAILED`: the request, such as a picture, is what was refused. Neither is
|
|
76
|
+
retried.
|
|
77
|
+
- A decision's trace and cache key hold a digest of each picture in its place, never its bytes, and the
|
|
78
|
+
budget holds a picture at 1,600 tokens rather than reading its base64 as text.
|
|
79
|
+
- A doctor report has one more check, `upgrade`, skipped unless `upgrade` asks about a release.
|
|
80
|
+
|
|
7
81
|
## [2.20.2] - 2026-10-24
|
|
8
82
|
|
|
9
83
|
The first published release of 2.20. The package is 2.20.0's: the publishes of 2.20.0 and 2.20.1
|
|
@@ -3287,7 +3361,8 @@ shared policy vocabulary is in place for a future release that revisits this.
|
|
|
3287
3361
|
- URL fetching rejects unsafe private-network targets and limits response reads.
|
|
3288
3362
|
- CLI and audit findings no longer disclose detected secret values.
|
|
3289
3363
|
|
|
3290
|
-
[Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.
|
|
3364
|
+
[Unreleased]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.21.0...HEAD
|
|
3365
|
+
[2.21.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.20.2...v2.21.0
|
|
3291
3366
|
[2.20.2]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.20.1...v2.20.2
|
|
3292
3367
|
[2.20.1]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.20.0...v2.20.1
|
|
3293
3368
|
[2.20.0]: https://github.com/mkhitar-abrahamyan/nexus-ai/compare/v2.19.0...v2.20.0
|
package/README.md
CHANGED
|
@@ -338,15 +338,16 @@ Shipped so far:
|
|
|
338
338
|
a person when it is not;
|
|
339
339
|
- 2.20 tools read from a service's own OpenAPI document, which an agent calls or an emulator plays,
|
|
340
340
|
an emulator that holds what a whole evaluation did so examples that build on each other are
|
|
341
|
-
evaluated with nothing sent, and one A2A handler serving many tenants, each seeing only its own
|
|
341
|
+
evaluated with nothing sent, and one A2A handler serving many tenants, each seeing only its own;
|
|
342
|
+
- 2.21 typed decisions about pictures, a yes or no, a choice, a score, or a box around something in
|
|
343
|
+
one, from TypeSafe AI's Jev or any vision model; community adapters' compatibility records gathered
|
|
344
|
+
from their own CI, a failing one turning red; and 3.0's defaults behind one switch today.
|
|
342
345
|
|
|
343
346
|
Upgrading from 1.x: `npx nexus migrate src --write` moves your imports, and
|
|
344
347
|
[MIGRATING.md](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/MIGRATING.md) covers the rest.
|
|
345
|
-
Next, 2.
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
embedding batch tier, media understanding, video, Gemini Live, SIP, and both phone providers
|
|
349
|
-
graduate once the live workflow passes against each. The
|
|
348
|
+
Next, in 2.22, Pinecone, images, the Gemini embedding batch tier, media understanding, video, typed
|
|
349
|
+
decisions, Gemini Live, SIP, and both phone providers graduate once the live workflow passes against
|
|
350
|
+
each. The
|
|
350
351
|
[roadmap](https://github.com/mkhitar-abrahamyan/nexus-ai/blob/main/ROADMAP.md) lists what comes after, in the order
|
|
351
352
|
applications ask for it.
|
|
352
353
|
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compatibility records gathered from where adapters' own CI publishes them: packages found on npm by
|
|
3
|
+
* a keyword, each naming its record's URL in its `package.json`, and sources listed by hand. Each
|
|
4
|
+
* record is read as `nexus adapters verify` sealed it, and must name the package that points to it.
|
|
5
|
+
* Nothing an adapter publishes is run: a gathered record is listed as its CI recorded it.
|
|
6
|
+
*/
|
|
7
|
+
import { type CompatibilityRecord } from './record.js';
|
|
8
|
+
/** The npm keyword an adapter package carries to be found: `nexus-ai-pro-adapter`. */
|
|
9
|
+
export declare const ADAPTER_KEYWORD = "nexus-ai-pro-adapter";
|
|
10
|
+
/** Where one adapter's CI publishes its record. */
|
|
11
|
+
export interface RecordSource {
|
|
12
|
+
/** The adapter's package name, which its record must name. */
|
|
13
|
+
package: string;
|
|
14
|
+
/** The record's URL: HTTPS, or HTTP to this machine. */
|
|
15
|
+
url: string;
|
|
16
|
+
}
|
|
17
|
+
/** Options for `gatherRecords()`. */
|
|
18
|
+
export interface GatherRecordsOptions {
|
|
19
|
+
/**
|
|
20
|
+
* The keyword adapter packages are found by on npm, each naming its record's URL as
|
|
21
|
+
* `nexus.compatibility` in its `package.json`. Defaults to `ADAPTER_KEYWORD`; `false` finds none.
|
|
22
|
+
*/
|
|
23
|
+
keyword?: string | false;
|
|
24
|
+
/** Sources listed by hand, beside those found; a listed package's URL is used over its own. */
|
|
25
|
+
sources?: readonly RecordSource[];
|
|
26
|
+
/** The npm registry. Defaults to `https://registry.npmjs.org`. */
|
|
27
|
+
registry?: string;
|
|
28
|
+
/**
|
|
29
|
+
* The records gathered last time: a package whose record cannot be read now, because its source is
|
|
30
|
+
* down or what it serves is refused, keeps its last one, and says why.
|
|
31
|
+
*/
|
|
32
|
+
previous?: readonly CompatibilityRecord[];
|
|
33
|
+
/** The most bytes a record may have. Defaults to 1 MiB. */
|
|
34
|
+
maxBytes?: number;
|
|
35
|
+
/** How long each request may take, in milliseconds. Defaults to 15 seconds. */
|
|
36
|
+
timeoutMs?: number;
|
|
37
|
+
/** The most packages found by keyword. Defaults to 1,000. */
|
|
38
|
+
maxPackages?: number;
|
|
39
|
+
/** Replaces the global `fetch`. */
|
|
40
|
+
fetch?: typeof fetch;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* What became of one package's record: `gathered` now; `kept` from last time, as now it could not be;
|
|
44
|
+
* `refused` or `unreachable` with nothing to keep; or `undeclared`, a package that names no record.
|
|
45
|
+
*/
|
|
46
|
+
export type GatherOutcome = 'gathered' | 'kept' | 'refused' | 'unreachable' | 'undeclared';
|
|
47
|
+
/** One package's source, and what became of its record. */
|
|
48
|
+
export interface GatheredSource {
|
|
49
|
+
/** The package's name. */
|
|
50
|
+
package: string;
|
|
51
|
+
/** Where its record is published, when it says. */
|
|
52
|
+
url?: string;
|
|
53
|
+
/** What became of it. */
|
|
54
|
+
outcome: GatherOutcome;
|
|
55
|
+
/** Why, for every outcome but `gathered`. */
|
|
56
|
+
detail?: string;
|
|
57
|
+
/** The record listed for it: gathered now, or kept. */
|
|
58
|
+
record?: CompatibilityRecord;
|
|
59
|
+
}
|
|
60
|
+
/** What `gatherRecords()` found. */
|
|
61
|
+
export interface GatherReport {
|
|
62
|
+
/** Every record to list, gathered now or kept, by package name. */
|
|
63
|
+
records: CompatibilityRecord[];
|
|
64
|
+
/** Every package, by name, and what became of its record. */
|
|
65
|
+
sources: GatheredSource[];
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Gathers the compatibility records adapters' own CI publishes: finds adapter packages on npm by
|
|
69
|
+
* `keyword`, reads where each publishes its record, and fetches each, with `sources` listed by hand.
|
|
70
|
+
* A record is gathered only when it reads as sealed, with a digest that matches it, and names the
|
|
71
|
+
* package that points to it; a package whose record cannot be read now keeps its `previous` one.
|
|
72
|
+
* Throws when npm cannot be searched, rather than reporting every adapter gone.
|
|
73
|
+
*/
|
|
74
|
+
export declare function gatherRecords(options?: GatherRecordsOptions): Promise<GatherReport>;
|
|
75
|
+
/** The name of a package from how a record names it, such as `@acme/nexus-milvus` from `@acme/nexus-milvus@0.3.0`. */
|
|
76
|
+
export declare function packageName(specifier: string): string;
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { readCompatibilityRecord } from './record.js';
|
|
2
|
+
export const ADAPTER_KEYWORD = 'nexus-ai-pro-adapter';
|
|
3
|
+
export async function gatherRecords(options = {}) {
|
|
4
|
+
const request = options.fetch ?? fetch;
|
|
5
|
+
const registry = (options.registry ?? 'https://registry.npmjs.org').replace(/\/+$/, '');
|
|
6
|
+
const timeoutMs = options.timeoutMs ?? 15_000;
|
|
7
|
+
const maxBytes = options.maxBytes ?? 1024 * 1024;
|
|
8
|
+
const get = (url) => request(url, { signal: AbortSignal.timeout(timeoutMs), redirect: 'follow' });
|
|
9
|
+
const found = new Map();
|
|
10
|
+
const keyword = options.keyword === undefined ? ADAPTER_KEYWORD : options.keyword;
|
|
11
|
+
if (keyword) {
|
|
12
|
+
for (const name of await search(get, registry, keyword, options.maxPackages ?? 1_000))
|
|
13
|
+
found.set(name, undefined);
|
|
14
|
+
}
|
|
15
|
+
for (const source of options.sources ?? [])
|
|
16
|
+
found.set(source.package, source.url);
|
|
17
|
+
const previous = new Map();
|
|
18
|
+
for (const record of options.previous ?? []) {
|
|
19
|
+
const name = record.adapter.package ? packageName(record.adapter.package) : undefined;
|
|
20
|
+
if (name)
|
|
21
|
+
previous.set(name, record);
|
|
22
|
+
}
|
|
23
|
+
const sources = [];
|
|
24
|
+
for (const name of [...found.keys()].sort()) {
|
|
25
|
+
const listed = found.get(name);
|
|
26
|
+
const result = await gatherOne(name, listed, { get, registry, maxBytes });
|
|
27
|
+
const last = previous.get(name);
|
|
28
|
+
if ((result.outcome === 'refused' || result.outcome === 'unreachable') && last) {
|
|
29
|
+
sources.push({ ...result, outcome: 'kept', record: last, detail: `${result.outcome}: ${result.detail}` });
|
|
30
|
+
}
|
|
31
|
+
else
|
|
32
|
+
sources.push(result);
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
records: sources.flatMap((source) => (source.record ? [source.record] : [])),
|
|
36
|
+
sources,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export function packageName(specifier) {
|
|
40
|
+
const at = specifier.lastIndexOf('@');
|
|
41
|
+
return at > 0 ? specifier.slice(0, at) : specifier;
|
|
42
|
+
}
|
|
43
|
+
async function search(get, registry, keyword, limit) {
|
|
44
|
+
const names = [];
|
|
45
|
+
for (let from = 0; names.length < limit;) {
|
|
46
|
+
const url = `${registry}/-/v1/search?text=keywords:${encodeURIComponent(keyword)}&size=250&from=${from}`;
|
|
47
|
+
let page;
|
|
48
|
+
try {
|
|
49
|
+
const response = await get(url);
|
|
50
|
+
if (!response.ok)
|
|
51
|
+
throw new Error(`it answered ${response.status}`);
|
|
52
|
+
page = (await response.json());
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
throw new Error(`npm could not be searched for "${keyword}": ${message(error)}`);
|
|
56
|
+
}
|
|
57
|
+
const objects = page.objects ?? [];
|
|
58
|
+
for (const item of objects)
|
|
59
|
+
if (typeof item.package?.name === 'string')
|
|
60
|
+
names.push(item.package.name);
|
|
61
|
+
from += objects.length;
|
|
62
|
+
if (objects.length === 0 || from >= (page.total ?? 0))
|
|
63
|
+
break;
|
|
64
|
+
}
|
|
65
|
+
return [...new Set(names)].slice(0, limit);
|
|
66
|
+
}
|
|
67
|
+
async function gatherOne(name, listed, context) {
|
|
68
|
+
let url = listed;
|
|
69
|
+
if (url === undefined) {
|
|
70
|
+
try {
|
|
71
|
+
const response = await context.get(`${context.registry}/${name.replace('/', '%2f')}/latest`);
|
|
72
|
+
if (!response.ok)
|
|
73
|
+
return { package: name, outcome: 'unreachable', detail: `npm answered ${response.status}` };
|
|
74
|
+
const manifest = (await response.json());
|
|
75
|
+
const declared = manifest.nexus?.compatibility;
|
|
76
|
+
if (typeof declared !== 'string' || !declared.trim()) {
|
|
77
|
+
return { package: name, outcome: 'undeclared', detail: 'its package.json names no nexus.compatibility record' };
|
|
78
|
+
}
|
|
79
|
+
url = declared.trim();
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
return { package: name, outcome: 'unreachable', detail: `npm could not be read: ${message(error)}` };
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (!allowed(url))
|
|
86
|
+
return { package: name, url, outcome: 'refused', detail: `${url} is not HTTPS` };
|
|
87
|
+
let text;
|
|
88
|
+
try {
|
|
89
|
+
const response = await context.get(url);
|
|
90
|
+
if (response.url && !allowed(response.url)) {
|
|
91
|
+
return {
|
|
92
|
+
package: name,
|
|
93
|
+
url,
|
|
94
|
+
outcome: 'refused',
|
|
95
|
+
detail: `${url} redirects to ${response.url}, which is not HTTPS`,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
if (!response.ok)
|
|
99
|
+
return { package: name, url, outcome: 'unreachable', detail: `${url} answered ${response.status}` };
|
|
100
|
+
const declared = Number(response.headers.get('content-length') ?? 0);
|
|
101
|
+
if (declared > context.maxBytes)
|
|
102
|
+
return tooLarge(name, url, declared, context.maxBytes);
|
|
103
|
+
text = await response.text();
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
return { package: name, url, outcome: 'unreachable', detail: `${url} could not be read: ${message(error)}` };
|
|
107
|
+
}
|
|
108
|
+
const size = new TextEncoder().encode(text).length;
|
|
109
|
+
if (size > context.maxBytes)
|
|
110
|
+
return tooLarge(name, url, size, context.maxBytes);
|
|
111
|
+
let record;
|
|
112
|
+
try {
|
|
113
|
+
record = readCompatibilityRecord(text);
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
return { package: name, url, outcome: 'refused', detail: message(error) };
|
|
117
|
+
}
|
|
118
|
+
const named = record.adapter.package;
|
|
119
|
+
if (!named)
|
|
120
|
+
return { package: name, url, outcome: 'refused', detail: 'the record names no package' };
|
|
121
|
+
if (packageName(named) !== name) {
|
|
122
|
+
return { package: name, url, outcome: 'refused', detail: `the record names ${named}, not ${name}` };
|
|
123
|
+
}
|
|
124
|
+
return { package: name, url, outcome: 'gathered', record };
|
|
125
|
+
}
|
|
126
|
+
function allowed(url) {
|
|
127
|
+
try {
|
|
128
|
+
const parsed = new URL(url);
|
|
129
|
+
if (parsed.protocol === 'https:')
|
|
130
|
+
return true;
|
|
131
|
+
return parsed.protocol === 'http:' && ['localhost', '127.0.0.1', '[::1]'].includes(parsed.hostname);
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function tooLarge(name, url, size, limit) {
|
|
138
|
+
return {
|
|
139
|
+
package: name,
|
|
140
|
+
url,
|
|
141
|
+
outcome: 'refused',
|
|
142
|
+
detail: `the record is ${size} bytes, over the ${limit}-byte limit`,
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
function message(error) {
|
|
146
|
+
return error instanceof Error ? error.message : String(error);
|
|
147
|
+
}
|
|
@@ -20,6 +20,7 @@ import { type AdapterErrorClass } from './errors.js';
|
|
|
20
20
|
import { type CompatibilityRecord, type CompatibilityRecordOptions } from './record.js';
|
|
21
21
|
export { ADAPTER_CONTRACTS, ADAPTER_FIXTURE_PASSAGES, type AdapterCheck, type AdapterContract, type AdapterContractVersion, type RetrieverCapabilities, type RetrieverContractOptions, runRetrieverContract, runStoreContract, runVectorStoreContract, type StoreCapabilities, type VectorStoreCapabilities, type VectorStoreContractOptions, } from './contracts.js';
|
|
22
22
|
export { AdapterError, type AdapterErrorClass, type AdapterErrorCode, classifyAdapterError } from './errors.js';
|
|
23
|
+
export { ADAPTER_KEYWORD, type GatheredSource, type GatherOutcome, type GatherRecordsOptions, type GatherReport, gatherRecords, type RecordSource, } from './gather.js';
|
|
23
24
|
export { type CheckChange, COMPATIBILITY_RECORD_FORMAT, type CompatibilityComparison, type CompatibilityPageOptions, type CompatibilityRecord, type CompatibilityRecordAdapter, CompatibilityRecordError, type CompatibilityRecordOptions, type CompatibilityState, type CompatibilityStatus, compareRecords, compatibilityPage, compatibilityRecord, readCompatibilityRecord, recordDigest, recordStatus, } from './record.js';
|
|
24
25
|
export { parseVersion, satisfiesRange } from './semver.js';
|
|
25
26
|
/** The kinds of adapter the kit builds. */
|
|
@@ -7,6 +7,7 @@ import { compatibilityRecord } from './record.js';
|
|
|
7
7
|
import { satisfiesRange } from './semver.js';
|
|
8
8
|
export { ADAPTER_CONTRACTS, ADAPTER_FIXTURE_PASSAGES, runRetrieverContract, runStoreContract, runVectorStoreContract, } from './contracts.js';
|
|
9
9
|
export { AdapterError, classifyAdapterError } from './errors.js';
|
|
10
|
+
export { ADAPTER_KEYWORD, gatherRecords, } from './gather.js';
|
|
10
11
|
export { COMPATIBILITY_RECORD_FORMAT, CompatibilityRecordError, compareRecords, compatibilityPage, compatibilityRecord, readCompatibilityRecord, recordDigest, recordStatus, } from './record.js';
|
|
11
12
|
export { parseVersion, satisfiesRange } from './semver.js';
|
|
12
13
|
const OPERATIONS = {
|
|
@@ -145,6 +145,8 @@ export interface CompatibilityPageOptions {
|
|
|
145
145
|
/**
|
|
146
146
|
* One page listing every record: each adapter, its kind and version, the release and contract it was
|
|
147
147
|
* verified against, its state there, its checks, and its p95 per operation, by kind and name. The
|
|
148
|
-
* same records make the same page, so it can be checked for drift.
|
|
148
|
+
* same records make the same page, so it can be checked for drift. A record is written as text, never
|
|
149
|
+
* as markup: an adapter links to its repository only at an `http` or `https` address, and the HTML
|
|
150
|
+
* page colours each row by its state, a failing one red.
|
|
149
151
|
*/
|
|
150
152
|
export declare function compatibilityPage(records: readonly CompatibilityRecord[], options?: CompatibilityPageOptions): string;
|
|
@@ -161,7 +161,7 @@ export function compatibilityPage(records, options = {}) {
|
|
|
161
161
|
record,
|
|
162
162
|
status,
|
|
163
163
|
cells: [
|
|
164
|
-
record.adapter.repository
|
|
164
|
+
record.adapter.repository && /^https?:\/\//i.test(record.adapter.repository)
|
|
165
165
|
? { text: record.adapter.name, href: record.adapter.repository }
|
|
166
166
|
: { text: record.adapter.name },
|
|
167
167
|
{ text: record.adapter.kind },
|
|
@@ -187,7 +187,8 @@ export function compatibilityPage(records, options = {}) {
|
|
|
187
187
|
const cell = (item) => item.href ? `<a href="${escapeHtml(item.href)}">${escapeHtml(item.text)}</a>` : escapeHtml(item.text);
|
|
188
188
|
return [
|
|
189
189
|
'<!doctype html>',
|
|
190
|
-
`<html lang="en"><head><meta charset="utf-8"><title>${escapeHtml(title)}</title
|
|
190
|
+
`<html lang="en"><head><meta charset="utf-8"><title>${escapeHtml(title)}</title>`,
|
|
191
|
+
`<style>${STATE_STYLE}</style></head><body>`,
|
|
191
192
|
`<h1>${escapeHtml(title)}</h1>`,
|
|
192
193
|
...(options.intro ? [`<p>${escapeHtml(options.intro)}</p>`] : []),
|
|
193
194
|
`<p>States are judged against nexus-ai-pro ${escapeHtml(nexusVersion)}.</p>`,
|
|
@@ -207,9 +208,11 @@ export function compatibilityPage(records, options = {}) {
|
|
|
207
208
|
'',
|
|
208
209
|
].join('\n');
|
|
209
210
|
}
|
|
211
|
+
const literal = (text) => oneLine(text).replace(/[|[\]*_`\\]/g, (char) => `\\${char}`);
|
|
210
212
|
const cell = (item) => {
|
|
211
|
-
const text = item.text
|
|
212
|
-
|
|
213
|
+
const text = literal(item.text);
|
|
214
|
+
const href = item.href?.replace(/[|\s()]/g, (char) => `%${char.charCodeAt(0).toString(16).toUpperCase().padStart(2, '0')}`);
|
|
215
|
+
return href ? `[${text}](${href})` : text;
|
|
213
216
|
};
|
|
214
217
|
return [
|
|
215
218
|
`# ${title}`,
|
|
@@ -221,7 +224,10 @@ export function compatibilityPage(records, options = {}) {
|
|
|
221
224
|
`| ${headers.map(() => '---').join(' | ')} |`,
|
|
222
225
|
...rows.map((row) => `| ${row.cells.map(cell).join(' | ')} |`),
|
|
223
226
|
...(notes.length > 0
|
|
224
|
-
? [
|
|
227
|
+
? [
|
|
228
|
+
'',
|
|
229
|
+
...notes.map((row) => `- **${literal(row.record.adapter.name)}**: ${row.status.reasons.map(literal).join('; ')}`),
|
|
230
|
+
]
|
|
225
231
|
: []),
|
|
226
232
|
'',
|
|
227
233
|
].join('\n');
|
|
@@ -240,6 +246,15 @@ function runtimeName() {
|
|
|
240
246
|
function isRecord(value) {
|
|
241
247
|
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
242
248
|
}
|
|
249
|
+
const STATE_STYLE = [
|
|
250
|
+
'tr[data-state="passing"] td{color:#1a7f37}',
|
|
251
|
+
'tr[data-state="failing"] td{color:#cf222e;font-weight:600}',
|
|
252
|
+
'tr[data-state="incompatible"] td{color:#9a6700}',
|
|
253
|
+
'tr[data-state="stale"] td{color:#57606a}',
|
|
254
|
+
].join('');
|
|
255
|
+
function oneLine(text) {
|
|
256
|
+
return text.replace(/\s+/g, ' ').trim();
|
|
257
|
+
}
|
|
243
258
|
function escapeHtml(text) {
|
|
244
259
|
return text.replace(/[&<>"']/g, (char) => `&#${char.charCodeAt(0)};`);
|
|
245
260
|
}
|
package/dist/cli/adapters.d.ts
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { type ParsedArgs } from './args.js';
|
|
2
2
|
/**
|
|
3
|
-
* `nexus adapters verify <module>[#export]`, `nexus adapters page <records...>`,
|
|
4
|
-
* `nexus adapters recheck <records
|
|
3
|
+
* `nexus adapters verify <module>[#export]`, `nexus adapters page <records...>`,
|
|
4
|
+
* `nexus adapters recheck <records...>`, and `nexus adapters gather --out <dir>`.
|
|
5
5
|
*
|
|
6
6
|
* `verify` runs an adapter's contract suite and benchmark from its own repository and writes its
|
|
7
7
|
* compatibility record; `page` lists records on one page; `recheck` verifies each listed adapter again
|
|
8
|
-
* against the installed release and fails when one that passed no longer does
|
|
8
|
+
* against the installed release and fails when one that passed no longer does; `gather` fetches the
|
|
9
|
+
* records adapters' own CI publishes, checked and never run, into a directory a page lists.
|
|
9
10
|
*/
|
|
10
11
|
export declare function runAdaptersCommand(subcommand: string | undefined, args: ParsedArgs): Promise<void>;
|
package/dist/cli/adapters.js
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
|
-
import { mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { mkdir, readdir, readFile, rm, stat, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { createRequire } from 'node:module';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { pathToFileURL } from 'node:url';
|
|
6
|
+
import { ADAPTER_KEYWORD, gatherRecords } from '../adapter-kit/gather.js';
|
|
6
7
|
import { compareRecords, compatibilityPage, readCompatibilityRecord, recordStatus, } from '../adapter-kit/record.js';
|
|
7
8
|
import { CliUsageError, flagBool, isRecord, printTable, stringFlag, writeJson } from './args.js';
|
|
8
9
|
export async function runAdaptersCommand(subcommand, args) {
|
|
@@ -12,7 +13,75 @@ export async function runAdaptersCommand(subcommand, args) {
|
|
|
12
13
|
return page(args);
|
|
13
14
|
if (subcommand === 'recheck')
|
|
14
15
|
return recheck(args);
|
|
15
|
-
|
|
16
|
+
if (subcommand === 'gather')
|
|
17
|
+
return gather(args);
|
|
18
|
+
throw new CliUsageError(`Unknown adapters command "${subcommand ?? ''}". Use verify, page, recheck, or gather.`);
|
|
19
|
+
}
|
|
20
|
+
async function gather(args) {
|
|
21
|
+
const out = stringFlag(args.flags, 'out');
|
|
22
|
+
if (!out)
|
|
23
|
+
throw new CliUsageError('nexus adapters gather needs --out <dir>: where the gathered records are kept.');
|
|
24
|
+
const sourcesFile = stringFlag(args.flags, 'sources');
|
|
25
|
+
const sources = sourcesFile ? sourcesFrom(await readFile(sourcesFile, 'utf8'), sourcesFile) : [];
|
|
26
|
+
const previous = [];
|
|
27
|
+
for (const name of existsSync(out) ? (await readdir(out)).sort() : []) {
|
|
28
|
+
if (!name.endsWith('.json'))
|
|
29
|
+
continue;
|
|
30
|
+
try {
|
|
31
|
+
previous.push({ file: name, record: readCompatibilityRecord(await readFile(path.join(out, name), 'utf8')) });
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const registry = stringFlag(args.flags, 'registry');
|
|
37
|
+
const report = await gatherRecords({
|
|
38
|
+
keyword: args.flags.keyword === false ? false : (stringFlag(args.flags, 'keyword') ?? ADAPTER_KEYWORD),
|
|
39
|
+
sources,
|
|
40
|
+
previous: previous.map((entry) => entry.record),
|
|
41
|
+
...(registry ? { registry } : {}),
|
|
42
|
+
});
|
|
43
|
+
await mkdir(out, { recursive: true });
|
|
44
|
+
const written = new Set();
|
|
45
|
+
for (const source of report.sources) {
|
|
46
|
+
if (!source.record)
|
|
47
|
+
continue;
|
|
48
|
+
const file = `${slug(source.package)}.json`;
|
|
49
|
+
written.add(file);
|
|
50
|
+
await writeFile(path.join(out, file), `${JSON.stringify(source.record, null, 2)}
|
|
51
|
+
`);
|
|
52
|
+
}
|
|
53
|
+
for (const { file } of previous)
|
|
54
|
+
if (!written.has(file))
|
|
55
|
+
await rm(path.join(out, file));
|
|
56
|
+
if (flagBool(args.flags, 'json')) {
|
|
57
|
+
writeJson(report);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
printTable(report.sources.map((source) => ({
|
|
61
|
+
package: source.package,
|
|
62
|
+
outcome: source.outcome,
|
|
63
|
+
state: source.record ? recordStatus(source.record).state : '',
|
|
64
|
+
detail: source.detail ?? '',
|
|
65
|
+
})), ['package', 'outcome', 'state', 'detail']);
|
|
66
|
+
console.log('');
|
|
67
|
+
console.log(`${report.records.length} records listed in ${out}, of ${report.sources.length} packages.`);
|
|
68
|
+
}
|
|
69
|
+
function sourcesFrom(text, file) {
|
|
70
|
+
let parsed;
|
|
71
|
+
try {
|
|
72
|
+
parsed = JSON.parse(text);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
throw new CliUsageError(`${file} is not JSON: ${error.message}`);
|
|
76
|
+
}
|
|
77
|
+
if (!Array.isArray(parsed))
|
|
78
|
+
throw new CliUsageError(`${file} must list sources: [{ "package": ..., "url": ... }]`);
|
|
79
|
+
return parsed.map((item, index) => {
|
|
80
|
+
if (!isRecord(item) || typeof item.package !== 'string' || typeof item.url !== 'string') {
|
|
81
|
+
throw new CliUsageError(`${file}, source ${index + 1}: each source names its "package" and its record's "url"`);
|
|
82
|
+
}
|
|
83
|
+
return { package: item.package, url: item.url };
|
|
84
|
+
});
|
|
16
85
|
}
|
|
17
86
|
async function verify(args) {
|
|
18
87
|
const [target] = args.positionals;
|
package/dist/cli/doctor.js
CHANGED
|
@@ -28,6 +28,9 @@ export async function runDoctorCommand({ flags }) {
|
|
|
28
28
|
const maxQueueAgeMs = numberFlag(flags, 'max-queue-age-ms');
|
|
29
29
|
if (maxQueueAgeMs !== undefined)
|
|
30
30
|
options.maxQueueAgeMs = maxQueueAgeMs;
|
|
31
|
+
const upgrade = stringFlag(flags, 'upgrade');
|
|
32
|
+
if (upgrade !== undefined)
|
|
33
|
+
options.upgrade = upgrade;
|
|
31
34
|
let report;
|
|
32
35
|
try {
|
|
33
36
|
report = await diagnose(options);
|
package/dist/cli.js
CHANGED
|
@@ -437,7 +437,7 @@ Usage:
|
|
|
437
437
|
nexus db status --client <db.mjs> [--adapters ...] [--dialect postgres|sqlite] [--table t] [--check] [--json]
|
|
438
438
|
nexus db migrate --client <db.mjs> [--dry-run] [--adapters ...] [--dialect postgres|sqlite] [--table t] [--json]
|
|
439
439
|
nexus migrate [paths...] [--write] [--check] [--json]
|
|
440
|
-
nexus doctor [--module doctor.mjs] [--replicas n] [--max-queue-age-ms n] [--strict] [--json]
|
|
440
|
+
nexus doctor [--module doctor.mjs] [--replicas n] [--max-queue-age-ms n] [--upgrade 3.0] [--strict] [--json]
|
|
441
441
|
nexus graph lint <module>[#export] [--deployed] [--strict] [--ignore CODE,CODE] [--json]
|
|
442
442
|
nexus deploy status [assistant] --url <server> [--token t] [--json]
|
|
443
443
|
nexus deploy canary <assistant> <revision> <percent> --url <server> [--reason r] [--version n]
|
|
@@ -446,6 +446,7 @@ Usage:
|
|
|
446
446
|
nexus adapters verify <module>[#export] [--out record.json] [--package name@version] [--repository url] [--verify spec] [--json]
|
|
447
447
|
nexus adapters page <records|dir...> [--out page.md] [--html] [--title t] [--intro text] [--expect page.md] [--check] [--strict]
|
|
448
448
|
nexus adapters recheck <records|dir...> [--out dir] [--update] [--json]
|
|
449
|
+
nexus adapters gather --out <dir> [--keyword nexus-ai-pro-adapter | --no-keyword] [--sources sources.json] [--registry url] [--json]
|
|
449
450
|
nexus policy check <policy.json> [--base <policy.json>|git:<ref>] [--accept path,path] [--json]
|
|
450
451
|
nexus optimize [request.json|prompt.txt] [--model gpt-5-mini] [--max-input-tokens 4000] [--densify] [--json]
|
|
451
452
|
|
|
@@ -461,6 +462,7 @@ Database modules for "db status" and "db migrate" export { client }, and optiona
|
|
|
461
462
|
"graph lint" reads a compiled graph's shape and exits 1 on an error finding, or on a warning with --strict.
|
|
462
463
|
"adapters verify" exits 1 when the adapter does not pass; its module exports { adapter, config, options, record, close }.
|
|
463
464
|
"adapters recheck" verifies each listed adapter again and exits 1 when one that passed no longer does; "adapters page --check" exits 1 on a failing or incompatible record, --strict on a stale one too.
|
|
465
|
+
"adapters gather" fetches the records adapters' own CI publishes, found on npm by keyword or listed in --sources, keeps only those sealed and naming their package, and never runs them.
|
|
464
466
|
"policy check" exits 1 when the policy is not valid, or, against --base, when a change that widens what a tenant may reach is not acknowledged.`);
|
|
465
467
|
}
|
|
466
468
|
main().catch((error) => {
|
|
@@ -95,3 +95,4 @@ export declare function createNexusConfig(seed?: Partial<NexusAIConfig>): NexusC
|
|
|
95
95
|
* Identity helper for users who prefer object literals but want type inference and validation in editors.
|
|
96
96
|
*/
|
|
97
97
|
export declare function defineNexusConfig(config: NexusAIConfig): NexusAIConfig;
|
|
98
|
+
export { DEFAULT_CHANGES, type DefaultChange, type NexusDefaults, nexusDefaults, useDefaults } from './defaults.js';
|