@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 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 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";
@@ -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 fs5 } from "fs";
674
- import { join as join5 } from "path";
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 = join5(nitDir, CONFIG_FILE);
1033
+ const configPath = join6(nitDir, CONFIG_FILE);
678
1034
  let raw;
679
1035
  try {
680
- raw = await fs5.readFile(configPath, "utf-8");
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 = join5(nitDir, CONFIG_FILE);
688
- await fs5.writeFile(configPath, serializeConfig(config), "utf-8");
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 = join6(dir, NIT_DIR);
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 = join6(projectDir(nitDir), CARD_FILE);
1140
+ const cardPath = join7(projectDir(nitDir), CARD_FILE);
785
1141
  try {
786
- const raw = await fs6.readFile(cardPath, "utf-8");
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 = join6(projectDir(nitDir), CARD_FILE);
794
- 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");
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 = join6(projDir, NIT_DIR);
1168
+ const nitDir = join7(projDir, NIT_DIR);
813
1169
  try {
814
- await fs6.access(nitDir);
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 fs6.mkdir(join6(nitDir, "objects"), { recursive: true });
820
- await fs6.mkdir(join6(nitDir, "refs", "heads"), { recursive: true });
821
- await fs6.mkdir(join6(nitDir, "refs", "remote"), { recursive: true });
822
- await fs6.mkdir(join6(nitDir, "identity"), { recursive: true });
823
- 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 });
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 = join6(projDir, CARD_FILE);
1184
+ const cardPath = join7(projDir, CARD_FILE);
829
1185
  let card;
830
1186
  let skillsFound = [];
831
1187
  try {
832
- const raw = await fs6.readFile(cardPath, "utf-8");
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 fs6.writeFile(join6(nitDir, "logs", "HEAD"), "", "utf-8");
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-CJUMX46F.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;
@@ -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-CJUMX46F.js";
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.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"