@newtype-ai/nit 0.3.0 → 0.4.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 +7 -1
- package/dist/{chunk-CJUMX46F.js → chunk-BC7OEIFR.js} +393 -23
- package/dist/cli.js +11 -1
- package/dist/index.d.ts +34 -1
- package/dist/index.js +11 -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, loadRawKeyPair } 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
|
|
|
@@ -147,6 +149,10 @@ await push({ all: true });
|
|
|
147
149
|
// Access raw Ed25519 keypair (64 bytes: [seed || pubkey])
|
|
148
150
|
const keypair = await loadRawKeyPair('/path/to/.nit');
|
|
149
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..." }
|
|
150
156
|
```
|
|
151
157
|
|
|
152
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";
|
|
@@ -484,6 +484,362 @@ function parseFrontmatter(content, dirName, filePath) {
|
|
|
484
484
|
};
|
|
485
485
|
}
|
|
486
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
|
+
|
|
487
843
|
// src/diff.ts
|
|
488
844
|
function diffCards(oldCard, newCard) {
|
|
489
845
|
const fields = [];
|
|
@@ -670,22 +1026,22 @@ async function fetchBranchCard(cardUrl, branch2, nitDir) {
|
|
|
670
1026
|
}
|
|
671
1027
|
|
|
672
1028
|
// src/config.ts
|
|
673
|
-
import { promises as
|
|
674
|
-
import { join as
|
|
1029
|
+
import { promises as fs6 } from "fs";
|
|
1030
|
+
import { join as join6 } from "path";
|
|
675
1031
|
var CONFIG_FILE = "config";
|
|
676
1032
|
async function readConfig(nitDir) {
|
|
677
|
-
const configPath =
|
|
1033
|
+
const configPath = join6(nitDir, CONFIG_FILE);
|
|
678
1034
|
let raw;
|
|
679
1035
|
try {
|
|
680
|
-
raw = await
|
|
1036
|
+
raw = await fs6.readFile(configPath, "utf-8");
|
|
681
1037
|
} catch {
|
|
682
1038
|
return { remotes: {} };
|
|
683
1039
|
}
|
|
684
1040
|
return parseConfig(raw);
|
|
685
1041
|
}
|
|
686
1042
|
async function writeConfig(nitDir, config) {
|
|
687
|
-
const configPath =
|
|
688
|
-
await
|
|
1043
|
+
const configPath = join6(nitDir, CONFIG_FILE);
|
|
1044
|
+
await fs6.writeFile(configPath, serializeConfig(config), "utf-8");
|
|
689
1045
|
}
|
|
690
1046
|
async function getRemoteUrl(nitDir, remoteName) {
|
|
691
1047
|
const config = await readConfig(nitDir);
|
|
@@ -762,7 +1118,7 @@ var DEFAULT_API_BASE = "https://api.newtype-ai.org";
|
|
|
762
1118
|
function findNitDir(startDir) {
|
|
763
1119
|
let dir = resolve2(startDir || process.cwd());
|
|
764
1120
|
while (true) {
|
|
765
|
-
const candidate =
|
|
1121
|
+
const candidate = join7(dir, NIT_DIR);
|
|
766
1122
|
try {
|
|
767
1123
|
const s = statSync(candidate);
|
|
768
1124
|
if (s.isDirectory()) return candidate;
|
|
@@ -781,17 +1137,17 @@ function projectDir(nitDir) {
|
|
|
781
1137
|
return resolve2(nitDir, "..");
|
|
782
1138
|
}
|
|
783
1139
|
async function readWorkingCard(nitDir) {
|
|
784
|
-
const cardPath =
|
|
1140
|
+
const cardPath = join7(projectDir(nitDir), CARD_FILE);
|
|
785
1141
|
try {
|
|
786
|
-
const raw = await
|
|
1142
|
+
const raw = await fs7.readFile(cardPath, "utf-8");
|
|
787
1143
|
return JSON.parse(raw);
|
|
788
1144
|
} catch {
|
|
789
1145
|
throw new Error(`Cannot read ${CARD_FILE}. Does it exist?`);
|
|
790
1146
|
}
|
|
791
1147
|
}
|
|
792
1148
|
async function writeWorkingCard(nitDir, card) {
|
|
793
|
-
const cardPath =
|
|
794
|
-
await
|
|
1149
|
+
const cardPath = join7(projectDir(nitDir), CARD_FILE);
|
|
1150
|
+
await fs7.writeFile(cardPath, JSON.stringify(card, null, 2) + "\n", "utf-8");
|
|
795
1151
|
}
|
|
796
1152
|
async function getCardAtCommit(nitDir, commitHash) {
|
|
797
1153
|
const commitRaw = await readObject(nitDir, commitHash);
|
|
@@ -809,27 +1165,27 @@ async function getAuthorName(nitDir) {
|
|
|
809
1165
|
}
|
|
810
1166
|
async function init(options) {
|
|
811
1167
|
const projDir = resolve2(options?.projectDir || process.cwd());
|
|
812
|
-
const nitDir =
|
|
1168
|
+
const nitDir = join7(projDir, NIT_DIR);
|
|
813
1169
|
try {
|
|
814
|
-
await
|
|
1170
|
+
await fs7.access(nitDir);
|
|
815
1171
|
throw new Error("Already initialized. .nit/ directory exists.");
|
|
816
1172
|
} catch (err) {
|
|
817
1173
|
if (err instanceof Error && err.message.startsWith("Already")) throw err;
|
|
818
1174
|
}
|
|
819
|
-
await
|
|
820
|
-
await
|
|
821
|
-
await
|
|
822
|
-
await
|
|
823
|
-
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 });
|
|
824
1180
|
const { publicKey: pubBase64 } = await generateKeypair(nitDir);
|
|
825
1181
|
const publicKeyField = formatPublicKeyField(pubBase64);
|
|
826
1182
|
const agentId = deriveAgentId(publicKeyField);
|
|
827
1183
|
await saveAgentId(nitDir, agentId);
|
|
828
|
-
const cardPath =
|
|
1184
|
+
const cardPath = join7(projDir, CARD_FILE);
|
|
829
1185
|
let card;
|
|
830
1186
|
let skillsFound = [];
|
|
831
1187
|
try {
|
|
832
|
-
const raw = await
|
|
1188
|
+
const raw = await fs7.readFile(cardPath, "utf-8");
|
|
833
1189
|
card = JSON.parse(raw);
|
|
834
1190
|
card.publicKey = publicKeyField;
|
|
835
1191
|
skillsFound = card.skills.map((s) => s.id);
|
|
@@ -868,16 +1224,18 @@ async function init(options) {
|
|
|
868
1224
|
const commitHash = await writeObject(nitDir, "commit", commitContent);
|
|
869
1225
|
await setBranch(nitDir, "main", commitHash);
|
|
870
1226
|
await setHead(nitDir, "main");
|
|
871
|
-
await
|
|
1227
|
+
await fs7.writeFile(join7(nitDir, "logs", "HEAD"), "", "utf-8");
|
|
872
1228
|
const skillsDir = await discoverSkillsDir(projDir);
|
|
873
1229
|
await writeConfig(nitDir, {
|
|
874
1230
|
remotes: { origin: { url: DEFAULT_API_BASE } },
|
|
875
1231
|
skillsDir
|
|
876
1232
|
});
|
|
1233
|
+
const walletAddresses = await getWalletAddresses(nitDir);
|
|
877
1234
|
return {
|
|
878
1235
|
agentId,
|
|
879
1236
|
publicKey: publicKeyField,
|
|
880
1237
|
cardUrl: card.url,
|
|
1238
|
+
walletAddresses,
|
|
881
1239
|
skillsFound,
|
|
882
1240
|
skillsDir
|
|
883
1241
|
};
|
|
@@ -933,11 +1291,13 @@ async function status(options) {
|
|
|
933
1291
|
}
|
|
934
1292
|
branchStatus.push({ name: b.name, ahead, behind: 0 });
|
|
935
1293
|
}
|
|
1294
|
+
const walletAddresses = await getWalletAddresses(nitDir);
|
|
936
1295
|
return {
|
|
937
1296
|
agentId,
|
|
938
1297
|
cardUrl,
|
|
939
1298
|
branch: currentBranch,
|
|
940
1299
|
publicKey,
|
|
1300
|
+
walletAddresses,
|
|
941
1301
|
uncommittedChanges,
|
|
942
1302
|
branches: branchStatus
|
|
943
1303
|
};
|
|
@@ -1163,6 +1523,11 @@ export {
|
|
|
1163
1523
|
NIT_NAMESPACE,
|
|
1164
1524
|
deriveAgentId,
|
|
1165
1525
|
loadAgentId,
|
|
1526
|
+
base58Encode,
|
|
1527
|
+
getSolanaAddress,
|
|
1528
|
+
getEvmAddress,
|
|
1529
|
+
getWalletAddresses,
|
|
1530
|
+
loadSecp256k1RawKeyPair,
|
|
1166
1531
|
diffCards,
|
|
1167
1532
|
formatDiff,
|
|
1168
1533
|
fetchBranchCard,
|
|
@@ -1181,3 +1546,8 @@ export {
|
|
|
1181
1546
|
remoteAdd,
|
|
1182
1547
|
remoteSetUrl
|
|
1183
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;
|
|
@@ -194,6 +200,32 @@ declare function loadAgentId(nitDir: string): Promise<string>;
|
|
|
194
200
|
*/
|
|
195
201
|
declare function fetchBranchCard(cardUrl: string, branch: string, nitDir?: string): Promise<AgentCard>;
|
|
196
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
|
+
|
|
197
229
|
/**
|
|
198
230
|
* Walk up from startDir looking for a .nit/ directory.
|
|
199
231
|
* Returns the path to .nit/ or throws if not found.
|
|
@@ -203,6 +235,7 @@ interface InitResult {
|
|
|
203
235
|
agentId: string;
|
|
204
236
|
publicKey: string;
|
|
205
237
|
cardUrl: string;
|
|
238
|
+
walletAddresses: WalletAddresses$1;
|
|
206
239
|
skillsFound: string[];
|
|
207
240
|
skillsDir: string;
|
|
208
241
|
}
|
|
@@ -311,4 +344,4 @@ declare function remoteSetUrl(name: string, url: string, options?: {
|
|
|
311
344
|
projectDir?: string;
|
|
312
345
|
}): Promise<void>;
|
|
313
346
|
|
|
314
|
-
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, loadRawKeyPair, 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,9 +12,13 @@ import {
|
|
|
11
12
|
findNitDir,
|
|
12
13
|
formatDiff,
|
|
13
14
|
formatPublicKeyField,
|
|
15
|
+
getEvmAddress,
|
|
16
|
+
getSolanaAddress,
|
|
17
|
+
getWalletAddresses,
|
|
14
18
|
init,
|
|
15
19
|
loadAgentId,
|
|
16
20
|
loadRawKeyPair,
|
|
21
|
+
loadSecp256k1RawKeyPair,
|
|
17
22
|
log,
|
|
18
23
|
loginPayload,
|
|
19
24
|
parsePublicKeyField,
|
|
@@ -25,9 +30,10 @@ import {
|
|
|
25
30
|
signChallenge,
|
|
26
31
|
signMessage,
|
|
27
32
|
status
|
|
28
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-BC7OEIFR.js";
|
|
29
34
|
export {
|
|
30
35
|
NIT_NAMESPACE,
|
|
36
|
+
base58Encode,
|
|
31
37
|
branch,
|
|
32
38
|
checkout,
|
|
33
39
|
commit,
|
|
@@ -38,9 +44,13 @@ export {
|
|
|
38
44
|
findNitDir,
|
|
39
45
|
formatDiff,
|
|
40
46
|
formatPublicKeyField,
|
|
47
|
+
getEvmAddress,
|
|
48
|
+
getSolanaAddress,
|
|
49
|
+
getWalletAddresses,
|
|
41
50
|
init,
|
|
42
51
|
loadAgentId,
|
|
43
52
|
loadRawKeyPair,
|
|
53
|
+
loadSecp256k1RawKeyPair,
|
|
44
54
|
log,
|
|
45
55
|
loginPayload,
|
|
46
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.1",
|
|
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"
|