@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 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 fs6, statSync } from "fs";
5
- import { join as join6, basename as basename2, resolve as resolve2 } from "path";
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 fs5 } from "fs";
656
- import { join as join5 } from "path";
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 = join5(nitDir, CONFIG_FILE);
1033
+ const configPath = join6(nitDir, CONFIG_FILE);
660
1034
  let raw;
661
1035
  try {
662
- raw = await fs5.readFile(configPath, "utf-8");
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 = join5(nitDir, CONFIG_FILE);
670
- await fs5.writeFile(configPath, serializeConfig(config), "utf-8");
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 = join6(dir, NIT_DIR);
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 = join6(projectDir(nitDir), CARD_FILE);
1140
+ const cardPath = join7(projectDir(nitDir), CARD_FILE);
767
1141
  try {
768
- const raw = await fs6.readFile(cardPath, "utf-8");
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 = join6(projectDir(nitDir), CARD_FILE);
776
- await fs6.writeFile(cardPath, JSON.stringify(card, null, 2) + "\n", "utf-8");
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 = join6(projDir, NIT_DIR);
1168
+ const nitDir = join7(projDir, NIT_DIR);
795
1169
  try {
796
- await fs6.access(nitDir);
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 fs6.mkdir(join6(nitDir, "objects"), { recursive: true });
802
- await fs6.mkdir(join6(nitDir, "refs", "heads"), { recursive: true });
803
- await fs6.mkdir(join6(nitDir, "refs", "remote"), { recursive: true });
804
- await fs6.mkdir(join6(nitDir, "identity"), { recursive: true });
805
- await fs6.mkdir(join6(nitDir, "logs"), { recursive: true });
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 = join6(projDir, CARD_FILE);
1184
+ const cardPath = join7(projDir, CARD_FILE);
811
1185
  let card;
812
1186
  let skillsFound = [];
813
1187
  try {
814
- const raw = await fs6.readFile(cardPath, "utf-8");
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 fs6.writeFile(join6(nitDir, "logs", "HEAD"), "", "utf-8");
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-3RRHLPAC.js";
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-3RRHLPAC.js";
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.2.6",
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"