@newtype-ai/nit 0.2.6 → 0.4.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/README.md +11 -1
- package/dist/{chunk-3RRHLPAC.js → chunk-BC7OEIFR.js} +412 -23
- package/dist/cli.js +11 -1
- package/dist/index.d.ts +40 -1
- package/dist/index.js +13 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -76,6 +76,8 @@ nit push --all
|
|
|
76
76
|
|
|
77
77
|
Platforms verify your identity by challenging you to sign a nonce — no shared secrets, no bearer tokens.
|
|
78
78
|
|
|
79
|
+
nit also derives blockchain wallet addresses from your keypair — Solana (Ed25519 native) and EVM chains (Ethereum, BSC, Polygon, etc.) via a deterministic secp256k1 derivation. Run `nit status` to see your addresses.
|
|
80
|
+
|
|
79
81
|
### Branches
|
|
80
82
|
|
|
81
83
|
Each branch is a different agent card for a different platform. Branch name = root domain of the platform (e.g., `faam.io`, `polymarket.com`).
|
|
@@ -132,7 +134,7 @@ your-project/
|
|
|
132
134
|
## Programmatic API
|
|
133
135
|
|
|
134
136
|
```typescript
|
|
135
|
-
import { init, commit, checkout, branch, push, status, sign, loginPayload } from '@newtype-ai/nit';
|
|
137
|
+
import { init, commit, checkout, branch, push, status, sign, loginPayload, loadRawKeyPair, getWalletAddresses } from '@newtype-ai/nit';
|
|
136
138
|
|
|
137
139
|
await init();
|
|
138
140
|
|
|
@@ -143,6 +145,14 @@ const payload = await loginPayload('faam.io');
|
|
|
143
145
|
// Customize card, then commit & push
|
|
144
146
|
await commit('FAAM config');
|
|
145
147
|
await push({ all: true });
|
|
148
|
+
|
|
149
|
+
// Access raw Ed25519 keypair (64 bytes: [seed || pubkey])
|
|
150
|
+
const keypair = await loadRawKeyPair('/path/to/.nit');
|
|
151
|
+
// → Uint8Array(64) — compatible with Solana and other Ed25519 libraries
|
|
152
|
+
|
|
153
|
+
// Get blockchain wallet addresses (derived from your identity)
|
|
154
|
+
const addresses = await getWalletAddresses('/path/to/.nit');
|
|
155
|
+
// → { solana: "C54kvW3...", ethereum: "0x2317..." }
|
|
146
156
|
```
|
|
147
157
|
|
|
148
158
|
## Design Principles
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
// nit — version control for agent cards
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { promises as
|
|
5
|
-
import { join as
|
|
4
|
+
import { promises as fs7, statSync } from "fs";
|
|
5
|
+
import { join as join7, basename as basename2, resolve as resolve2 } from "path";
|
|
6
6
|
|
|
7
7
|
// src/objects.ts
|
|
8
8
|
import { createHash } from "crypto";
|
|
@@ -232,6 +232,24 @@ async function loadPrivateKey(nitDir) {
|
|
|
232
232
|
format: "jwk"
|
|
233
233
|
});
|
|
234
234
|
}
|
|
235
|
+
async function loadRawKeyPair(nitDir) {
|
|
236
|
+
const pubBase64 = await loadPublicKey(nitDir);
|
|
237
|
+
const keyPath = join3(nitDir, "identity", "agent.key");
|
|
238
|
+
let privBase64;
|
|
239
|
+
try {
|
|
240
|
+
privBase64 = (await fs3.readFile(keyPath, "utf-8")).trim();
|
|
241
|
+
} catch {
|
|
242
|
+
throw new Error(
|
|
243
|
+
"Private key not found at .nit/identity/agent.key. Regenerate with `nit init`."
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
const seed = Buffer.from(privBase64, "base64");
|
|
247
|
+
const pubkey = Buffer.from(pubBase64, "base64");
|
|
248
|
+
const keypair = new Uint8Array(64);
|
|
249
|
+
keypair.set(seed, 0);
|
|
250
|
+
keypair.set(pubkey, 32);
|
|
251
|
+
return keypair;
|
|
252
|
+
}
|
|
235
253
|
function formatPublicKeyField(pubBase64) {
|
|
236
254
|
return `ed25519:${pubBase64}`;
|
|
237
255
|
}
|
|
@@ -466,6 +484,362 @@ function parseFrontmatter(content, dirName, filePath) {
|
|
|
466
484
|
};
|
|
467
485
|
}
|
|
468
486
|
|
|
487
|
+
// src/wallet.ts
|
|
488
|
+
import { createHmac, createECDH } from "crypto";
|
|
489
|
+
import { promises as fs5 } from "fs";
|
|
490
|
+
import { join as join5 } from "path";
|
|
491
|
+
|
|
492
|
+
// node_modules/@noble/hashes/_u64.js
|
|
493
|
+
var U32_MASK64 = /* @__PURE__ */ BigInt(2 ** 32 - 1);
|
|
494
|
+
var _32n = /* @__PURE__ */ BigInt(32);
|
|
495
|
+
function fromBig(n, le = false) {
|
|
496
|
+
if (le)
|
|
497
|
+
return { h: Number(n & U32_MASK64), l: Number(n >> _32n & U32_MASK64) };
|
|
498
|
+
return { h: Number(n >> _32n & U32_MASK64) | 0, l: Number(n & U32_MASK64) | 0 };
|
|
499
|
+
}
|
|
500
|
+
function split(lst, le = false) {
|
|
501
|
+
const len = lst.length;
|
|
502
|
+
let Ah = new Uint32Array(len);
|
|
503
|
+
let Al = new Uint32Array(len);
|
|
504
|
+
for (let i = 0; i < len; i++) {
|
|
505
|
+
const { h, l } = fromBig(lst[i], le);
|
|
506
|
+
[Ah[i], Al[i]] = [h, l];
|
|
507
|
+
}
|
|
508
|
+
return [Ah, Al];
|
|
509
|
+
}
|
|
510
|
+
var rotlSH = (h, l, s) => h << s | l >>> 32 - s;
|
|
511
|
+
var rotlSL = (h, l, s) => l << s | h >>> 32 - s;
|
|
512
|
+
var rotlBH = (h, l, s) => l << s - 32 | h >>> 64 - s;
|
|
513
|
+
var rotlBL = (h, l, s) => h << s - 32 | l >>> 64 - s;
|
|
514
|
+
|
|
515
|
+
// node_modules/@noble/hashes/utils.js
|
|
516
|
+
function isBytes(a) {
|
|
517
|
+
return a instanceof Uint8Array || ArrayBuffer.isView(a) && a.constructor.name === "Uint8Array";
|
|
518
|
+
}
|
|
519
|
+
function anumber(n, title = "") {
|
|
520
|
+
if (!Number.isSafeInteger(n) || n < 0) {
|
|
521
|
+
const prefix = title && `"${title}" `;
|
|
522
|
+
throw new Error(`${prefix}expected integer >= 0, got ${n}`);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
function abytes(value, length, title = "") {
|
|
526
|
+
const bytes = isBytes(value);
|
|
527
|
+
const len = value?.length;
|
|
528
|
+
const needsLen = length !== void 0;
|
|
529
|
+
if (!bytes || needsLen && len !== length) {
|
|
530
|
+
const prefix = title && `"${title}" `;
|
|
531
|
+
const ofLen = needsLen ? ` of length ${length}` : "";
|
|
532
|
+
const got = bytes ? `length=${len}` : `type=${typeof value}`;
|
|
533
|
+
throw new Error(prefix + "expected Uint8Array" + ofLen + ", got " + got);
|
|
534
|
+
}
|
|
535
|
+
return value;
|
|
536
|
+
}
|
|
537
|
+
function aexists(instance, checkFinished = true) {
|
|
538
|
+
if (instance.destroyed)
|
|
539
|
+
throw new Error("Hash instance has been destroyed");
|
|
540
|
+
if (checkFinished && instance.finished)
|
|
541
|
+
throw new Error("Hash#digest() has already been called");
|
|
542
|
+
}
|
|
543
|
+
function aoutput(out, instance) {
|
|
544
|
+
abytes(out, void 0, "digestInto() output");
|
|
545
|
+
const min = instance.outputLen;
|
|
546
|
+
if (out.length < min) {
|
|
547
|
+
throw new Error('"digestInto() output" expected to be of length >=' + min);
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
function u32(arr) {
|
|
551
|
+
return new Uint32Array(arr.buffer, arr.byteOffset, Math.floor(arr.byteLength / 4));
|
|
552
|
+
}
|
|
553
|
+
function clean(...arrays) {
|
|
554
|
+
for (let i = 0; i < arrays.length; i++) {
|
|
555
|
+
arrays[i].fill(0);
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
var isLE = /* @__PURE__ */ (() => new Uint8Array(new Uint32Array([287454020]).buffer)[0] === 68)();
|
|
559
|
+
function byteSwap(word) {
|
|
560
|
+
return word << 24 & 4278190080 | word << 8 & 16711680 | word >>> 8 & 65280 | word >>> 24 & 255;
|
|
561
|
+
}
|
|
562
|
+
function byteSwap32(arr) {
|
|
563
|
+
for (let i = 0; i < arr.length; i++) {
|
|
564
|
+
arr[i] = byteSwap(arr[i]);
|
|
565
|
+
}
|
|
566
|
+
return arr;
|
|
567
|
+
}
|
|
568
|
+
var swap32IfBE = isLE ? (u) => u : byteSwap32;
|
|
569
|
+
function createHasher(hashCons, info = {}) {
|
|
570
|
+
const hashC = (msg, opts) => hashCons(opts).update(msg).digest();
|
|
571
|
+
const tmp = hashCons(void 0);
|
|
572
|
+
hashC.outputLen = tmp.outputLen;
|
|
573
|
+
hashC.blockLen = tmp.blockLen;
|
|
574
|
+
hashC.create = (opts) => hashCons(opts);
|
|
575
|
+
Object.assign(hashC, info);
|
|
576
|
+
return Object.freeze(hashC);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
// node_modules/@noble/hashes/sha3.js
|
|
580
|
+
var _0n = BigInt(0);
|
|
581
|
+
var _1n = BigInt(1);
|
|
582
|
+
var _2n = BigInt(2);
|
|
583
|
+
var _7n = BigInt(7);
|
|
584
|
+
var _256n = BigInt(256);
|
|
585
|
+
var _0x71n = BigInt(113);
|
|
586
|
+
var SHA3_PI = [];
|
|
587
|
+
var SHA3_ROTL = [];
|
|
588
|
+
var _SHA3_IOTA = [];
|
|
589
|
+
for (let round = 0, R = _1n, x = 1, y = 0; round < 24; round++) {
|
|
590
|
+
[x, y] = [y, (2 * x + 3 * y) % 5];
|
|
591
|
+
SHA3_PI.push(2 * (5 * y + x));
|
|
592
|
+
SHA3_ROTL.push((round + 1) * (round + 2) / 2 % 64);
|
|
593
|
+
let t = _0n;
|
|
594
|
+
for (let j = 0; j < 7; j++) {
|
|
595
|
+
R = (R << _1n ^ (R >> _7n) * _0x71n) % _256n;
|
|
596
|
+
if (R & _2n)
|
|
597
|
+
t ^= _1n << (_1n << BigInt(j)) - _1n;
|
|
598
|
+
}
|
|
599
|
+
_SHA3_IOTA.push(t);
|
|
600
|
+
}
|
|
601
|
+
var IOTAS = split(_SHA3_IOTA, true);
|
|
602
|
+
var SHA3_IOTA_H = IOTAS[0];
|
|
603
|
+
var SHA3_IOTA_L = IOTAS[1];
|
|
604
|
+
var rotlH = (h, l, s) => s > 32 ? rotlBH(h, l, s) : rotlSH(h, l, s);
|
|
605
|
+
var rotlL = (h, l, s) => s > 32 ? rotlBL(h, l, s) : rotlSL(h, l, s);
|
|
606
|
+
function keccakP(s, rounds = 24) {
|
|
607
|
+
const B = new Uint32Array(5 * 2);
|
|
608
|
+
for (let round = 24 - rounds; round < 24; round++) {
|
|
609
|
+
for (let x = 0; x < 10; x++)
|
|
610
|
+
B[x] = s[x] ^ s[x + 10] ^ s[x + 20] ^ s[x + 30] ^ s[x + 40];
|
|
611
|
+
for (let x = 0; x < 10; x += 2) {
|
|
612
|
+
const idx1 = (x + 8) % 10;
|
|
613
|
+
const idx0 = (x + 2) % 10;
|
|
614
|
+
const B0 = B[idx0];
|
|
615
|
+
const B1 = B[idx0 + 1];
|
|
616
|
+
const Th = rotlH(B0, B1, 1) ^ B[idx1];
|
|
617
|
+
const Tl = rotlL(B0, B1, 1) ^ B[idx1 + 1];
|
|
618
|
+
for (let y = 0; y < 50; y += 10) {
|
|
619
|
+
s[x + y] ^= Th;
|
|
620
|
+
s[x + y + 1] ^= Tl;
|
|
621
|
+
}
|
|
622
|
+
}
|
|
623
|
+
let curH = s[2];
|
|
624
|
+
let curL = s[3];
|
|
625
|
+
for (let t = 0; t < 24; t++) {
|
|
626
|
+
const shift = SHA3_ROTL[t];
|
|
627
|
+
const Th = rotlH(curH, curL, shift);
|
|
628
|
+
const Tl = rotlL(curH, curL, shift);
|
|
629
|
+
const PI = SHA3_PI[t];
|
|
630
|
+
curH = s[PI];
|
|
631
|
+
curL = s[PI + 1];
|
|
632
|
+
s[PI] = Th;
|
|
633
|
+
s[PI + 1] = Tl;
|
|
634
|
+
}
|
|
635
|
+
for (let y = 0; y < 50; y += 10) {
|
|
636
|
+
for (let x = 0; x < 10; x++)
|
|
637
|
+
B[x] = s[y + x];
|
|
638
|
+
for (let x = 0; x < 10; x++)
|
|
639
|
+
s[y + x] ^= ~B[(x + 2) % 10] & B[(x + 4) % 10];
|
|
640
|
+
}
|
|
641
|
+
s[0] ^= SHA3_IOTA_H[round];
|
|
642
|
+
s[1] ^= SHA3_IOTA_L[round];
|
|
643
|
+
}
|
|
644
|
+
clean(B);
|
|
645
|
+
}
|
|
646
|
+
var Keccak = class _Keccak {
|
|
647
|
+
state;
|
|
648
|
+
pos = 0;
|
|
649
|
+
posOut = 0;
|
|
650
|
+
finished = false;
|
|
651
|
+
state32;
|
|
652
|
+
destroyed = false;
|
|
653
|
+
blockLen;
|
|
654
|
+
suffix;
|
|
655
|
+
outputLen;
|
|
656
|
+
enableXOF = false;
|
|
657
|
+
rounds;
|
|
658
|
+
// NOTE: we accept arguments in bytes instead of bits here.
|
|
659
|
+
constructor(blockLen, suffix, outputLen, enableXOF = false, rounds = 24) {
|
|
660
|
+
this.blockLen = blockLen;
|
|
661
|
+
this.suffix = suffix;
|
|
662
|
+
this.outputLen = outputLen;
|
|
663
|
+
this.enableXOF = enableXOF;
|
|
664
|
+
this.rounds = rounds;
|
|
665
|
+
anumber(outputLen, "outputLen");
|
|
666
|
+
if (!(0 < blockLen && blockLen < 200))
|
|
667
|
+
throw new Error("only keccak-f1600 function is supported");
|
|
668
|
+
this.state = new Uint8Array(200);
|
|
669
|
+
this.state32 = u32(this.state);
|
|
670
|
+
}
|
|
671
|
+
clone() {
|
|
672
|
+
return this._cloneInto();
|
|
673
|
+
}
|
|
674
|
+
keccak() {
|
|
675
|
+
swap32IfBE(this.state32);
|
|
676
|
+
keccakP(this.state32, this.rounds);
|
|
677
|
+
swap32IfBE(this.state32);
|
|
678
|
+
this.posOut = 0;
|
|
679
|
+
this.pos = 0;
|
|
680
|
+
}
|
|
681
|
+
update(data) {
|
|
682
|
+
aexists(this);
|
|
683
|
+
abytes(data);
|
|
684
|
+
const { blockLen, state } = this;
|
|
685
|
+
const len = data.length;
|
|
686
|
+
for (let pos = 0; pos < len; ) {
|
|
687
|
+
const take = Math.min(blockLen - this.pos, len - pos);
|
|
688
|
+
for (let i = 0; i < take; i++)
|
|
689
|
+
state[this.pos++] ^= data[pos++];
|
|
690
|
+
if (this.pos === blockLen)
|
|
691
|
+
this.keccak();
|
|
692
|
+
}
|
|
693
|
+
return this;
|
|
694
|
+
}
|
|
695
|
+
finish() {
|
|
696
|
+
if (this.finished)
|
|
697
|
+
return;
|
|
698
|
+
this.finished = true;
|
|
699
|
+
const { state, suffix, pos, blockLen } = this;
|
|
700
|
+
state[pos] ^= suffix;
|
|
701
|
+
if ((suffix & 128) !== 0 && pos === blockLen - 1)
|
|
702
|
+
this.keccak();
|
|
703
|
+
state[blockLen - 1] ^= 128;
|
|
704
|
+
this.keccak();
|
|
705
|
+
}
|
|
706
|
+
writeInto(out) {
|
|
707
|
+
aexists(this, false);
|
|
708
|
+
abytes(out);
|
|
709
|
+
this.finish();
|
|
710
|
+
const bufferOut = this.state;
|
|
711
|
+
const { blockLen } = this;
|
|
712
|
+
for (let pos = 0, len = out.length; pos < len; ) {
|
|
713
|
+
if (this.posOut >= blockLen)
|
|
714
|
+
this.keccak();
|
|
715
|
+
const take = Math.min(blockLen - this.posOut, len - pos);
|
|
716
|
+
out.set(bufferOut.subarray(this.posOut, this.posOut + take), pos);
|
|
717
|
+
this.posOut += take;
|
|
718
|
+
pos += take;
|
|
719
|
+
}
|
|
720
|
+
return out;
|
|
721
|
+
}
|
|
722
|
+
xofInto(out) {
|
|
723
|
+
if (!this.enableXOF)
|
|
724
|
+
throw new Error("XOF is not possible for this instance");
|
|
725
|
+
return this.writeInto(out);
|
|
726
|
+
}
|
|
727
|
+
xof(bytes) {
|
|
728
|
+
anumber(bytes);
|
|
729
|
+
return this.xofInto(new Uint8Array(bytes));
|
|
730
|
+
}
|
|
731
|
+
digestInto(out) {
|
|
732
|
+
aoutput(out, this);
|
|
733
|
+
if (this.finished)
|
|
734
|
+
throw new Error("digest() was already called");
|
|
735
|
+
this.writeInto(out);
|
|
736
|
+
this.destroy();
|
|
737
|
+
return out;
|
|
738
|
+
}
|
|
739
|
+
digest() {
|
|
740
|
+
return this.digestInto(new Uint8Array(this.outputLen));
|
|
741
|
+
}
|
|
742
|
+
destroy() {
|
|
743
|
+
this.destroyed = true;
|
|
744
|
+
clean(this.state);
|
|
745
|
+
}
|
|
746
|
+
_cloneInto(to) {
|
|
747
|
+
const { blockLen, suffix, outputLen, rounds, enableXOF } = this;
|
|
748
|
+
to ||= new _Keccak(blockLen, suffix, outputLen, enableXOF, rounds);
|
|
749
|
+
to.state32.set(this.state32);
|
|
750
|
+
to.pos = this.pos;
|
|
751
|
+
to.posOut = this.posOut;
|
|
752
|
+
to.finished = this.finished;
|
|
753
|
+
to.rounds = rounds;
|
|
754
|
+
to.suffix = suffix;
|
|
755
|
+
to.outputLen = outputLen;
|
|
756
|
+
to.enableXOF = enableXOF;
|
|
757
|
+
to.destroyed = this.destroyed;
|
|
758
|
+
return to;
|
|
759
|
+
}
|
|
760
|
+
};
|
|
761
|
+
var genKeccak = (suffix, blockLen, outputLen, info = {}) => createHasher(() => new Keccak(blockLen, suffix, outputLen), info);
|
|
762
|
+
var keccak_256 = /* @__PURE__ */ genKeccak(1, 136, 32);
|
|
763
|
+
|
|
764
|
+
// src/wallet.ts
|
|
765
|
+
var BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
766
|
+
function base58Encode(bytes) {
|
|
767
|
+
let leadingZeros = 0;
|
|
768
|
+
for (const byte of bytes) {
|
|
769
|
+
if (byte === 0) leadingZeros++;
|
|
770
|
+
else break;
|
|
771
|
+
}
|
|
772
|
+
let num = 0n;
|
|
773
|
+
for (const byte of bytes) {
|
|
774
|
+
num = num * 256n + BigInt(byte);
|
|
775
|
+
}
|
|
776
|
+
let encoded = "";
|
|
777
|
+
while (num > 0n) {
|
|
778
|
+
encoded = BASE58_ALPHABET[Number(num % 58n)] + encoded;
|
|
779
|
+
num = num / 58n;
|
|
780
|
+
}
|
|
781
|
+
return BASE58_ALPHABET[0].repeat(leadingZeros) + encoded;
|
|
782
|
+
}
|
|
783
|
+
function deriveSecp256k1Seed(ed25519Seed) {
|
|
784
|
+
const hmac = createHmac("sha512", "secp256k1");
|
|
785
|
+
hmac.update(ed25519Seed);
|
|
786
|
+
return hmac.digest().subarray(0, 32);
|
|
787
|
+
}
|
|
788
|
+
function getSecp256k1PublicKey(privateKey) {
|
|
789
|
+
const ecdh = createECDH("secp256k1");
|
|
790
|
+
ecdh.setPrivateKey(privateKey);
|
|
791
|
+
return Buffer.from(ecdh.getPublicKey());
|
|
792
|
+
}
|
|
793
|
+
function evmAddressFromPublicKey(uncompressedPubKey) {
|
|
794
|
+
const pubKeyBody = uncompressedPubKey.subarray(1);
|
|
795
|
+
const hash = keccak_256(pubKeyBody);
|
|
796
|
+
const addressBytes = hash.slice(hash.length - 20);
|
|
797
|
+
return "0x" + Buffer.from(addressBytes).toString("hex");
|
|
798
|
+
}
|
|
799
|
+
function checksumAddress(address) {
|
|
800
|
+
const addr = address.slice(2).toLowerCase();
|
|
801
|
+
const hash = Buffer.from(keccak_256(Buffer.from(addr, "utf-8"))).toString(
|
|
802
|
+
"hex"
|
|
803
|
+
);
|
|
804
|
+
let checksummed = "0x";
|
|
805
|
+
for (let i = 0; i < addr.length; i++) {
|
|
806
|
+
checksummed += parseInt(hash[i], 16) >= 8 ? addr[i].toUpperCase() : addr[i];
|
|
807
|
+
}
|
|
808
|
+
return checksummed;
|
|
809
|
+
}
|
|
810
|
+
async function getSolanaAddress(nitDir) {
|
|
811
|
+
const pubBase64 = await loadPublicKey(nitDir);
|
|
812
|
+
const pubBytes = Buffer.from(pubBase64, "base64");
|
|
813
|
+
return base58Encode(pubBytes);
|
|
814
|
+
}
|
|
815
|
+
async function getEvmAddress(nitDir) {
|
|
816
|
+
const keyPath = join5(nitDir, "identity", "agent.key");
|
|
817
|
+
const privBase64 = (await fs5.readFile(keyPath, "utf-8")).trim();
|
|
818
|
+
const ed25519Seed = Buffer.from(privBase64, "base64");
|
|
819
|
+
const secp256k1PrivKey = deriveSecp256k1Seed(ed25519Seed);
|
|
820
|
+
const secp256k1PubKey = getSecp256k1PublicKey(secp256k1PrivKey);
|
|
821
|
+
const rawAddress = evmAddressFromPublicKey(secp256k1PubKey);
|
|
822
|
+
return checksumAddress(rawAddress);
|
|
823
|
+
}
|
|
824
|
+
async function getWalletAddresses(nitDir) {
|
|
825
|
+
const [solana, ethereum] = await Promise.all([
|
|
826
|
+
getSolanaAddress(nitDir),
|
|
827
|
+
getEvmAddress(nitDir)
|
|
828
|
+
]);
|
|
829
|
+
return { solana, ethereum };
|
|
830
|
+
}
|
|
831
|
+
async function loadSecp256k1RawKeyPair(nitDir) {
|
|
832
|
+
const keyPath = join5(nitDir, "identity", "agent.key");
|
|
833
|
+
const privBase64 = (await fs5.readFile(keyPath, "utf-8")).trim();
|
|
834
|
+
const ed25519Seed = Buffer.from(privBase64, "base64");
|
|
835
|
+
const secp256k1PrivKey = deriveSecp256k1Seed(ed25519Seed);
|
|
836
|
+
const secp256k1PubKey = getSecp256k1PublicKey(secp256k1PrivKey);
|
|
837
|
+
const keypair = new Uint8Array(64);
|
|
838
|
+
keypair.set(secp256k1PrivKey, 0);
|
|
839
|
+
keypair.set(secp256k1PubKey.subarray(1, 33), 32);
|
|
840
|
+
return keypair;
|
|
841
|
+
}
|
|
842
|
+
|
|
469
843
|
// src/diff.ts
|
|
470
844
|
function diffCards(oldCard, newCard) {
|
|
471
845
|
const fields = [];
|
|
@@ -652,22 +1026,22 @@ async function fetchBranchCard(cardUrl, branch2, nitDir) {
|
|
|
652
1026
|
}
|
|
653
1027
|
|
|
654
1028
|
// src/config.ts
|
|
655
|
-
import { promises as
|
|
656
|
-
import { join as
|
|
1029
|
+
import { promises as fs6 } from "fs";
|
|
1030
|
+
import { join as join6 } from "path";
|
|
657
1031
|
var CONFIG_FILE = "config";
|
|
658
1032
|
async function readConfig(nitDir) {
|
|
659
|
-
const configPath =
|
|
1033
|
+
const configPath = join6(nitDir, CONFIG_FILE);
|
|
660
1034
|
let raw;
|
|
661
1035
|
try {
|
|
662
|
-
raw = await
|
|
1036
|
+
raw = await fs6.readFile(configPath, "utf-8");
|
|
663
1037
|
} catch {
|
|
664
1038
|
return { remotes: {} };
|
|
665
1039
|
}
|
|
666
1040
|
return parseConfig(raw);
|
|
667
1041
|
}
|
|
668
1042
|
async function writeConfig(nitDir, config) {
|
|
669
|
-
const configPath =
|
|
670
|
-
await
|
|
1043
|
+
const configPath = join6(nitDir, CONFIG_FILE);
|
|
1044
|
+
await fs6.writeFile(configPath, serializeConfig(config), "utf-8");
|
|
671
1045
|
}
|
|
672
1046
|
async function getRemoteUrl(nitDir, remoteName) {
|
|
673
1047
|
const config = await readConfig(nitDir);
|
|
@@ -744,7 +1118,7 @@ var DEFAULT_API_BASE = "https://api.newtype-ai.org";
|
|
|
744
1118
|
function findNitDir(startDir) {
|
|
745
1119
|
let dir = resolve2(startDir || process.cwd());
|
|
746
1120
|
while (true) {
|
|
747
|
-
const candidate =
|
|
1121
|
+
const candidate = join7(dir, NIT_DIR);
|
|
748
1122
|
try {
|
|
749
1123
|
const s = statSync(candidate);
|
|
750
1124
|
if (s.isDirectory()) return candidate;
|
|
@@ -763,17 +1137,17 @@ function projectDir(nitDir) {
|
|
|
763
1137
|
return resolve2(nitDir, "..");
|
|
764
1138
|
}
|
|
765
1139
|
async function readWorkingCard(nitDir) {
|
|
766
|
-
const cardPath =
|
|
1140
|
+
const cardPath = join7(projectDir(nitDir), CARD_FILE);
|
|
767
1141
|
try {
|
|
768
|
-
const raw = await
|
|
1142
|
+
const raw = await fs7.readFile(cardPath, "utf-8");
|
|
769
1143
|
return JSON.parse(raw);
|
|
770
1144
|
} catch {
|
|
771
1145
|
throw new Error(`Cannot read ${CARD_FILE}. Does it exist?`);
|
|
772
1146
|
}
|
|
773
1147
|
}
|
|
774
1148
|
async function writeWorkingCard(nitDir, card) {
|
|
775
|
-
const cardPath =
|
|
776
|
-
await
|
|
1149
|
+
const cardPath = join7(projectDir(nitDir), CARD_FILE);
|
|
1150
|
+
await fs7.writeFile(cardPath, JSON.stringify(card, null, 2) + "\n", "utf-8");
|
|
777
1151
|
}
|
|
778
1152
|
async function getCardAtCommit(nitDir, commitHash) {
|
|
779
1153
|
const commitRaw = await readObject(nitDir, commitHash);
|
|
@@ -791,27 +1165,27 @@ async function getAuthorName(nitDir) {
|
|
|
791
1165
|
}
|
|
792
1166
|
async function init(options) {
|
|
793
1167
|
const projDir = resolve2(options?.projectDir || process.cwd());
|
|
794
|
-
const nitDir =
|
|
1168
|
+
const nitDir = join7(projDir, NIT_DIR);
|
|
795
1169
|
try {
|
|
796
|
-
await
|
|
1170
|
+
await fs7.access(nitDir);
|
|
797
1171
|
throw new Error("Already initialized. .nit/ directory exists.");
|
|
798
1172
|
} catch (err) {
|
|
799
1173
|
if (err instanceof Error && err.message.startsWith("Already")) throw err;
|
|
800
1174
|
}
|
|
801
|
-
await
|
|
802
|
-
await
|
|
803
|
-
await
|
|
804
|
-
await
|
|
805
|
-
await
|
|
1175
|
+
await fs7.mkdir(join7(nitDir, "objects"), { recursive: true });
|
|
1176
|
+
await fs7.mkdir(join7(nitDir, "refs", "heads"), { recursive: true });
|
|
1177
|
+
await fs7.mkdir(join7(nitDir, "refs", "remote"), { recursive: true });
|
|
1178
|
+
await fs7.mkdir(join7(nitDir, "identity"), { recursive: true });
|
|
1179
|
+
await fs7.mkdir(join7(nitDir, "logs"), { recursive: true });
|
|
806
1180
|
const { publicKey: pubBase64 } = await generateKeypair(nitDir);
|
|
807
1181
|
const publicKeyField = formatPublicKeyField(pubBase64);
|
|
808
1182
|
const agentId = deriveAgentId(publicKeyField);
|
|
809
1183
|
await saveAgentId(nitDir, agentId);
|
|
810
|
-
const cardPath =
|
|
1184
|
+
const cardPath = join7(projDir, CARD_FILE);
|
|
811
1185
|
let card;
|
|
812
1186
|
let skillsFound = [];
|
|
813
1187
|
try {
|
|
814
|
-
const raw = await
|
|
1188
|
+
const raw = await fs7.readFile(cardPath, "utf-8");
|
|
815
1189
|
card = JSON.parse(raw);
|
|
816
1190
|
card.publicKey = publicKeyField;
|
|
817
1191
|
skillsFound = card.skills.map((s) => s.id);
|
|
@@ -850,16 +1224,18 @@ async function init(options) {
|
|
|
850
1224
|
const commitHash = await writeObject(nitDir, "commit", commitContent);
|
|
851
1225
|
await setBranch(nitDir, "main", commitHash);
|
|
852
1226
|
await setHead(nitDir, "main");
|
|
853
|
-
await
|
|
1227
|
+
await fs7.writeFile(join7(nitDir, "logs", "HEAD"), "", "utf-8");
|
|
854
1228
|
const skillsDir = await discoverSkillsDir(projDir);
|
|
855
1229
|
await writeConfig(nitDir, {
|
|
856
1230
|
remotes: { origin: { url: DEFAULT_API_BASE } },
|
|
857
1231
|
skillsDir
|
|
858
1232
|
});
|
|
1233
|
+
const walletAddresses = await getWalletAddresses(nitDir);
|
|
859
1234
|
return {
|
|
860
1235
|
agentId,
|
|
861
1236
|
publicKey: publicKeyField,
|
|
862
1237
|
cardUrl: card.url,
|
|
1238
|
+
walletAddresses,
|
|
863
1239
|
skillsFound,
|
|
864
1240
|
skillsDir
|
|
865
1241
|
};
|
|
@@ -915,11 +1291,13 @@ async function status(options) {
|
|
|
915
1291
|
}
|
|
916
1292
|
branchStatus.push({ name: b.name, ahead, behind: 0 });
|
|
917
1293
|
}
|
|
1294
|
+
const walletAddresses = await getWalletAddresses(nitDir);
|
|
918
1295
|
return {
|
|
919
1296
|
agentId,
|
|
920
1297
|
cardUrl,
|
|
921
1298
|
branch: currentBranch,
|
|
922
1299
|
publicKey,
|
|
1300
|
+
walletAddresses,
|
|
923
1301
|
uncommittedChanges,
|
|
924
1302
|
branches: branchStatus
|
|
925
1303
|
};
|
|
@@ -1137,6 +1515,7 @@ async function remoteSetUrl(name, url, options) {
|
|
|
1137
1515
|
}
|
|
1138
1516
|
|
|
1139
1517
|
export {
|
|
1518
|
+
loadRawKeyPair,
|
|
1140
1519
|
formatPublicKeyField,
|
|
1141
1520
|
parsePublicKeyField,
|
|
1142
1521
|
signChallenge,
|
|
@@ -1144,6 +1523,11 @@ export {
|
|
|
1144
1523
|
NIT_NAMESPACE,
|
|
1145
1524
|
deriveAgentId,
|
|
1146
1525
|
loadAgentId,
|
|
1526
|
+
base58Encode,
|
|
1527
|
+
getSolanaAddress,
|
|
1528
|
+
getEvmAddress,
|
|
1529
|
+
getWalletAddresses,
|
|
1530
|
+
loadSecp256k1RawKeyPair,
|
|
1147
1531
|
diffCards,
|
|
1148
1532
|
formatDiff,
|
|
1149
1533
|
fetchBranchCard,
|
|
@@ -1162,3 +1546,8 @@ export {
|
|
|
1162
1546
|
remoteAdd,
|
|
1163
1547
|
remoteSetUrl
|
|
1164
1548
|
};
|
|
1549
|
+
/*! Bundled license information:
|
|
1550
|
+
|
|
1551
|
+
@noble/hashes/utils.js:
|
|
1552
|
+
(*! noble-hashes - MIT License (c) 2022 Paul Miller (paulmillr.com) *)
|
|
1553
|
+
*/
|
package/dist/cli.js
CHANGED
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
remoteSetUrl,
|
|
16
16
|
sign,
|
|
17
17
|
status
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-BC7OEIFR.js";
|
|
19
19
|
|
|
20
20
|
// src/cli.ts
|
|
21
21
|
var bold = (s) => `\x1B[1m${s}\x1B[0m`;
|
|
@@ -80,6 +80,11 @@ async function cmdInit() {
|
|
|
80
80
|
console.log(` Agent ID: ${green(result.agentId)}`);
|
|
81
81
|
console.log(` Public key: ${dim(result.publicKey)}`);
|
|
82
82
|
console.log(` Card URL: ${result.cardUrl}`);
|
|
83
|
+
console.log();
|
|
84
|
+
console.log(` ${bold("Wallet addresses:")}`);
|
|
85
|
+
console.log(` Solana: ${result.walletAddresses.solana}`);
|
|
86
|
+
console.log(` Ethereum: ${result.walletAddresses.ethereum}`);
|
|
87
|
+
console.log();
|
|
83
88
|
console.log(` Skills dir: ${dim(result.skillsDir)}`);
|
|
84
89
|
if (result.skillsFound.length > 0) {
|
|
85
90
|
console.log(` Skills: ${result.skillsFound.join(", ")}`);
|
|
@@ -88,6 +93,7 @@ async function cmdInit() {
|
|
|
88
93
|
}
|
|
89
94
|
console.log();
|
|
90
95
|
console.log(dim("Created .nit/ with initial commit on main."));
|
|
96
|
+
console.log(dim("Fund the wallet addresses above to use blockchain features in apps."));
|
|
91
97
|
console.log();
|
|
92
98
|
console.log(`Next: open ${bold("agent-card.json")} and set your name, description, and skills.`);
|
|
93
99
|
}
|
|
@@ -99,6 +105,10 @@ async function cmdStatus() {
|
|
|
99
105
|
console.log(` Public key: ${dim(s.publicKey)}`);
|
|
100
106
|
console.log(` Card URL: ${s.cardUrl}`);
|
|
101
107
|
console.log();
|
|
108
|
+
console.log(` ${bold("Wallet addresses:")}`);
|
|
109
|
+
console.log(` Solana: ${s.walletAddresses.solana}`);
|
|
110
|
+
console.log(` Ethereum: ${s.walletAddresses.ethereum}`);
|
|
111
|
+
console.log();
|
|
102
112
|
if (s.uncommittedChanges) {
|
|
103
113
|
console.log(yellow("Uncommitted changes:"));
|
|
104
114
|
console.log(formatDiff(s.uncommittedChanges));
|
package/dist/index.d.ts
CHANGED
|
@@ -105,12 +105,18 @@ interface PushResult {
|
|
|
105
105
|
success: boolean;
|
|
106
106
|
error?: string;
|
|
107
107
|
}
|
|
108
|
+
/** Wallet addresses derived from the agent's Ed25519 keypair. */
|
|
109
|
+
interface WalletAddresses$1 {
|
|
110
|
+
solana: string;
|
|
111
|
+
ethereum: string;
|
|
112
|
+
}
|
|
108
113
|
/** Result returned by the status command. */
|
|
109
114
|
interface StatusResult {
|
|
110
115
|
agentId: string;
|
|
111
116
|
cardUrl: string;
|
|
112
117
|
branch: string;
|
|
113
118
|
publicKey: string;
|
|
119
|
+
walletAddresses: WalletAddresses$1;
|
|
114
120
|
uncommittedChanges: DiffResult | null;
|
|
115
121
|
branches: Array<{
|
|
116
122
|
name: string;
|
|
@@ -135,6 +141,12 @@ declare function diffCards(oldCard: AgentCard, newCard: AgentCard): DiffResult;
|
|
|
135
141
|
*/
|
|
136
142
|
declare function formatDiff(diff: DiffResult): string;
|
|
137
143
|
|
|
144
|
+
/**
|
|
145
|
+
* Load the Ed25519 keypair as raw bytes (64-byte Uint8Array).
|
|
146
|
+
* Format: [32-byte seed || 32-byte public key]
|
|
147
|
+
* Compatible with Solana keypair format and other Ed25519 libraries.
|
|
148
|
+
*/
|
|
149
|
+
declare function loadRawKeyPair(nitDir: string): Promise<Uint8Array>;
|
|
138
150
|
/**
|
|
139
151
|
* Format a raw base64 public key as the value for the agent card's
|
|
140
152
|
* `publicKey` field: "ed25519:<base64>".
|
|
@@ -188,6 +200,32 @@ declare function loadAgentId(nitDir: string): Promise<string>;
|
|
|
188
200
|
*/
|
|
189
201
|
declare function fetchBranchCard(cardUrl: string, branch: string, nitDir?: string): Promise<AgentCard>;
|
|
190
202
|
|
|
203
|
+
declare function base58Encode(bytes: Uint8Array): string;
|
|
204
|
+
interface WalletAddresses {
|
|
205
|
+
solana: string;
|
|
206
|
+
ethereum: string;
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Get the Solana wallet address (base58-encoded Ed25519 public key).
|
|
210
|
+
*/
|
|
211
|
+
declare function getSolanaAddress(nitDir: string): Promise<string>;
|
|
212
|
+
/**
|
|
213
|
+
* Get the EVM wallet address (Ethereum, BSC, Polygon, etc.).
|
|
214
|
+
* Derives a secp256k1 key from the Ed25519 seed, then computes keccak256.
|
|
215
|
+
* Returns EIP-55 checksummed address.
|
|
216
|
+
*/
|
|
217
|
+
declare function getEvmAddress(nitDir: string): Promise<string>;
|
|
218
|
+
/**
|
|
219
|
+
* Get wallet addresses for all supported chains.
|
|
220
|
+
*/
|
|
221
|
+
declare function getWalletAddresses(nitDir: string): Promise<WalletAddresses>;
|
|
222
|
+
/**
|
|
223
|
+
* Load the secp256k1 keypair as raw bytes (64-byte Uint8Array).
|
|
224
|
+
* Format: [32-byte private key || 32-byte compressed public key (X only)]
|
|
225
|
+
* For EVM transaction signing.
|
|
226
|
+
*/
|
|
227
|
+
declare function loadSecp256k1RawKeyPair(nitDir: string): Promise<Uint8Array>;
|
|
228
|
+
|
|
191
229
|
/**
|
|
192
230
|
* Walk up from startDir looking for a .nit/ directory.
|
|
193
231
|
* Returns the path to .nit/ or throws if not found.
|
|
@@ -197,6 +235,7 @@ interface InitResult {
|
|
|
197
235
|
agentId: string;
|
|
198
236
|
publicKey: string;
|
|
199
237
|
cardUrl: string;
|
|
238
|
+
walletAddresses: WalletAddresses$1;
|
|
200
239
|
skillsFound: string[];
|
|
201
240
|
skillsDir: string;
|
|
202
241
|
}
|
|
@@ -305,4 +344,4 @@ declare function remoteSetUrl(name: string, url: string, options?: {
|
|
|
305
344
|
projectDir?: string;
|
|
306
345
|
}): Promise<void>;
|
|
307
346
|
|
|
308
|
-
export { type AgentCard, type AgentCardSkill, type DiffResult, type FieldDiff, type InitResult, type LoginPayload, NIT_NAMESPACE, type NitBranch, type NitCommit, type NitConfig, type NitHead, type NitRemoteConfig, type PushResult, type RemoteInfo, type SkillMetadata, type StatusResult, branch, checkout, commit, deriveAgentId, diff, diffCards, fetchBranchCard, findNitDir, formatDiff, formatPublicKeyField, init, loadAgentId, log, loginPayload, parsePublicKeyField, push, remote, remoteAdd, remoteSetUrl, sign, signChallenge, signMessage, status };
|
|
347
|
+
export { type AgentCard, type AgentCardSkill, type DiffResult, type FieldDiff, type InitResult, type LoginPayload, NIT_NAMESPACE, type NitBranch, type NitCommit, type NitConfig, type NitHead, type NitRemoteConfig, type PushResult, type RemoteInfo, type SkillMetadata, type StatusResult, type WalletAddresses$1 as WalletAddresses, base58Encode, branch, checkout, commit, deriveAgentId, diff, diffCards, fetchBranchCard, findNitDir, formatDiff, formatPublicKeyField, getEvmAddress, getSolanaAddress, getWalletAddresses, init, loadAgentId, loadRawKeyPair, loadSecp256k1RawKeyPair, log, loginPayload, parsePublicKeyField, push, remote, remoteAdd, remoteSetUrl, sign, signChallenge, signMessage, status };
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// nit — version control for agent cards
|
|
2
2
|
import {
|
|
3
3
|
NIT_NAMESPACE,
|
|
4
|
+
base58Encode,
|
|
4
5
|
branch,
|
|
5
6
|
checkout,
|
|
6
7
|
commit,
|
|
@@ -11,8 +12,13 @@ import {
|
|
|
11
12
|
findNitDir,
|
|
12
13
|
formatDiff,
|
|
13
14
|
formatPublicKeyField,
|
|
15
|
+
getEvmAddress,
|
|
16
|
+
getSolanaAddress,
|
|
17
|
+
getWalletAddresses,
|
|
14
18
|
init,
|
|
15
19
|
loadAgentId,
|
|
20
|
+
loadRawKeyPair,
|
|
21
|
+
loadSecp256k1RawKeyPair,
|
|
16
22
|
log,
|
|
17
23
|
loginPayload,
|
|
18
24
|
parsePublicKeyField,
|
|
@@ -24,9 +30,10 @@ import {
|
|
|
24
30
|
signChallenge,
|
|
25
31
|
signMessage,
|
|
26
32
|
status
|
|
27
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-BC7OEIFR.js";
|
|
28
34
|
export {
|
|
29
35
|
NIT_NAMESPACE,
|
|
36
|
+
base58Encode,
|
|
30
37
|
branch,
|
|
31
38
|
checkout,
|
|
32
39
|
commit,
|
|
@@ -37,8 +44,13 @@ export {
|
|
|
37
44
|
findNitDir,
|
|
38
45
|
formatDiff,
|
|
39
46
|
formatPublicKeyField,
|
|
47
|
+
getEvmAddress,
|
|
48
|
+
getSolanaAddress,
|
|
49
|
+
getWalletAddresses,
|
|
40
50
|
init,
|
|
41
51
|
loadAgentId,
|
|
52
|
+
loadRawKeyPair,
|
|
53
|
+
loadSecp256k1RawKeyPair,
|
|
42
54
|
log,
|
|
43
55
|
loginPayload,
|
|
44
56
|
parsePublicKeyField,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@newtype-ai/nit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "Version control for agent cards",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -42,6 +42,7 @@
|
|
|
42
42
|
},
|
|
43
43
|
"homepage": "https://github.com/newtype-ai/nit",
|
|
44
44
|
"devDependencies": {
|
|
45
|
+
"@noble/hashes": "^2.0.1",
|
|
45
46
|
"@types/node": "^25.3.0",
|
|
46
47
|
"tsup": "^8.0.0",
|
|
47
48
|
"typescript": "^5.0.0"
|