noms-cli 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +23 -2
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +292 -0
- package/dist/self-test-vectors.d.ts +7 -0
- package/dist/self-test-vectors.js +8 -0
- package/package.json +36 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 OpenRai
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,24 @@
|
|
|
1
|
-
#
|
|
1
|
+
# noms-cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The TypeScript command-line reference implementation for NOMS. It uses
|
|
4
|
+
`@openrai/noms` for the protocol implementation and exposes both `noms-cli` and
|
|
5
|
+
`noms` commands.
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
npx noms-cli self-test
|
|
9
|
+
npx noms-cli sign --key-file ./account-key.hex --message "Hello world"
|
|
10
|
+
npx noms-cli verify --account nano_… --signature "$SIGNATURE" --message "Hello world"
|
|
11
|
+
npx --package noms-cli noms inspect --message "Hello world"
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
The unscoped npm package name `noms` belongs to an unrelated package. The
|
|
15
|
+
`noms` executable is available through the `noms-cli` package.
|
|
16
|
+
|
|
17
|
+
Signing keys are read from a restricted-permission file, a hidden prompt, or
|
|
18
|
+
standard input. Never pass private keys as command-line arguments. Use a
|
|
19
|
+
zero-balance test account when trying the tool.
|
|
20
|
+
|
|
21
|
+
For exact multiline messages use `--message-stdin`. Verification can read a
|
|
22
|
+
signature from stdin with `--signature-stdin`. Key files must exclude group and
|
|
23
|
+
other access on POSIX systems; on Windows, restrict the file ACL to your user.
|
|
24
|
+
Running a package through `npx` is a software supply-chain decision.
|
package/dist/cli.d.ts
ADDED
package/dist/cli.js
ADDED
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { open } from 'node:fs/promises';
|
|
3
|
+
import { stdin, stdout, stderr } from 'node:process';
|
|
4
|
+
import { emitKeypressEvents } from 'node:readline';
|
|
5
|
+
import { encodePayload, hashMessage, signMessage, verifyMessage, } from '@openrai/noms';
|
|
6
|
+
import { TEST_PRIVATE_KEY, TEST_ACCOUNT, TEST_MESSAGE, TEST_SIGNATURE, EMPTY_SIGNATURE, EMPTY_DIGEST, NONCANONICAL_SIGNATURE } from './self-test-vectors.js';
|
|
7
|
+
function trimEnvelope(value) {
|
|
8
|
+
return value.replace(/^[ \t\r\n]+|[ \t\r\n]+$/g, '');
|
|
9
|
+
}
|
|
10
|
+
function hex(bytes) {
|
|
11
|
+
return Array.from(bytes, (byte) => byte.toString(16).padStart(2, '0')).join('');
|
|
12
|
+
}
|
|
13
|
+
function optionValue(args, name) {
|
|
14
|
+
const indexes = [];
|
|
15
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
16
|
+
if (args[index] === name)
|
|
17
|
+
indexes.push(index);
|
|
18
|
+
if (['--account', '--key-file', '--message', '--signature'].includes(args[index]))
|
|
19
|
+
index += 1;
|
|
20
|
+
}
|
|
21
|
+
if (indexes.length > 1)
|
|
22
|
+
throw new Error(`${name} may be provided only once`);
|
|
23
|
+
if (indexes.length === 0)
|
|
24
|
+
return undefined;
|
|
25
|
+
const value = args[indexes[0] + 1];
|
|
26
|
+
if (value === undefined)
|
|
27
|
+
throw new Error(`missing value for ${name}`);
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
function selectOptions(args, names) {
|
|
31
|
+
const selected = [];
|
|
32
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
33
|
+
const name = args[index];
|
|
34
|
+
const takesValue = ['--account', '--key-file', '--message', '--signature'].includes(name);
|
|
35
|
+
if (names.includes(name))
|
|
36
|
+
selected.push(...args.slice(index, index + (takesValue ? 2 : 1)));
|
|
37
|
+
if (takesValue)
|
|
38
|
+
index += 1;
|
|
39
|
+
}
|
|
40
|
+
return selected;
|
|
41
|
+
}
|
|
42
|
+
function hasFlag(args, name) {
|
|
43
|
+
const valueOptions = new Set(['--account', '--key-file', '--message', '--signature']);
|
|
44
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
45
|
+
if (valueOptions.has(args[index])) {
|
|
46
|
+
index += 1;
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
if (args[index] === name)
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
function rejectUnknown(args, valueOptions, flagOptions) {
|
|
55
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
56
|
+
const arg = args[index];
|
|
57
|
+
if (valueOptions.includes(arg)) {
|
|
58
|
+
index += 1;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
if (flagOptions.includes(arg))
|
|
62
|
+
continue;
|
|
63
|
+
throw new Error(`unexpected argument: ${arg}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
async function readBounded(source, limit) {
|
|
67
|
+
const chunks = [];
|
|
68
|
+
let length = 0;
|
|
69
|
+
for await (const chunk of source) {
|
|
70
|
+
length += chunk.length;
|
|
71
|
+
if (length > limit)
|
|
72
|
+
throw new Error('input exceeds the implementation size limit');
|
|
73
|
+
chunks.push(Buffer.from(chunk));
|
|
74
|
+
}
|
|
75
|
+
return Buffer.concat(chunks);
|
|
76
|
+
}
|
|
77
|
+
async function readStdinText(label) {
|
|
78
|
+
const bytes = await readBounded(stdin, label === 'message' ? 16 * 1024 * 1024 : 4096);
|
|
79
|
+
try {
|
|
80
|
+
return new TextDecoder('utf-8', { fatal: true, ignoreBOM: true }).decode(bytes);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
throw new Error(`${label} input must be valid UTF-8`);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function promptHiddenKey() {
|
|
87
|
+
if (!stdin.isTTY || typeof stdin.setRawMode !== 'function') {
|
|
88
|
+
throw new Error('--prompt-key requires an interactive terminal');
|
|
89
|
+
}
|
|
90
|
+
emitKeypressEvents(stdin);
|
|
91
|
+
stdin.setRawMode(true);
|
|
92
|
+
stdin.resume();
|
|
93
|
+
stderr.write('Nano account private key (hex): ');
|
|
94
|
+
return new Promise((resolve, reject) => {
|
|
95
|
+
let value = '';
|
|
96
|
+
const finish = (error) => {
|
|
97
|
+
stdin.off('keypress', onKeypress);
|
|
98
|
+
stdin.setRawMode(false);
|
|
99
|
+
stdin.pause();
|
|
100
|
+
stderr.write('\n');
|
|
101
|
+
if (error)
|
|
102
|
+
reject(error);
|
|
103
|
+
else
|
|
104
|
+
resolve(value);
|
|
105
|
+
};
|
|
106
|
+
const onKeypress = (character, key = {}) => {
|
|
107
|
+
if (key.ctrl && key.name === 'c') {
|
|
108
|
+
finish(new Error('key prompt cancelled'));
|
|
109
|
+
}
|
|
110
|
+
else if (key.name === 'return' || key.name === 'enter') {
|
|
111
|
+
finish();
|
|
112
|
+
}
|
|
113
|
+
else if (key.name === 'backspace') {
|
|
114
|
+
value = value.slice(0, -1);
|
|
115
|
+
}
|
|
116
|
+
else if (character && !key.ctrl) {
|
|
117
|
+
value += character;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
stdin.on('keypress', onKeypress);
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
async function readKey(args) {
|
|
124
|
+
rejectUnknown(args, ['--key-file'], ['--key-stdin', '--prompt-key', '--message', '--message-stdin']);
|
|
125
|
+
const keyFile = optionValue(args, '--key-file');
|
|
126
|
+
const keyStdin = hasFlag(args, '--key-stdin');
|
|
127
|
+
const promptKey = hasFlag(args, '--prompt-key');
|
|
128
|
+
if (Number(keyFile !== undefined) + Number(keyStdin) + Number(promptKey) !== 1) {
|
|
129
|
+
throw new Error('provide exactly one of --key-file PATH, --key-stdin, or --prompt-key; never pass a private key as an argument');
|
|
130
|
+
}
|
|
131
|
+
if (keyFile !== undefined) {
|
|
132
|
+
const handle = await open(keyFile, 'r');
|
|
133
|
+
try {
|
|
134
|
+
const metadata = await handle.stat();
|
|
135
|
+
if (metadata.size > 4096)
|
|
136
|
+
throw new Error('key file exceeds the implementation size limit');
|
|
137
|
+
if (process.platform !== 'win32' && (metadata.mode & 0o077) !== 0) {
|
|
138
|
+
throw new Error('key file permissions must exclude group and other access (use chmod 600)');
|
|
139
|
+
}
|
|
140
|
+
return trimEnvelope((await readBounded(handle.createReadStream({ autoClose: false }), 4096)).toString('utf8'));
|
|
141
|
+
}
|
|
142
|
+
finally {
|
|
143
|
+
await handle.close();
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return trimEnvelope(keyStdin ? await readStdinText('key') : await promptHiddenKey());
|
|
147
|
+
}
|
|
148
|
+
async function readMessage(args) {
|
|
149
|
+
rejectUnknown(args, ['--message'], ['--message-stdin']);
|
|
150
|
+
const message = optionValue(args, '--message');
|
|
151
|
+
const messageStdin = hasFlag(args, '--message-stdin');
|
|
152
|
+
if (Number(message !== undefined) + Number(messageStdin) !== 1) {
|
|
153
|
+
throw new Error('provide exactly one of --message TEXT or --message-stdin');
|
|
154
|
+
}
|
|
155
|
+
return messageStdin ? readStdinText('message') : message;
|
|
156
|
+
}
|
|
157
|
+
async function signingKeyAndMessage(args) {
|
|
158
|
+
if ((hasFlag(args, '--key-stdin') || hasFlag(args, '--prompt-key')) && hasFlag(args, '--message-stdin')) {
|
|
159
|
+
throw new Error('standard input cannot supply both key and message; use --key-file with --message-stdin');
|
|
160
|
+
}
|
|
161
|
+
rejectUnknown(args, ['--key-file', '--message'], ['--key-stdin', '--prompt-key', '--message-stdin']);
|
|
162
|
+
const keyArgs = selectOptions(args, ['--key-file', '--key-stdin', '--prompt-key']);
|
|
163
|
+
const messageArgs = selectOptions(args, ['--message', '--message-stdin']);
|
|
164
|
+
const key = await readKey(keyArgs);
|
|
165
|
+
const message = await readMessage(messageArgs);
|
|
166
|
+
return { key, message };
|
|
167
|
+
}
|
|
168
|
+
const HELP = {
|
|
169
|
+
help: `Usage: noms <command> [options]
|
|
170
|
+
|
|
171
|
+
Commands:
|
|
172
|
+
sign Sign an exact message with a Nano account private key
|
|
173
|
+
verify Verify a signature against an exact message and Nano account
|
|
174
|
+
inspect Show the ORIS-001 payload bytes and digest
|
|
175
|
+
self-test Check the built-in NOMS conformance vectors
|
|
176
|
+
|
|
177
|
+
Run "noms <command> --help" for command-specific options and examples.`,
|
|
178
|
+
sign: `Usage: noms sign (--key-file PATH | --key-stdin | --prompt-key) (--message TEXT | --message-stdin)
|
|
179
|
+
|
|
180
|
+
Sign an exact message with a raw 32-byte Nano account private key. A wallet
|
|
181
|
+
seed is not an account private key. The key is never accepted as an argument.
|
|
182
|
+
|
|
183
|
+
Examples:
|
|
184
|
+
noms sign --key-file ./account-key.hex --message "Hello, Nano!"
|
|
185
|
+
noms sign --prompt-key --message "Hello, Nano!"
|
|
186
|
+
printf %s "Hello, Nano!" | noms sign --key-file ./account-key.hex --message-stdin
|
|
187
|
+
cat ./account-key.hex | noms sign --key-stdin --message "Hello, Nano!"
|
|
188
|
+
|
|
189
|
+
On macOS and Linux, restrict a key file with: chmod 600 ./account-key.hex
|
|
190
|
+
The signature is written to stdout as 128 lowercase hexadecimal characters.`,
|
|
191
|
+
verify: `Usage: noms verify --account NANO_ADDRESS (--signature HEX | --signature-stdin) (--message TEXT | --message-stdin)
|
|
192
|
+
|
|
193
|
+
Verify a NOMS signature for the exact message and Nano account. Prints "valid"
|
|
194
|
+
or "invalid"; exits with status 0 for valid, 1 for invalid, and 2 for errors.
|
|
195
|
+
|
|
196
|
+
Examples:
|
|
197
|
+
noms verify --account nano_... --signature "$SIGNATURE" --message "Hello, Nano!"
|
|
198
|
+
printf %s "Hello, Nano!" | noms verify --account nano_... --signature "$SIGNATURE" --message-stdin
|
|
199
|
+
printf %s "$SIGNATURE" | noms verify --account nano_... --signature-stdin --message "Hello, Nano!"
|
|
200
|
+
|
|
201
|
+
The message is not trimmed, normalized, or otherwise changed.`,
|
|
202
|
+
inspect: `Usage: noms inspect (--message TEXT | --message-stdin)
|
|
203
|
+
|
|
204
|
+
Display the exact UTF-8 byte length, ORIS-001 payload, and BLAKE2b-256 digest.
|
|
205
|
+
|
|
206
|
+
Examples:
|
|
207
|
+
noms inspect --message "Hello, Nano!"
|
|
208
|
+
printf %s "Hello, Nano!" | noms inspect --message-stdin`,
|
|
209
|
+
'self-test': `Usage: noms self-test
|
|
210
|
+
|
|
211
|
+
Run the built-in known-answer and canonical-signature checks. This needs no
|
|
212
|
+
network connection or Nano node.
|
|
213
|
+
|
|
214
|
+
Example:
|
|
215
|
+
noms self-test`,
|
|
216
|
+
};
|
|
217
|
+
function showHelp(topic) {
|
|
218
|
+
const help = HELP[topic ?? 'help'];
|
|
219
|
+
if (!help)
|
|
220
|
+
return false;
|
|
221
|
+
stdout.write(`${help}\n`);
|
|
222
|
+
return true;
|
|
223
|
+
}
|
|
224
|
+
async function run(command, args) {
|
|
225
|
+
if (command === 'sign') {
|
|
226
|
+
const { key, message } = await signingKeyAndMessage(args);
|
|
227
|
+
stdout.write(`${signMessage(message, key)}\n`);
|
|
228
|
+
return true;
|
|
229
|
+
}
|
|
230
|
+
if (command === 'verify') {
|
|
231
|
+
rejectUnknown(args, ['--account', '--signature', '--message'], ['--signature-stdin', '--message-stdin']);
|
|
232
|
+
const account = optionValue(args, '--account');
|
|
233
|
+
if (!account)
|
|
234
|
+
throw new Error('missing --account');
|
|
235
|
+
const signature = optionValue(args, '--signature');
|
|
236
|
+
const signatureStdin = hasFlag(args, '--signature-stdin');
|
|
237
|
+
if (Number(signature !== undefined) + Number(signatureStdin) !== 1) {
|
|
238
|
+
throw new Error('provide exactly one of --signature HEX or --signature-stdin');
|
|
239
|
+
}
|
|
240
|
+
if (signatureStdin && hasFlag(args, '--message-stdin')) {
|
|
241
|
+
throw new Error('standard input cannot supply both signature and message; use --message TEXT');
|
|
242
|
+
}
|
|
243
|
+
const message = await readMessage(selectOptions(args, ['--message', '--message-stdin']));
|
|
244
|
+
const signatureText = signatureStdin ? trimEnvelope(await readStdinText('signature')) : signature;
|
|
245
|
+
const valid = verifyMessage(message, signatureText, account);
|
|
246
|
+
stdout.write(valid ? 'valid\n' : 'invalid\n');
|
|
247
|
+
return valid;
|
|
248
|
+
}
|
|
249
|
+
if (command === 'inspect') {
|
|
250
|
+
const message = await readMessage(args);
|
|
251
|
+
const payload = encodePayload(message);
|
|
252
|
+
stdout.write(`utf8_bytes: ${new TextEncoder().encode(message).length}\npayload: ${hex(payload)}\ndigest: ${hex(hashMessage(message))}\n`);
|
|
253
|
+
return true;
|
|
254
|
+
}
|
|
255
|
+
if (command === 'self-test') {
|
|
256
|
+
rejectUnknown(args, [], []);
|
|
257
|
+
const ok = signMessage(TEST_MESSAGE, TEST_PRIVATE_KEY) === TEST_SIGNATURE
|
|
258
|
+
&& verifyMessage(TEST_MESSAGE, TEST_SIGNATURE, TEST_ACCOUNT)
|
|
259
|
+
&& hex(hashMessage('')) === EMPTY_DIGEST
|
|
260
|
+
&& signMessage('', TEST_PRIVATE_KEY) === EMPTY_SIGNATURE
|
|
261
|
+
&& verifyMessage('', EMPTY_SIGNATURE, TEST_ACCOUNT);
|
|
262
|
+
const strictOk = ok && !verifyMessage(TEST_MESSAGE, NONCANONICAL_SIGNATURE, TEST_ACCOUNT);
|
|
263
|
+
stdout.write(strictOk ? 'NOMS conformance vectors passed\n' : 'NOMS conformance vectors failed\n');
|
|
264
|
+
return strictOk;
|
|
265
|
+
}
|
|
266
|
+
throw new Error(`unknown command: ${command}`);
|
|
267
|
+
}
|
|
268
|
+
const [command, ...args] = process.argv.slice(2);
|
|
269
|
+
if (command === undefined || command === '--help' || command === '-h') {
|
|
270
|
+
showHelp();
|
|
271
|
+
}
|
|
272
|
+
else if (command === 'help') {
|
|
273
|
+
if (!showHelp(args[0])) {
|
|
274
|
+
stderr.write(`unknown help topic: ${args[0]}\n`);
|
|
275
|
+
process.exitCode = 2;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
else if (args.includes('--help') || args.includes('-h')) {
|
|
279
|
+
if (!showHelp(command)) {
|
|
280
|
+
stderr.write(`unknown command: ${command}\n`);
|
|
281
|
+
process.exitCode = 2;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
else {
|
|
285
|
+
run(command, args).then((success) => {
|
|
286
|
+
if (!success)
|
|
287
|
+
process.exitCode = 1;
|
|
288
|
+
}).catch((error) => {
|
|
289
|
+
stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
290
|
+
process.exitCode = 2;
|
|
291
|
+
});
|
|
292
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare const TEST_PRIVATE_KEY = "681fd5ed71a9f81e9d29e3450f6cd8aacb87346fd21a26003389290b9d0cb173";
|
|
2
|
+
export declare const TEST_ACCOUNT = "nano_3noms9a1zytox399kygpge6cc7hu1z79ms1cgzojodz8741qi7w5u3nzb8mn";
|
|
3
|
+
export declare const TEST_MESSAGE = "Hej Nano!\uD83E\uDD66";
|
|
4
|
+
export declare const TEST_SIGNATURE = "535c745819d0f40056f3c46402b4fae4356b3a8897bde99c955d411920e740d781e6dddcbde228e8b86c4383a1003f9f315519ff73bd356f561d19865dc90f09";
|
|
5
|
+
export declare const EMPTY_SIGNATURE = "8fca45d1490a276ac9d4376d9251df3a1069f673013c33d49f3490077066f174d7fb6795b966e1d9078952ad065f836b35cd82d402cbeb63f9ace94b2123c506";
|
|
6
|
+
export declare const EMPTY_DIGEST = "977a10e19a7857eefad986d73b071bbb7dad60846c7785f6d0ccffe0d7bd40b9";
|
|
7
|
+
export declare const NONCANONICAL_SIGNATURE = "535c745819d0f40056f3c46402b4fae4356b3a8897bde99c955d411920e740d76ebad339d8453b408f093b2680fa1db4315519ff73bd356f561d19865dc90f19";
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
// Generated by scripts/check-fixtures.mjs from the canonical suite.
|
|
2
|
+
export const TEST_PRIVATE_KEY = "681fd5ed71a9f81e9d29e3450f6cd8aacb87346fd21a26003389290b9d0cb173";
|
|
3
|
+
export const TEST_ACCOUNT = "nano_3noms9a1zytox399kygpge6cc7hu1z79ms1cgzojodz8741qi7w5u3nzb8mn";
|
|
4
|
+
export const TEST_MESSAGE = "Hej Nano!🥦";
|
|
5
|
+
export const TEST_SIGNATURE = "535c745819d0f40056f3c46402b4fae4356b3a8897bde99c955d411920e740d781e6dddcbde228e8b86c4383a1003f9f315519ff73bd356f561d19865dc90f09";
|
|
6
|
+
export const EMPTY_SIGNATURE = "8fca45d1490a276ac9d4376d9251df3a1069f673013c33d49f3490077066f174d7fb6795b966e1d9078952ad065f836b35cd82d402cbeb63f9ace94b2123c506";
|
|
7
|
+
export const EMPTY_DIGEST = "977a10e19a7857eefad986d73b071bbb7dad60846c7785f6d0ccffe0d7bd40b9";
|
|
8
|
+
export const NONCANONICAL_SIGNATURE = "535c745819d0f40056f3c46402b4fae4356b3a8897bde99c955d411920e740d76ebad339d8453b408f093b2680fa1db4315519ff73bd356f561d19865dc90f19";
|
package/package.json
CHANGED
|
@@ -1,6 +1,39 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "noms-cli",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Command-line interface for Nano Off-chain Message Signing",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "git+https://github.com/OpenRai/noms.git"
|
|
8
|
+
},
|
|
9
|
+
"license": "MIT",
|
|
10
|
+
"type": "module",
|
|
11
|
+
"engines": {
|
|
12
|
+
"node": ">=20.19.0"
|
|
13
|
+
},
|
|
14
|
+
"bin": {
|
|
15
|
+
"noms": "dist/cli.js",
|
|
16
|
+
"noms-cli": "dist/cli.js"
|
|
17
|
+
},
|
|
18
|
+
"files": [
|
|
19
|
+
"dist"
|
|
20
|
+
],
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@openrai/noms": "^0.2.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/node": "^24.6.0",
|
|
26
|
+
"typescript": "^6.0.2",
|
|
27
|
+
"vitest": "^4.1.2"
|
|
28
|
+
},
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"scripts": {
|
|
33
|
+
"build": "tsc -p tsconfig.json && node scripts/chmod-bin.mjs",
|
|
34
|
+
"test": "vitest run",
|
|
35
|
+
"release:patch": "pnpm version patch --tag-version-prefix noms-cli-npm-v --message \"release: noms-cli %s\"",
|
|
36
|
+
"release:minor": "pnpm version minor --tag-version-prefix noms-cli-npm-v --message \"release: noms-cli %s\"",
|
|
37
|
+
"release:major": "pnpm version major --tag-version-prefix noms-cli-npm-v --message \"release: noms-cli %s\""
|
|
38
|
+
}
|
|
6
39
|
}
|