@newtype-ai/nit 0.4.8 → 0.4.10
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 +36 -1
- package/dist/chunk-M6SR6QMR.js +291 -0
- package/dist/chunk-WHSQICPU.js +3896 -0
- package/dist/cli.js +204 -2
- package/dist/index.d.ts +168 -1
- package/dist/index.js +34 -10
- package/dist/remote-WZ625MBZ.js +15 -0
- package/package.json +2 -1
- package/scripts/postinstall.js +8 -1
- package/dist/chunk-7LNGTHKO.js +0 -1601
package/README.md
CHANGED
|
@@ -1,7 +1,20 @@
|
|
|
1
1
|
# nit
|
|
2
2
|
|
|
3
|
+
[](https://nodejs.org)
|
|
4
|
+
[](https://www.npmjs.com/package/@newtype-ai/nit)
|
|
5
|
+
|
|
3
6
|
Git for agent identity.
|
|
4
7
|
|
|
8
|
+
```
|
|
9
|
+
_ _ _____
|
|
10
|
+
| \ |"| ___ |_ " _|
|
|
11
|
+
<| \| |> |_"_| | |
|
|
12
|
+
U| |\ |u | | /| |\
|
|
13
|
+
|_| \_| U/| |\u u |_|U
|
|
14
|
+
|| \\,-.-,_|___|_,-._// \\_
|
|
15
|
+
(_") (_/ \_)-' '-(_/(__) (__)
|
|
16
|
+
```
|
|
17
|
+
|
|
5
18
|
**One identity. Any apps.**
|
|
6
19
|
|
|
7
20
|
- **Self-sovereign** — your keys, your identity. No authority assigns or revokes it
|
|
@@ -58,9 +71,12 @@ One keypair, multiple chains. No seed phrases, no extra key management.
|
|
|
58
71
|
|
|
59
72
|
- **Solana** — your Ed25519 public key *is* your Solana address
|
|
60
73
|
- **EVM** (Ethereum, BSC, Polygon, Arbitrum, etc.) — deterministic secp256k1 derivation from your Ed25519 seed
|
|
74
|
+
- **Sign & broadcast** — sign transactions and send them to any RPC endpoint
|
|
61
75
|
|
|
62
76
|
```bash
|
|
63
77
|
nit status # shows your wallet addresses
|
|
78
|
+
nit sign-tx --chain evm <hash> # sign a transaction
|
|
79
|
+
nit broadcast --chain evm <tx> # broadcast to RPC
|
|
64
80
|
```
|
|
65
81
|
|
|
66
82
|
### Skill resolution
|
|
@@ -98,11 +114,20 @@ Pure Node.js builtins. No bloat.
|
|
|
98
114
|
| `nit branch [name]` | List branches or create a new one |
|
|
99
115
|
| `nit checkout <branch>` | Switch branch (overwrites agent-card.json) |
|
|
100
116
|
| `nit push [--all]` | Push branch(es) to remote |
|
|
117
|
+
| `nit pull [--all]` | Pull branch(es) from remote |
|
|
118
|
+
| `nit reset [target]` | Restore agent-card.json from HEAD or target |
|
|
119
|
+
| `nit show [target]` | Show commit metadata and card content |
|
|
101
120
|
| `nit sign "msg"` | Sign a message with your Ed25519 key |
|
|
102
121
|
| `nit sign --login <domain>` | Auto-switch to domain branch + generate login payload |
|
|
103
122
|
| `nit remote` | Show remote URL and credential status |
|
|
104
123
|
| `nit remote add <name> <url>` | Add a new remote |
|
|
105
124
|
| `nit remote set-url <name> <url>` | Change a remote's URL |
|
|
125
|
+
| `nit sign-tx --chain <c> <data>` | Sign transaction data (EVM: 32-byte hash, Solana: message bytes) |
|
|
126
|
+
| `nit broadcast --chain <c> <tx>` | Broadcast signed transaction to configured RPC endpoint |
|
|
127
|
+
| `nit rpc` | Show configured RPC endpoints |
|
|
128
|
+
| `nit rpc set-url <chain> <url>` | Set RPC endpoint for a chain |
|
|
129
|
+
| `nit auth set <domain> --provider <p> --account <a>` | Configure OAuth auth for a domain branch |
|
|
130
|
+
| `nit auth show [domain]` | Show auth config for branch(es) |
|
|
106
131
|
|
|
107
132
|
## How It Works
|
|
108
133
|
|
|
@@ -164,7 +189,10 @@ your-project/
|
|
|
164
189
|
## Programmatic API
|
|
165
190
|
|
|
166
191
|
```typescript
|
|
167
|
-
import {
|
|
192
|
+
import {
|
|
193
|
+
init, commit, checkout, branch, push, status, sign, loginPayload,
|
|
194
|
+
loadRawKeyPair, getWalletAddresses, signTx, broadcast, rpcSetUrl, authSet, authShow,
|
|
195
|
+
} from '@newtype-ai/nit';
|
|
168
196
|
|
|
169
197
|
await init();
|
|
170
198
|
|
|
@@ -183,6 +211,13 @@ const keypair = await loadRawKeyPair('/path/to/.nit');
|
|
|
183
211
|
// Get blockchain wallet addresses (derived from your identity)
|
|
184
212
|
const addresses = await getWalletAddresses('/path/to/.nit');
|
|
185
213
|
// → { solana: "C54kvW3...", ethereum: "0x2317..." }
|
|
214
|
+
|
|
215
|
+
// Sign and broadcast transactions
|
|
216
|
+
await rpcSetUrl('evm', 'https://eth.llamarpc.com');
|
|
217
|
+
const sig = await signTx('evm', '0x<32-byte-keccak256-hash>');
|
|
218
|
+
// → { chain: 'evm', signature: '0x...', recovery: 0, address: '0x...' }
|
|
219
|
+
await broadcast('evm', '0x<signed-tx-hex>');
|
|
220
|
+
// → { chain: 'evm', txHash: '0x...', rpcUrl: 'https://...' }
|
|
186
221
|
```
|
|
187
222
|
|
|
188
223
|
## License
|
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
// nit — version control for agent cards
|
|
2
|
+
|
|
3
|
+
// src/remote.ts
|
|
4
|
+
import { createHash as createHash2 } from "crypto";
|
|
5
|
+
|
|
6
|
+
// src/identity.ts
|
|
7
|
+
import {
|
|
8
|
+
createHash,
|
|
9
|
+
generateKeyPairSync,
|
|
10
|
+
createPrivateKey,
|
|
11
|
+
createPublicKey,
|
|
12
|
+
sign,
|
|
13
|
+
verify
|
|
14
|
+
} from "crypto";
|
|
15
|
+
import { promises as fs } from "fs";
|
|
16
|
+
import { join } from "path";
|
|
17
|
+
function base64urlToBase64(b64url) {
|
|
18
|
+
let s = b64url.replace(/-/g, "+").replace(/_/g, "/");
|
|
19
|
+
while (s.length % 4 !== 0) s += "=";
|
|
20
|
+
return s;
|
|
21
|
+
}
|
|
22
|
+
function base64ToBase64url(b64) {
|
|
23
|
+
return b64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
24
|
+
}
|
|
25
|
+
async function generateKeypair(nitDir) {
|
|
26
|
+
const identityDir = join(nitDir, "identity");
|
|
27
|
+
await fs.mkdir(identityDir, { recursive: true });
|
|
28
|
+
const { publicKey, privateKey } = generateKeyPairSync("ed25519");
|
|
29
|
+
const pubJwk = publicKey.export({ format: "jwk" });
|
|
30
|
+
const privJwk = privateKey.export({ format: "jwk" });
|
|
31
|
+
const pubBase64 = base64urlToBase64(pubJwk.x);
|
|
32
|
+
const privBase64 = base64urlToBase64(privJwk.d);
|
|
33
|
+
const pubPath = join(identityDir, "agent.pub");
|
|
34
|
+
const keyPath = join(identityDir, "agent.key");
|
|
35
|
+
await fs.writeFile(pubPath, pubBase64 + "\n", "utf-8");
|
|
36
|
+
await fs.writeFile(keyPath, privBase64 + "\n", {
|
|
37
|
+
mode: 384,
|
|
38
|
+
encoding: "utf-8"
|
|
39
|
+
});
|
|
40
|
+
return { publicKey: pubBase64, privateKey: privBase64 };
|
|
41
|
+
}
|
|
42
|
+
async function loadPublicKey(nitDir) {
|
|
43
|
+
const pubPath = join(nitDir, "identity", "agent.pub");
|
|
44
|
+
try {
|
|
45
|
+
return (await fs.readFile(pubPath, "utf-8")).trim();
|
|
46
|
+
} catch {
|
|
47
|
+
throw new Error(
|
|
48
|
+
"No identity found. Run `nit init` to generate a keypair."
|
|
49
|
+
);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
async function loadPrivateKey(nitDir) {
|
|
53
|
+
const pubBase64 = await loadPublicKey(nitDir);
|
|
54
|
+
const keyPath = join(nitDir, "identity", "agent.key");
|
|
55
|
+
let privBase64;
|
|
56
|
+
try {
|
|
57
|
+
privBase64 = (await fs.readFile(keyPath, "utf-8")).trim();
|
|
58
|
+
} catch {
|
|
59
|
+
throw new Error(
|
|
60
|
+
"Private key not found at .nit/identity/agent.key. Regenerate with `nit init`."
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
const xB64url = base64ToBase64url(pubBase64);
|
|
64
|
+
const dB64url = base64ToBase64url(privBase64);
|
|
65
|
+
return createPrivateKey({
|
|
66
|
+
key: { kty: "OKP", crv: "Ed25519", x: xB64url, d: dB64url },
|
|
67
|
+
format: "jwk"
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
async function loadRawKeyPair(nitDir) {
|
|
71
|
+
const pubBase64 = await loadPublicKey(nitDir);
|
|
72
|
+
const keyPath = join(nitDir, "identity", "agent.key");
|
|
73
|
+
let privBase64;
|
|
74
|
+
try {
|
|
75
|
+
privBase64 = (await fs.readFile(keyPath, "utf-8")).trim();
|
|
76
|
+
} catch {
|
|
77
|
+
throw new Error(
|
|
78
|
+
"Private key not found at .nit/identity/agent.key. Regenerate with `nit init`."
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
const seed = Buffer.from(privBase64, "base64");
|
|
82
|
+
const pubkey = Buffer.from(pubBase64, "base64");
|
|
83
|
+
const keypair = new Uint8Array(64);
|
|
84
|
+
keypair.set(seed, 0);
|
|
85
|
+
keypair.set(pubkey, 32);
|
|
86
|
+
return keypair;
|
|
87
|
+
}
|
|
88
|
+
function formatPublicKeyField(pubBase64) {
|
|
89
|
+
return `ed25519:${pubBase64}`;
|
|
90
|
+
}
|
|
91
|
+
function parsePublicKeyField(field) {
|
|
92
|
+
const prefix = "ed25519:";
|
|
93
|
+
if (!field.startsWith(prefix)) {
|
|
94
|
+
throw new Error(
|
|
95
|
+
`Invalid publicKey format: expected "ed25519:<base64>", got "${field}"`
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
return field.slice(prefix.length);
|
|
99
|
+
}
|
|
100
|
+
async function signChallenge(nitDir, challenge) {
|
|
101
|
+
const privateKey = await loadPrivateKey(nitDir);
|
|
102
|
+
const sig = sign(null, Buffer.from(challenge, "utf-8"), privateKey);
|
|
103
|
+
return sig.toString("base64");
|
|
104
|
+
}
|
|
105
|
+
async function signMessage(nitDir, message) {
|
|
106
|
+
const privateKey = await loadPrivateKey(nitDir);
|
|
107
|
+
const sig = sign(null, Buffer.from(message, "utf-8"), privateKey);
|
|
108
|
+
return sig.toString("base64");
|
|
109
|
+
}
|
|
110
|
+
var NIT_NAMESPACE = "801ba518-f326-47e5-97c9-d1efd1865a19";
|
|
111
|
+
function deriveAgentId(publicKeyField) {
|
|
112
|
+
return uuidv5(publicKeyField, NIT_NAMESPACE);
|
|
113
|
+
}
|
|
114
|
+
async function loadAgentId(nitDir) {
|
|
115
|
+
const idPath = join(nitDir, "identity", "agent-id");
|
|
116
|
+
try {
|
|
117
|
+
return (await fs.readFile(idPath, "utf-8")).trim();
|
|
118
|
+
} catch {
|
|
119
|
+
throw new Error(
|
|
120
|
+
"No agent ID found. Run `nit init` to generate identity."
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
async function saveAgentId(nitDir, agentId) {
|
|
125
|
+
const idPath = join(nitDir, "identity", "agent-id");
|
|
126
|
+
await fs.writeFile(idPath, agentId + "\n", "utf-8");
|
|
127
|
+
}
|
|
128
|
+
function parseUuid(uuid) {
|
|
129
|
+
const hex = uuid.replace(/-/g, "");
|
|
130
|
+
return Buffer.from(hex, "hex");
|
|
131
|
+
}
|
|
132
|
+
function formatUuid(bytes) {
|
|
133
|
+
const hex = bytes.toString("hex");
|
|
134
|
+
return [
|
|
135
|
+
hex.slice(0, 8),
|
|
136
|
+
hex.slice(8, 12),
|
|
137
|
+
hex.slice(12, 16),
|
|
138
|
+
hex.slice(16, 20),
|
|
139
|
+
hex.slice(20, 32)
|
|
140
|
+
].join("-");
|
|
141
|
+
}
|
|
142
|
+
function uuidv5(name, namespace) {
|
|
143
|
+
const namespaceBytes = parseUuid(namespace);
|
|
144
|
+
const nameBytes = Buffer.from(name, "utf-8");
|
|
145
|
+
const data = Buffer.concat([namespaceBytes, nameBytes]);
|
|
146
|
+
const hash = createHash("sha1").update(data).digest();
|
|
147
|
+
const uuid = Buffer.from(hash.subarray(0, 16));
|
|
148
|
+
uuid[6] = uuid[6] & 15 | 80;
|
|
149
|
+
uuid[8] = uuid[8] & 63 | 128;
|
|
150
|
+
return formatUuid(uuid);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// src/remote.ts
|
|
154
|
+
function sha256Hex(data) {
|
|
155
|
+
return createHash2("sha256").update(data, "utf-8").digest("hex");
|
|
156
|
+
}
|
|
157
|
+
async function buildAuthHeaders(nitDir, method, path, body) {
|
|
158
|
+
const agentId = await loadAgentId(nitDir);
|
|
159
|
+
const timestamp = Math.floor(Date.now() / 1e3).toString();
|
|
160
|
+
let message = `${method}
|
|
161
|
+
${path}
|
|
162
|
+
${agentId}
|
|
163
|
+
${timestamp}`;
|
|
164
|
+
if (body !== void 0) {
|
|
165
|
+
message += `
|
|
166
|
+
${sha256Hex(body)}`;
|
|
167
|
+
}
|
|
168
|
+
const signature = await signMessage(nitDir, message);
|
|
169
|
+
return {
|
|
170
|
+
"X-Nit-Agent-Id": agentId,
|
|
171
|
+
"X-Nit-Timestamp": timestamp,
|
|
172
|
+
"X-Nit-Signature": signature
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
async function pushBranch(nitDir, apiBase, branch, cardJson, commitHash) {
|
|
176
|
+
const path = `/agent-card/branches/${encodeURIComponent(branch)}`;
|
|
177
|
+
const body = JSON.stringify({ card_json: cardJson, commit_hash: commitHash });
|
|
178
|
+
try {
|
|
179
|
+
const authHeaders = await buildAuthHeaders(nitDir, "PUT", path, body);
|
|
180
|
+
const res = await fetch(`${apiBase}${path}`, {
|
|
181
|
+
method: "PUT",
|
|
182
|
+
headers: {
|
|
183
|
+
"Content-Type": "application/json",
|
|
184
|
+
...authHeaders
|
|
185
|
+
},
|
|
186
|
+
body
|
|
187
|
+
});
|
|
188
|
+
if (!res.ok) {
|
|
189
|
+
const text = await res.text();
|
|
190
|
+
return {
|
|
191
|
+
branch,
|
|
192
|
+
commitHash,
|
|
193
|
+
remoteUrl: apiBase,
|
|
194
|
+
success: false,
|
|
195
|
+
error: `HTTP ${res.status}: ${text}`
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
return { branch, commitHash, remoteUrl: apiBase, success: true };
|
|
199
|
+
} catch (err) {
|
|
200
|
+
return {
|
|
201
|
+
branch,
|
|
202
|
+
commitHash,
|
|
203
|
+
remoteUrl: apiBase,
|
|
204
|
+
success: false,
|
|
205
|
+
error: err instanceof Error ? err.message : String(err)
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
async function pushAll(nitDir, apiBase, branches) {
|
|
210
|
+
const results = [];
|
|
211
|
+
for (const b of branches) {
|
|
212
|
+
const result = await pushBranch(nitDir, apiBase, b.name, b.cardJson, b.commitHash);
|
|
213
|
+
results.push(result);
|
|
214
|
+
}
|
|
215
|
+
return results;
|
|
216
|
+
}
|
|
217
|
+
async function listRemoteBranches(nitDir, apiBase) {
|
|
218
|
+
const path = "/agent-card/branches";
|
|
219
|
+
const authHeaders = await buildAuthHeaders(nitDir, "GET", path);
|
|
220
|
+
const res = await fetch(`${apiBase}${path}`, {
|
|
221
|
+
headers: authHeaders
|
|
222
|
+
});
|
|
223
|
+
if (!res.ok) {
|
|
224
|
+
throw new Error(`Failed to list remote branches: HTTP ${res.status}`);
|
|
225
|
+
}
|
|
226
|
+
const data = await res.json();
|
|
227
|
+
return data.branches.map((b) => b.name);
|
|
228
|
+
}
|
|
229
|
+
async function deleteRemoteBranch(nitDir, apiBase, branch) {
|
|
230
|
+
const path = `/agent-card/branches/${encodeURIComponent(branch)}`;
|
|
231
|
+
const authHeaders = await buildAuthHeaders(nitDir, "DELETE", path);
|
|
232
|
+
const res = await fetch(`${apiBase}${path}`, {
|
|
233
|
+
method: "DELETE",
|
|
234
|
+
headers: authHeaders
|
|
235
|
+
});
|
|
236
|
+
return res.ok;
|
|
237
|
+
}
|
|
238
|
+
async function fetchBranchCard(cardUrl, branch, nitDir) {
|
|
239
|
+
const baseUrl = cardUrl.replace(/\/$/, "");
|
|
240
|
+
let url = `${baseUrl}/.well-known/agent-card.json`;
|
|
241
|
+
if (branch !== "main") {
|
|
242
|
+
url += `?branch=${encodeURIComponent(branch)}`;
|
|
243
|
+
}
|
|
244
|
+
const res = await fetch(url);
|
|
245
|
+
if (res.ok) {
|
|
246
|
+
return await res.json();
|
|
247
|
+
}
|
|
248
|
+
if (res.status === 401 && branch !== "main") {
|
|
249
|
+
if (!nitDir) {
|
|
250
|
+
throw new Error(
|
|
251
|
+
`Branch "${branch}" requires authentication. Provide nitDir for signing.`
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
const challengeData = await res.json();
|
|
255
|
+
const signature = await signChallenge(nitDir, challengeData.challenge);
|
|
256
|
+
const authRes = await fetch(url, {
|
|
257
|
+
headers: {
|
|
258
|
+
"X-Nit-Challenge": challengeData.challenge,
|
|
259
|
+
"X-Nit-Signature": signature
|
|
260
|
+
}
|
|
261
|
+
});
|
|
262
|
+
if (!authRes.ok) {
|
|
263
|
+
const body = await authRes.text();
|
|
264
|
+
throw new Error(
|
|
265
|
+
`Failed to fetch branch "${branch}" after challenge: HTTP ${authRes.status} ${body}`
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
return await authRes.json();
|
|
269
|
+
}
|
|
270
|
+
throw new Error(`Failed to fetch card: HTTP ${res.status}`);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
export {
|
|
274
|
+
generateKeypair,
|
|
275
|
+
loadPublicKey,
|
|
276
|
+
loadPrivateKey,
|
|
277
|
+
loadRawKeyPair,
|
|
278
|
+
formatPublicKeyField,
|
|
279
|
+
parsePublicKeyField,
|
|
280
|
+
signChallenge,
|
|
281
|
+
signMessage,
|
|
282
|
+
NIT_NAMESPACE,
|
|
283
|
+
deriveAgentId,
|
|
284
|
+
loadAgentId,
|
|
285
|
+
saveAgentId,
|
|
286
|
+
pushBranch,
|
|
287
|
+
pushAll,
|
|
288
|
+
listRemoteBranches,
|
|
289
|
+
deleteRemoteBranch,
|
|
290
|
+
fetchBranchCard
|
|
291
|
+
};
|