@atrib/daemon 0.1.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -30
- package/dist/backend.js +67 -50
- package/dist/http-host.js +8 -1
- package/dist/index.js +6 -5
- package/package.json +18 -9
package/README.md
CHANGED
|
@@ -1,34 +1,43 @@
|
|
|
1
1
|
# atribd
|
|
2
2
|
|
|
3
|
-
Local daemon for atrib. Serves the
|
|
4
|
-
stateless-native process over Streamable HTTP
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
3
|
+
Local daemon for atrib. Serves the two-verb cognitive surface plus the
|
|
4
|
+
legacy alias tools from one stateless-native process over Streamable HTTP
|
|
5
|
+
or stdio.
|
|
6
|
+
|
|
7
|
+
Per the attest/recall rename
|
|
8
|
+
([D164](../../DECISIONS.md#d164-attestrecall-verb-rename-and-primitive-surface-collapse)),
|
|
9
|
+
atribd mounts three primitives in process: the `attest` write home
|
|
10
|
+
(`@atrib/attest`), the `recall` read home (`@atrib/recall`), and
|
|
11
|
+
`@atrib/summarize`. Together they serve the seventeen-tool union: the
|
|
12
|
+
fifteen legacy tool names plus `attest` plus `recall`. A record signed
|
|
13
|
+
through the daemon is byte-identical to one signed through the standalone
|
|
14
|
+
per-primitive binary: same handler code paths, same `_local.producer`
|
|
15
|
+
sidecar labels, same `resolveChainRoot` chain selection. The daemon is the
|
|
16
|
+
recommended local topology; the standalone binaries keep shipping and keep
|
|
17
|
+
working.
|
|
15
18
|
|
|
16
19
|
## Install
|
|
17
20
|
|
|
18
|
-
|
|
19
|
-
decision on the final npm name. Until then, run it from the workspace:
|
|
21
|
+
Published as `@atrib/daemon`; the daemon and its binary are named `atribd`:
|
|
20
22
|
|
|
21
23
|
```sh
|
|
22
|
-
|
|
23
|
-
|
|
24
|
+
npm install -g @atrib/daemon
|
|
25
|
+
atribd --help
|
|
24
26
|
```
|
|
25
27
|
|
|
26
|
-
|
|
28
|
+
Or run without installing:
|
|
27
29
|
|
|
28
30
|
```sh
|
|
29
31
|
npx --package @atrib/daemon atribd --help
|
|
30
32
|
```
|
|
31
33
|
|
|
34
|
+
From the workspace (development):
|
|
35
|
+
|
|
36
|
+
```sh
|
|
37
|
+
pnpm --filter @atrib/daemon... build
|
|
38
|
+
node services/atribd/dist/index.js --help
|
|
39
|
+
```
|
|
40
|
+
|
|
32
41
|
## Quick start
|
|
33
42
|
|
|
34
43
|
One daemon per profile ([D120](../../DECISIONS.md#d120-local-substrate-coordinator-keeps-startup-spawn-sidecars-wrapper-owned) partition axis). Start the HTTP daemon:
|
|
@@ -69,7 +78,9 @@ instance:
|
|
|
69
78
|
resolve through the spec [§1.5.4](../../atrib-spec.md#154-mcp-transport-params_meta) ladder with the [§1.5.3](../../atrib-spec.md#153-http-fallback-x-atrib-chain) `X-Atrib-Chain`
|
|
70
79
|
fallback.
|
|
71
80
|
- `tools/list` responses carry `ttlMs` and `cacheScope` (SEP-2549) so clients
|
|
72
|
-
can cache the tool catalogue.
|
|
81
|
+
can cache the tool catalogue. The default `ttlMs` is 5 minutes during the
|
|
82
|
+
alias window (operator-tunable via `--tools-list-ttl-ms` /
|
|
83
|
+
`ATRIBD_TOOLS_LIST_TTL_MS`).
|
|
73
84
|
|
|
74
85
|
The transport binding sits behind an adapter
|
|
75
86
|
(`src/transport-adapter.ts`). The current adapter runs the session-era MCP
|
|
@@ -97,13 +108,14 @@ open question. The stdio surfaces keep the ambient ladder unchanged.
|
|
|
97
108
|
|
|
98
109
|
## Write serialization
|
|
99
110
|
|
|
100
|
-
The daemon serializes write-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
111
|
+
The daemon serializes calls to any write-union tool name (`attest`, `emit`,
|
|
112
|
+
`atrib-annotate`, `atrib-revise`) per resolved `context_id`: read-tail,
|
|
113
|
+
sign, append runs one writer at a time per context, so concurrent writes
|
|
114
|
+
routed through one daemon, regardless of which tool name the caller used,
|
|
115
|
+
yield a linear chain. Writers that append to the mirror corpus without
|
|
116
|
+
routing through the daemon sit outside this boundary and can still fork a
|
|
117
|
+
chain; the `spec/conformance/atribd/cases/concurrent-writer-serialization/`
|
|
118
|
+
family pins both sides of that line.
|
|
107
119
|
|
|
108
120
|
## Degradation
|
|
109
121
|
|
|
@@ -121,9 +133,8 @@ and mirror stay on the host.
|
|
|
121
133
|
`node scripts/update-primitives-runtime.mjs --runtime atribd`. It discovers
|
|
122
134
|
`com.nader.atribd.*` LaunchAgents running this package's `dist/index.js`,
|
|
123
135
|
builds the dependency closure, restarts, probes health and the direct MCP
|
|
124
|
-
surface, and gates on the daemon health shape.
|
|
125
|
-
|
|
126
|
-
read the legacy shape.
|
|
136
|
+
surface, and gates on the daemon health shape. All operator profiles now
|
|
137
|
+
run `@atrib/daemon`; the topology gate validates the daemon shape.
|
|
127
138
|
- **Deprecated session flags.** `--session-idle-ms` and
|
|
128
139
|
`ATRIB_PRIMITIVES_SESSION_IDLE_MS` are accepted and ignored with a
|
|
129
140
|
one-line stderr notice, never a fatal error. The stateless daemon has no
|
|
@@ -142,8 +153,8 @@ and mirror stay on the host.
|
|
|
142
153
|
## Verify locally
|
|
143
154
|
|
|
144
155
|
```sh
|
|
145
|
-
pnpm --filter
|
|
146
|
-
pnpm --filter
|
|
156
|
+
pnpm --filter @atrib/daemon... build
|
|
157
|
+
pnpm --filter @atrib/daemon test
|
|
147
158
|
```
|
|
148
159
|
|
|
149
160
|
The test suite includes the reference tests for
|
package/dist/backend.js
CHANGED
|
@@ -30,27 +30,16 @@ const HEALTH_PROBE_ABSENT_CONTEXT_ID = 'f'.repeat(32);
|
|
|
30
30
|
const CONTEXT_ID_PATTERN = /^[0-9a-f]{32}$/;
|
|
31
31
|
const runtimeRequire = createRequire(import.meta.url);
|
|
32
32
|
export const PRIMITIVE_SPECS = [
|
|
33
|
+
// The attest/recall two-verb surface with the fifteen legacy tool names
|
|
34
|
+
// mounted as aliases (alias-window rule W1). Three mounts serve the
|
|
35
|
+
// seventeen-tool union: the write home (@atrib/attest), the read home
|
|
36
|
+
// (@atrib/recall), and summarize (no successor; relocates to the harness
|
|
37
|
+
// after the window).
|
|
33
38
|
{
|
|
34
|
-
name: '
|
|
35
|
-
packageName: '@atrib/
|
|
39
|
+
name: 'attest',
|
|
40
|
+
packageName: '@atrib/attest',
|
|
36
41
|
kind: 'write',
|
|
37
|
-
expectedTools: ['emit'],
|
|
38
|
-
mutatesLogOnCall: true,
|
|
39
|
-
probeMode: 'package-and-tool-surface',
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
name: 'annotate',
|
|
43
|
-
packageName: '@atrib/annotate',
|
|
44
|
-
kind: 'write',
|
|
45
|
-
expectedTools: ['atrib-annotate'],
|
|
46
|
-
mutatesLogOnCall: true,
|
|
47
|
-
probeMode: 'package-and-tool-surface',
|
|
48
|
-
},
|
|
49
|
-
{
|
|
50
|
-
name: 'revise',
|
|
51
|
-
packageName: '@atrib/revise',
|
|
52
|
-
kind: 'write',
|
|
53
|
-
expectedTools: ['atrib-revise'],
|
|
42
|
+
expectedTools: ['atrib-annotate', 'atrib-revise', 'attest', 'emit'],
|
|
54
43
|
mutatesLogOnCall: true,
|
|
55
44
|
probeMode: 'package-and-tool-surface',
|
|
56
45
|
},
|
|
@@ -59,6 +48,8 @@ export const PRIMITIVE_SPECS = [
|
|
|
59
48
|
packageName: '@atrib/recall',
|
|
60
49
|
kind: 'read',
|
|
61
50
|
expectedTools: [
|
|
51
|
+
'atrib-verify',
|
|
52
|
+
'recall',
|
|
62
53
|
'recall_annotations',
|
|
63
54
|
'recall_by_content',
|
|
64
55
|
'recall_by_signer',
|
|
@@ -67,18 +58,12 @@ export const PRIMITIVE_SPECS = [
|
|
|
67
58
|
'recall_revisions',
|
|
68
59
|
'recall_session_chain',
|
|
69
60
|
'recall_walk',
|
|
61
|
+
'trace',
|
|
62
|
+
'trace_forward',
|
|
70
63
|
],
|
|
71
64
|
mutatesLogOnCall: false,
|
|
72
65
|
probeMode: 'read-only-behavioral-probe',
|
|
73
66
|
},
|
|
74
|
-
{
|
|
75
|
-
name: 'trace',
|
|
76
|
-
packageName: '@atrib/trace',
|
|
77
|
-
kind: 'read',
|
|
78
|
-
expectedTools: ['trace', 'trace_forward'],
|
|
79
|
-
mutatesLogOnCall: false,
|
|
80
|
-
probeMode: 'package-and-tool-surface',
|
|
81
|
-
},
|
|
82
67
|
{
|
|
83
68
|
name: 'summarize',
|
|
84
69
|
packageName: '@atrib/summarize',
|
|
@@ -87,27 +72,15 @@ export const PRIMITIVE_SPECS = [
|
|
|
87
72
|
mutatesLogOnCall: false,
|
|
88
73
|
probeMode: 'package-and-tool-surface',
|
|
89
74
|
},
|
|
90
|
-
{
|
|
91
|
-
name: 'verify',
|
|
92
|
-
packageName: '@atrib/verify-mcp',
|
|
93
|
-
kind: 'read',
|
|
94
|
-
expectedTools: ['atrib-verify'],
|
|
95
|
-
mutatesLogOnCall: false,
|
|
96
|
-
probeMode: 'package-and-tool-surface',
|
|
97
|
-
},
|
|
98
75
|
];
|
|
99
76
|
/** Physical tool names served by the write handler. */
|
|
100
77
|
export const WRITE_TOOL_NAMES = new Set(PRIMITIVE_SPECS.filter((spec) => spec.kind === 'write').flatMap((spec) => [
|
|
101
78
|
...spec.expectedTools,
|
|
102
79
|
]));
|
|
103
80
|
const PRIMITIVES = [
|
|
104
|
-
['
|
|
105
|
-
['annotate', async () => (await import('@atrib/annotate')).createAtribAnnotateServer()],
|
|
106
|
-
['revise', async () => (await import('@atrib/revise')).createAtribReviseServer()],
|
|
81
|
+
['attest', async () => (await import('@atrib/attest')).createAtribAttestServer()],
|
|
107
82
|
['recall', async () => (await import('@atrib/recall')).createAtribRecallServer()],
|
|
108
|
-
['trace', async () => (await import('@atrib/trace')).createAtribTraceServer()],
|
|
109
83
|
['summarize', async () => (await import('@atrib/summarize')).createAtribSummarizeServer()],
|
|
110
|
-
['verify', async () => (await import('@atrib/verify-mcp')).createAtribVerifyServer()],
|
|
111
84
|
];
|
|
112
85
|
export function readPackageVersion() {
|
|
113
86
|
try {
|
|
@@ -436,6 +409,52 @@ async function probeVerifyBehavior(primitive, spec, timeoutMs) {
|
|
|
436
409
|
missing_required_record_rejected: true,
|
|
437
410
|
});
|
|
438
411
|
}
|
|
412
|
+
async function probeRecallVerbBehavior(primitive, spec, timeoutMs) {
|
|
413
|
+
const payload = await callProbeTool(primitive, 'recall', {
|
|
414
|
+
shape: 'walk',
|
|
415
|
+
direction: 'backward',
|
|
416
|
+
start: HEALTH_PROBE_ABSENT_HASH,
|
|
417
|
+
depth: 0,
|
|
418
|
+
compact: true,
|
|
419
|
+
}, timeoutMs);
|
|
420
|
+
if (payload.start_hash !== HEALTH_PROBE_ABSENT_HASH) {
|
|
421
|
+
throw new Error(`recall verb returned unexpected start_hash ${String(payload.start_hash)}`);
|
|
422
|
+
}
|
|
423
|
+
if (payload.direction !== 'backward') {
|
|
424
|
+
throw new Error(`recall verb returned unexpected direction ${String(payload.direction)}`);
|
|
425
|
+
}
|
|
426
|
+
const dangling = assertArray(payload.dangling, 'recall.dangling');
|
|
427
|
+
if (!dangling.includes(HEALTH_PROBE_ABSENT_HASH)) {
|
|
428
|
+
throw new Error('recall verb did not surface the absent probe hash as dangling');
|
|
429
|
+
}
|
|
430
|
+
const visited = assertArray(payload.visited, 'recall.visited');
|
|
431
|
+
if (visited.length !== 0) {
|
|
432
|
+
throw new Error('recall verb visited records for an absent probe hash');
|
|
433
|
+
}
|
|
434
|
+
return behavioralProbePassed(spec, 'read-only', {
|
|
435
|
+
tool: 'recall',
|
|
436
|
+
shape: 'walk',
|
|
437
|
+
absent_hash_dangling: true,
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
/**
|
|
441
|
+
* The read home mounts the whole read union (recall verb + 8 recall_* +
|
|
442
|
+
* trace + trace_forward + atrib-verify), so its behavioral probe runs the
|
|
443
|
+
* per-family probes against one mount and combines them. Any single
|
|
444
|
+
* failure fails the union probe.
|
|
445
|
+
*/
|
|
446
|
+
async function probeReadUnionBehavior(primitive, spec, timeoutMs) {
|
|
447
|
+
const recallProbe = await probeRecallBehavior(primitive, spec, timeoutMs);
|
|
448
|
+
const traceProbe = await probeTraceBehavior(primitive, spec, timeoutMs);
|
|
449
|
+
const verifyProbe = await probeVerifyBehavior(primitive, spec, timeoutMs);
|
|
450
|
+
const verbProbe = await probeRecallVerbBehavior(primitive, spec, timeoutMs);
|
|
451
|
+
return behavioralProbePassed(spec, 'read-only', {
|
|
452
|
+
recall: recallProbe.observed ?? {},
|
|
453
|
+
trace: traceProbe.observed ?? {},
|
|
454
|
+
verify: verifyProbe.observed ?? {},
|
|
455
|
+
recall_verb: verbProbe.observed ?? {},
|
|
456
|
+
});
|
|
457
|
+
}
|
|
439
458
|
async function inspectPrimitiveBehavioralProbes(mounted, timeoutMs) {
|
|
440
459
|
const byName = new Map(mounted.map((primitive) => [primitive.name, primitive]));
|
|
441
460
|
const entries = [];
|
|
@@ -454,17 +473,11 @@ async function inspectPrimitiveBehavioralProbes(mounted, timeoutMs) {
|
|
|
454
473
|
}
|
|
455
474
|
try {
|
|
456
475
|
if (spec.name === 'recall') {
|
|
457
|
-
entries.push([spec.name, await
|
|
458
|
-
}
|
|
459
|
-
else if (spec.name === 'trace') {
|
|
460
|
-
entries.push([spec.name, await probeTraceBehavior(primitive, spec, timeoutMs)]);
|
|
476
|
+
entries.push([spec.name, await probeReadUnionBehavior(primitive, spec, timeoutMs)]);
|
|
461
477
|
}
|
|
462
478
|
else if (spec.name === 'summarize') {
|
|
463
479
|
entries.push([spec.name, await probeSummarizeBehavior(primitive, spec, timeoutMs)]);
|
|
464
480
|
}
|
|
465
|
-
else if (spec.name === 'verify') {
|
|
466
|
-
entries.push([spec.name, await probeVerifyBehavior(primitive, spec, timeoutMs)]);
|
|
467
|
-
}
|
|
468
481
|
else {
|
|
469
482
|
entries.push([spec.name, behavioralProbeSkipped(spec, 'no behavioral probe defined')]);
|
|
470
483
|
}
|
|
@@ -575,14 +588,18 @@ export async function createAtribdBackend(options = {}) {
|
|
|
575
588
|
let callsTimedOut = 0;
|
|
576
589
|
let callsSettledAfterTimeout = 0;
|
|
577
590
|
for (const primitive of mounted) {
|
|
578
|
-
//
|
|
579
|
-
//
|
|
580
|
-
|
|
591
|
+
// Route kind is per TOOL, not per mount: any tool name the write
|
|
592
|
+
// handler serves (the write union: attest plus its legacy aliases)
|
|
593
|
+
// goes through the write lock regardless of which mount exposes it.
|
|
594
|
+
// Unknown tool names on unknown mounts default to the read handler,
|
|
595
|
+
// unless the mount is a registered write primitive.
|
|
596
|
+
const mountKind = specByName.get(primitive.name)?.kind ?? 'read';
|
|
581
597
|
for (const tool of primitive.tools) {
|
|
582
598
|
const existing = routeByTool.get(tool.name);
|
|
583
599
|
if (existing) {
|
|
584
600
|
throw new Error(`duplicate atrib primitive tool ${tool.name}: ${existing.primitive} and ${primitive.name}`);
|
|
585
601
|
}
|
|
602
|
+
const kind = WRITE_TOOL_NAMES.has(tool.name) ? 'write' : mountKind;
|
|
586
603
|
routeByTool.set(tool.name, { primitive: primitive.name, kind, client: primitive.client });
|
|
587
604
|
tools.push(tool);
|
|
588
605
|
}
|
package/dist/http-host.js
CHANGED
|
@@ -34,7 +34,14 @@ import { createSessionSdkStatelessAdapter, } from './transport-adapter.js';
|
|
|
34
34
|
export const DEFAULT_HTTP_HOST = '127.0.0.1';
|
|
35
35
|
export const DEFAULT_HTTP_PORT = 8796;
|
|
36
36
|
export const DEFAULT_HTTP_PATH = '/mcp';
|
|
37
|
-
|
|
37
|
+
// Alias-window rule W2 (attest/recall rename): while legacy tool names are
|
|
38
|
+
// still being retired, every default deployment advertises a short tools/list
|
|
39
|
+
// ttlMs so a rename propagates on cache expiry within minutes, and a name may
|
|
40
|
+
// leave the default list only after the longest ttlMs the deployment ever
|
|
41
|
+
// advertised has elapsed. Operators tune via --tools-list-ttl-ms /
|
|
42
|
+
// ATRIBD_TOOLS_LIST_TTL_MS; the pre-window default was 24 hours and can
|
|
43
|
+
// return once retirement completes.
|
|
44
|
+
export const DEFAULT_TOOLS_LIST_TTL_MS = 5 * 60 * 1000;
|
|
38
45
|
const MAX_JSON_BODY_BYTES = 1024 * 1024;
|
|
39
46
|
function createBackendProvider(factory) {
|
|
40
47
|
const startedAt = Date.now();
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
// SPDX-License-Identifier: Apache-2.0
|
|
3
3
|
/**
|
|
4
|
-
* atribd: the local daemon for atrib's
|
|
4
|
+
* atribd: the local daemon for atrib's cognitive surface.
|
|
5
5
|
*
|
|
6
|
-
* One host-owned process mounts the
|
|
7
|
-
* and
|
|
8
|
-
*
|
|
6
|
+
* One host-owned process mounts the attest write home, the recall read
|
|
7
|
+
* home, and summarize in process and serves the seventeen-tool alias-window
|
|
8
|
+
* union (fifteen legacy names plus the attest and recall verbs) as thin
|
|
9
|
+
* aliases over two internal handlers (write, read). Transports: stateless Streamable HTTP (the
|
|
9
10
|
* recommended daemon topology), direct stdio (in-process), and a
|
|
10
11
|
* stdio-to-HTTP proxy shim for startup-spawn harnesses.
|
|
11
12
|
*
|
|
@@ -170,7 +171,7 @@ Options:
|
|
|
170
171
|
--port <port> HTTP bind port. Defaults to 8796. Use 0 for ephemeral.
|
|
171
172
|
--path <path> HTTP MCP path. Defaults to /mcp.
|
|
172
173
|
--tool-timeout-ms <ms> Bound each primitive tool call. Defaults to 45000.
|
|
173
|
-
--tools-list-ttl-ms <ms> SEP-2549 freshness hint on tools/list. Defaults to
|
|
174
|
+
--tools-list-ttl-ms <ms> SEP-2549 freshness hint on tools/list. Defaults to 5 minutes (alias-window W2).
|
|
174
175
|
--ambient-context Opt the HTTP surface back into ambient context discovery
|
|
175
176
|
(D078/D083). Default is explicit-required.
|
|
176
177
|
--session-idle-ms <ms> Deprecated; ignored (the stateless daemon has no sessions).
|
package/package.json
CHANGED
|
@@ -1,7 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atrib/daemon",
|
|
3
|
-
"version": "0.1
|
|
4
|
-
"description": "Local daemon for atrib. Serves the
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "Local daemon for atrib. Serves the attest/recall verbs plus the legacy alias tools from one stateless-native process over Streamable HTTP or stdio.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"atrib",
|
|
7
|
+
"mcp",
|
|
8
|
+
"model-context-protocol",
|
|
9
|
+
"attribution",
|
|
10
|
+
"verifiable",
|
|
11
|
+
"agent",
|
|
12
|
+
"cognitive-primitive",
|
|
13
|
+
"daemon",
|
|
14
|
+
"ed25519"
|
|
15
|
+
],
|
|
5
16
|
"author": "Nader Helmy",
|
|
6
17
|
"license": "Apache-2.0",
|
|
7
18
|
"homepage": "https://github.com/creatornader/atrib/tree/main/services/atribd",
|
|
@@ -17,7 +28,8 @@
|
|
|
17
28
|
".": {
|
|
18
29
|
"types": "./dist/index.d.ts",
|
|
19
30
|
"import": "./dist/index.js"
|
|
20
|
-
}
|
|
31
|
+
},
|
|
32
|
+
"./package.json": "./package.json"
|
|
21
33
|
},
|
|
22
34
|
"bin": {
|
|
23
35
|
"atribd": "./dist/index.js"
|
|
@@ -27,14 +39,11 @@
|
|
|
27
39
|
},
|
|
28
40
|
"dependencies": {
|
|
29
41
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
42
|
+
"@atrib/attest": "0.1.0",
|
|
30
43
|
"@atrib/mcp": "0.21.0",
|
|
31
|
-
"@atrib/
|
|
32
|
-
"@atrib/annotate": "0.2.41",
|
|
33
|
-
"@atrib/recall": "0.14.7",
|
|
44
|
+
"@atrib/recall": "2.0.0",
|
|
34
45
|
"@atrib/summarize": "0.4.23",
|
|
35
|
-
"@atrib/
|
|
36
|
-
"@atrib/revise": "0.2.41",
|
|
37
|
-
"@atrib/verify-mcp": "0.2.22"
|
|
46
|
+
"@atrib/verify": "0.10.0"
|
|
38
47
|
},
|
|
39
48
|
"devDependencies": {
|
|
40
49
|
"@types/node": "^25.9.3",
|