@newtype-ai/nit 0.1.2 → 0.2.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 +4 -2
- package/dist/{chunk-5AVY6P7B.js → chunk-AEWDQDBM.js} +141 -122
- package/dist/cli.js +41 -23
- package/dist/index.d.ts +55 -14
- package/dist/index.js +13 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,13 +49,15 @@ nit push --all
|
|
|
49
49
|
| Command | Description |
|
|
50
50
|
|---------|-------------|
|
|
51
51
|
| `nit init` | Create `.nit/`, generate Ed25519 keypair, initial commit |
|
|
52
|
-
| `nit status` |
|
|
53
|
-
| `nit commit -m "msg"` | Snapshot agent-card.json
|
|
52
|
+
| `nit status` | Identity info, current branch, uncommitted changes |
|
|
53
|
+
| `nit commit -m "msg"` | Snapshot agent-card.json |
|
|
54
54
|
| `nit log` | Commit history for current branch |
|
|
55
55
|
| `nit diff [target]` | JSON diff vs HEAD, branch, or commit |
|
|
56
56
|
| `nit branch [name]` | List branches or create a new one |
|
|
57
57
|
| `nit checkout <branch>` | Switch branch (overwrites agent-card.json) |
|
|
58
58
|
| `nit push [--all]` | Push branch(es) to remote |
|
|
59
|
+
| `nit sign "msg"` | Sign a message with your Ed25519 key |
|
|
60
|
+
| `nit sign --login <domain>` | Generate login payload for an app |
|
|
59
61
|
| `nit remote` | Show remote URL and credential status |
|
|
60
62
|
|
|
61
63
|
## How It Works
|
|
@@ -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 fs5, statSync } from "fs";
|
|
5
|
+
import { join as join5, basename as basename2, resolve } from "path";
|
|
6
6
|
|
|
7
7
|
// src/objects.ts
|
|
8
8
|
import { createHash } from "crypto";
|
|
@@ -170,6 +170,7 @@ async function getRemoteRef(nitDir, remote2, branch2) {
|
|
|
170
170
|
|
|
171
171
|
// src/identity.ts
|
|
172
172
|
import {
|
|
173
|
+
createHash as createHash2,
|
|
173
174
|
generateKeyPairSync,
|
|
174
175
|
createPrivateKey,
|
|
175
176
|
createPublicKey,
|
|
@@ -261,94 +262,73 @@ function verifySignature(pubBase64, challenge, signatureBase64) {
|
|
|
261
262
|
Buffer.from(signatureBase64, "base64")
|
|
262
263
|
);
|
|
263
264
|
}
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
265
|
+
async function signMessage(nitDir, message) {
|
|
266
|
+
const privateKey = await loadPrivateKey(nitDir);
|
|
267
|
+
const sig = sign(null, Buffer.from(message, "utf-8"), privateKey);
|
|
268
|
+
return sig.toString("base64");
|
|
269
|
+
}
|
|
270
|
+
var NIT_NAMESPACE = "801ba518-f326-47e5-97c9-d1efd1865a19";
|
|
271
|
+
function deriveAgentId(publicKeyField) {
|
|
272
|
+
return uuidv5(publicKeyField, NIT_NAMESPACE);
|
|
273
|
+
}
|
|
274
|
+
async function loadAgentId(nitDir) {
|
|
275
|
+
const idPath = join3(nitDir, "identity", "agent-id");
|
|
272
276
|
try {
|
|
273
|
-
|
|
277
|
+
return (await fs3.readFile(idPath, "utf-8")).trim();
|
|
274
278
|
} catch {
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
}
|
|
279
|
-
async function writeConfig(nitDir, config) {
|
|
280
|
-
const configPath = join4(nitDir, CONFIG_FILE);
|
|
281
|
-
await fs4.writeFile(configPath, serializeConfig(config), "utf-8");
|
|
282
|
-
}
|
|
283
|
-
async function getRemoteCredential(nitDir, remoteName) {
|
|
284
|
-
const config = await readConfig(nitDir);
|
|
285
|
-
return config.remotes[remoteName]?.credential ?? null;
|
|
286
|
-
}
|
|
287
|
-
async function setRemoteCredential(nitDir, remoteName, credential) {
|
|
288
|
-
const config = await readConfig(nitDir);
|
|
289
|
-
if (!config.remotes[remoteName]) {
|
|
290
|
-
config.remotes[remoteName] = {};
|
|
291
|
-
}
|
|
292
|
-
config.remotes[remoteName].credential = credential;
|
|
293
|
-
await writeConfig(nitDir, config);
|
|
294
|
-
}
|
|
295
|
-
function parseConfig(raw) {
|
|
296
|
-
const remotes = {};
|
|
297
|
-
let currentRemote = null;
|
|
298
|
-
for (const line of raw.split("\n")) {
|
|
299
|
-
const trimmed = line.trim();
|
|
300
|
-
if (trimmed === "" || trimmed.startsWith("#")) continue;
|
|
301
|
-
const sectionMatch = trimmed.match(/^\[remote\s+"([^"]+)"\]$/);
|
|
302
|
-
if (sectionMatch) {
|
|
303
|
-
currentRemote = sectionMatch[1];
|
|
304
|
-
if (!remotes[currentRemote]) {
|
|
305
|
-
remotes[currentRemote] = {};
|
|
306
|
-
}
|
|
307
|
-
continue;
|
|
308
|
-
}
|
|
309
|
-
if (currentRemote !== null) {
|
|
310
|
-
const kvMatch = trimmed.match(/^(\w+)\s*=\s*(.+)$/);
|
|
311
|
-
if (kvMatch) {
|
|
312
|
-
const [, key, value] = kvMatch;
|
|
313
|
-
if (key === "credential") {
|
|
314
|
-
remotes[currentRemote].credential = value.trim();
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
}
|
|
279
|
+
throw new Error(
|
|
280
|
+
"No agent ID found. Run `nit init` to generate identity."
|
|
281
|
+
);
|
|
318
282
|
}
|
|
319
|
-
return { remotes };
|
|
320
283
|
}
|
|
321
|
-
function
|
|
322
|
-
const
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
284
|
+
async function saveAgentId(nitDir, agentId) {
|
|
285
|
+
const idPath = join3(nitDir, "identity", "agent-id");
|
|
286
|
+
await fs3.writeFile(idPath, agentId + "\n", "utf-8");
|
|
287
|
+
}
|
|
288
|
+
function parseUuid(uuid) {
|
|
289
|
+
const hex = uuid.replace(/-/g, "");
|
|
290
|
+
return Buffer.from(hex, "hex");
|
|
291
|
+
}
|
|
292
|
+
function formatUuid(bytes) {
|
|
293
|
+
const hex = bytes.toString("hex");
|
|
294
|
+
return [
|
|
295
|
+
hex.slice(0, 8),
|
|
296
|
+
hex.slice(8, 12),
|
|
297
|
+
hex.slice(12, 16),
|
|
298
|
+
hex.slice(16, 20),
|
|
299
|
+
hex.slice(20, 32)
|
|
300
|
+
].join("-");
|
|
301
|
+
}
|
|
302
|
+
function uuidv5(name, namespace) {
|
|
303
|
+
const namespaceBytes = parseUuid(namespace);
|
|
304
|
+
const nameBytes = Buffer.from(name, "utf-8");
|
|
305
|
+
const data = Buffer.concat([namespaceBytes, nameBytes]);
|
|
306
|
+
const hash = createHash2("sha1").update(data).digest();
|
|
307
|
+
const uuid = Buffer.from(hash.subarray(0, 16));
|
|
308
|
+
uuid[6] = uuid[6] & 15 | 80;
|
|
309
|
+
uuid[8] = uuid[8] & 63 | 128;
|
|
310
|
+
return formatUuid(uuid);
|
|
331
311
|
}
|
|
332
312
|
|
|
333
313
|
// src/skills.ts
|
|
334
|
-
import { promises as
|
|
335
|
-
import { join as
|
|
314
|
+
import { promises as fs4 } from "fs";
|
|
315
|
+
import { join as join4 } from "path";
|
|
336
316
|
import { homedir } from "os";
|
|
337
317
|
async function discoverSkills(projectDir2) {
|
|
338
318
|
const home = homedir();
|
|
339
319
|
const searchDirs = [
|
|
340
320
|
// Project-local (all known agent frameworks)
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
321
|
+
join4(projectDir2, ".claude", "skills"),
|
|
322
|
+
join4(projectDir2, ".cursor", "skills"),
|
|
323
|
+
join4(projectDir2, ".windsurf", "skills"),
|
|
324
|
+
join4(projectDir2, ".codex", "skills"),
|
|
325
|
+
join4(projectDir2, ".agents", "skills"),
|
|
346
326
|
// User-global
|
|
347
|
-
|
|
327
|
+
join4(home, ".claude", "skills"),
|
|
348
328
|
// Claude Code + Cursor (shared)
|
|
349
|
-
|
|
329
|
+
join4(home, ".codex", "skills"),
|
|
350
330
|
// Codex CLI
|
|
351
|
-
|
|
331
|
+
join4(home, ".codeium", "windsurf", "skills")
|
|
352
332
|
// Windsurf
|
|
353
333
|
];
|
|
354
334
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -381,15 +361,15 @@ async function resolveSkillPointers(card, projectDir2) {
|
|
|
381
361
|
async function scanSkillDir(dir) {
|
|
382
362
|
let entries;
|
|
383
363
|
try {
|
|
384
|
-
entries = await
|
|
364
|
+
entries = await fs4.readdir(dir);
|
|
385
365
|
} catch {
|
|
386
366
|
return [];
|
|
387
367
|
}
|
|
388
368
|
const skills = [];
|
|
389
369
|
for (const entry of entries) {
|
|
390
|
-
const skillMdPath =
|
|
370
|
+
const skillMdPath = join4(dir, entry, "SKILL.md");
|
|
391
371
|
try {
|
|
392
|
-
const content = await
|
|
372
|
+
const content = await fs4.readFile(skillMdPath, "utf-8");
|
|
393
373
|
const meta = parseFrontmatter(content, entry, skillMdPath);
|
|
394
374
|
if (meta) skills.push(meta);
|
|
395
375
|
} catch {
|
|
@@ -531,36 +511,50 @@ function formatValue(val) {
|
|
|
531
511
|
}
|
|
532
512
|
|
|
533
513
|
// src/remote.ts
|
|
514
|
+
import { createHash as createHash3 } from "crypto";
|
|
534
515
|
var API_BASE = "https://api.newtype-ai.org";
|
|
516
|
+
function sha256Hex(data) {
|
|
517
|
+
return createHash3("sha256").update(data, "utf-8").digest("hex");
|
|
518
|
+
}
|
|
519
|
+
async function buildAuthHeaders(nitDir, method, path, body) {
|
|
520
|
+
const agentId = await loadAgentId(nitDir);
|
|
521
|
+
const timestamp = Math.floor(Date.now() / 1e3).toString();
|
|
522
|
+
let message = `${method}
|
|
523
|
+
${path}
|
|
524
|
+
${agentId}
|
|
525
|
+
${timestamp}`;
|
|
526
|
+
if (body !== void 0) {
|
|
527
|
+
message += `
|
|
528
|
+
${sha256Hex(body)}`;
|
|
529
|
+
}
|
|
530
|
+
const signature = await signMessage(nitDir, message);
|
|
531
|
+
return {
|
|
532
|
+
"X-Nit-Agent-Id": agentId,
|
|
533
|
+
"X-Nit-Timestamp": timestamp,
|
|
534
|
+
"X-Nit-Signature": signature
|
|
535
|
+
};
|
|
536
|
+
}
|
|
535
537
|
async function pushBranch(nitDir, remoteName, branch2, cardJson, commitHash) {
|
|
536
|
-
const
|
|
537
|
-
|
|
538
|
-
return {
|
|
539
|
-
branch: branch2,
|
|
540
|
-
commitHash,
|
|
541
|
-
remoteUrl: API_BASE,
|
|
542
|
-
success: false,
|
|
543
|
-
error: `No credential configured for remote "${remoteName}". Run: nit remote set-credential <agent-key>`
|
|
544
|
-
};
|
|
545
|
-
}
|
|
546
|
-
const url = `${API_BASE}/agent-card/branches/${encodeURIComponent(branch2)}`;
|
|
538
|
+
const path = `/agent-card/branches/${encodeURIComponent(branch2)}`;
|
|
539
|
+
const body = JSON.stringify({ card_json: cardJson, commit_hash: commitHash });
|
|
547
540
|
try {
|
|
548
|
-
const
|
|
541
|
+
const authHeaders = await buildAuthHeaders(nitDir, "PUT", path, body);
|
|
542
|
+
const res = await fetch(`${API_BASE}${path}`, {
|
|
549
543
|
method: "PUT",
|
|
550
544
|
headers: {
|
|
551
545
|
"Content-Type": "application/json",
|
|
552
|
-
|
|
546
|
+
...authHeaders
|
|
553
547
|
},
|
|
554
|
-
body
|
|
548
|
+
body
|
|
555
549
|
});
|
|
556
550
|
if (!res.ok) {
|
|
557
|
-
const
|
|
551
|
+
const text = await res.text();
|
|
558
552
|
return {
|
|
559
553
|
branch: branch2,
|
|
560
554
|
commitHash,
|
|
561
555
|
remoteUrl: API_BASE,
|
|
562
556
|
success: false,
|
|
563
|
-
error: `HTTP ${res.status}: ${
|
|
557
|
+
error: `HTTP ${res.status}: ${text}`
|
|
564
558
|
};
|
|
565
559
|
}
|
|
566
560
|
return { branch: branch2, commitHash, remoteUrl: API_BASE, success: true };
|
|
@@ -615,7 +609,7 @@ var CARD_FILE = "agent-card.json";
|
|
|
615
609
|
function findNitDir(startDir) {
|
|
616
610
|
let dir = resolve(startDir || process.cwd());
|
|
617
611
|
while (true) {
|
|
618
|
-
const candidate =
|
|
612
|
+
const candidate = join5(dir, NIT_DIR);
|
|
619
613
|
try {
|
|
620
614
|
const s = statSync(candidate);
|
|
621
615
|
if (s.isDirectory()) return candidate;
|
|
@@ -634,17 +628,17 @@ function projectDir(nitDir) {
|
|
|
634
628
|
return resolve(nitDir, "..");
|
|
635
629
|
}
|
|
636
630
|
async function readWorkingCard(nitDir) {
|
|
637
|
-
const cardPath =
|
|
631
|
+
const cardPath = join5(projectDir(nitDir), CARD_FILE);
|
|
638
632
|
try {
|
|
639
|
-
const raw = await
|
|
633
|
+
const raw = await fs5.readFile(cardPath, "utf-8");
|
|
640
634
|
return JSON.parse(raw);
|
|
641
635
|
} catch {
|
|
642
636
|
throw new Error(`Cannot read ${CARD_FILE}. Does it exist?`);
|
|
643
637
|
}
|
|
644
638
|
}
|
|
645
639
|
async function writeWorkingCard(nitDir, card) {
|
|
646
|
-
const cardPath =
|
|
647
|
-
await
|
|
640
|
+
const cardPath = join5(projectDir(nitDir), CARD_FILE);
|
|
641
|
+
await fs5.writeFile(cardPath, JSON.stringify(card, null, 2) + "\n", "utf-8");
|
|
648
642
|
}
|
|
649
643
|
async function getCardAtCommit(nitDir, commitHash) {
|
|
650
644
|
const commitRaw = await readObject(nitDir, commitHash);
|
|
@@ -662,25 +656,27 @@ async function getAuthorName(nitDir) {
|
|
|
662
656
|
}
|
|
663
657
|
async function init(options) {
|
|
664
658
|
const projDir = resolve(options?.projectDir || process.cwd());
|
|
665
|
-
const nitDir =
|
|
659
|
+
const nitDir = join5(projDir, NIT_DIR);
|
|
666
660
|
try {
|
|
667
|
-
await
|
|
661
|
+
await fs5.access(nitDir);
|
|
668
662
|
throw new Error("Already initialized. .nit/ directory exists.");
|
|
669
663
|
} catch (err) {
|
|
670
664
|
if (err instanceof Error && err.message.startsWith("Already")) throw err;
|
|
671
665
|
}
|
|
672
|
-
await
|
|
673
|
-
await
|
|
674
|
-
await
|
|
675
|
-
await
|
|
676
|
-
await
|
|
666
|
+
await fs5.mkdir(join5(nitDir, "objects"), { recursive: true });
|
|
667
|
+
await fs5.mkdir(join5(nitDir, "refs", "heads"), { recursive: true });
|
|
668
|
+
await fs5.mkdir(join5(nitDir, "refs", "remote"), { recursive: true });
|
|
669
|
+
await fs5.mkdir(join5(nitDir, "identity"), { recursive: true });
|
|
670
|
+
await fs5.mkdir(join5(nitDir, "logs"), { recursive: true });
|
|
677
671
|
const { publicKey: pubBase64 } = await generateKeypair(nitDir);
|
|
678
672
|
const publicKeyField = formatPublicKeyField(pubBase64);
|
|
679
|
-
const
|
|
673
|
+
const agentId = deriveAgentId(publicKeyField);
|
|
674
|
+
await saveAgentId(nitDir, agentId);
|
|
675
|
+
const cardPath = join5(projDir, CARD_FILE);
|
|
680
676
|
let card;
|
|
681
677
|
let skillsFound = [];
|
|
682
678
|
try {
|
|
683
|
-
const raw = await
|
|
679
|
+
const raw = await fs5.readFile(cardPath, "utf-8");
|
|
684
680
|
card = JSON.parse(raw);
|
|
685
681
|
card.publicKey = publicKeyField;
|
|
686
682
|
skillsFound = card.skills.map((s) => s.id);
|
|
@@ -703,6 +699,9 @@ async function init(options) {
|
|
|
703
699
|
}))
|
|
704
700
|
};
|
|
705
701
|
}
|
|
702
|
+
if (!card.url) {
|
|
703
|
+
card.url = `https://agent-${agentId}.newtype-ai.org`;
|
|
704
|
+
}
|
|
706
705
|
await writeWorkingCard(nitDir, card);
|
|
707
706
|
const cardJson = JSON.stringify(card, null, 2);
|
|
708
707
|
const cardHash = await writeObject(nitDir, "card", cardJson);
|
|
@@ -716,11 +715,12 @@ async function init(options) {
|
|
|
716
715
|
const commitHash = await writeObject(nitDir, "commit", commitContent);
|
|
717
716
|
await setBranch(nitDir, "main", commitHash);
|
|
718
717
|
await setHead(nitDir, "main");
|
|
719
|
-
await
|
|
720
|
-
await
|
|
718
|
+
await fs5.writeFile(join5(nitDir, "logs", "HEAD"), "", "utf-8");
|
|
719
|
+
await fs5.writeFile(join5(nitDir, "config"), "", "utf-8");
|
|
721
720
|
return {
|
|
721
|
+
agentId,
|
|
722
722
|
publicKey: publicKeyField,
|
|
723
|
-
cardUrl: card.url
|
|
723
|
+
cardUrl: card.url,
|
|
724
724
|
skillsFound
|
|
725
725
|
};
|
|
726
726
|
}
|
|
@@ -729,12 +729,15 @@ async function status(options) {
|
|
|
729
729
|
const currentBranch = await getCurrentBranch(nitDir);
|
|
730
730
|
const pubBase64 = await loadPublicKey(nitDir);
|
|
731
731
|
const publicKey = formatPublicKeyField(pubBase64);
|
|
732
|
+
const agentId = await loadAgentId(nitDir);
|
|
733
|
+
const workingCard = await readWorkingCard(nitDir);
|
|
734
|
+
const cardUrl = workingCard.url || `https://agent-${agentId}.newtype-ai.org`;
|
|
732
735
|
let uncommittedChanges = null;
|
|
733
736
|
try {
|
|
734
737
|
const headHash = await resolveHead(nitDir);
|
|
735
738
|
const headCard = await getCardAtCommit(nitDir, headHash);
|
|
736
|
-
const
|
|
737
|
-
const d = diffCards(headCard,
|
|
739
|
+
const workingCard2 = await readWorkingCard(nitDir);
|
|
740
|
+
const d = diffCards(headCard, workingCard2);
|
|
738
741
|
if (d.changed) {
|
|
739
742
|
uncommittedChanges = d;
|
|
740
743
|
}
|
|
@@ -773,12 +776,28 @@ async function status(options) {
|
|
|
773
776
|
branchStatus.push({ name: b.name, ahead, behind: 0 });
|
|
774
777
|
}
|
|
775
778
|
return {
|
|
779
|
+
agentId,
|
|
780
|
+
cardUrl,
|
|
776
781
|
branch: currentBranch,
|
|
777
782
|
publicKey,
|
|
778
783
|
uncommittedChanges,
|
|
779
784
|
branches: branchStatus
|
|
780
785
|
};
|
|
781
786
|
}
|
|
787
|
+
async function sign2(message, options) {
|
|
788
|
+
const nitDir = findNitDir(options?.projectDir);
|
|
789
|
+
return signMessage(nitDir, message);
|
|
790
|
+
}
|
|
791
|
+
async function loginPayload(domain, options) {
|
|
792
|
+
const nitDir = findNitDir(options?.projectDir);
|
|
793
|
+
const agentId = await loadAgentId(nitDir);
|
|
794
|
+
const timestamp = Math.floor(Date.now() / 1e3);
|
|
795
|
+
const message = `${agentId}
|
|
796
|
+
${domain}
|
|
797
|
+
${timestamp}`;
|
|
798
|
+
const signature = await signMessage(nitDir, message);
|
|
799
|
+
return { agent_id: agentId, domain, timestamp, signature };
|
|
800
|
+
}
|
|
782
801
|
async function commit(message, options) {
|
|
783
802
|
const nitDir = findNitDir(options?.projectDir);
|
|
784
803
|
const projDir = projectDir(nitDir);
|
|
@@ -921,36 +940,36 @@ async function push(options) {
|
|
|
921
940
|
async function remote(options) {
|
|
922
941
|
const nitDir = findNitDir(options?.projectDir);
|
|
923
942
|
const card = await readWorkingCard(nitDir);
|
|
924
|
-
const
|
|
943
|
+
const agentId = await loadAgentId(nitDir);
|
|
925
944
|
return {
|
|
926
945
|
name: "origin",
|
|
927
946
|
url: card.url || "(not set)",
|
|
928
|
-
|
|
947
|
+
agentId
|
|
929
948
|
};
|
|
930
949
|
}
|
|
931
|
-
async function setCredential(credential, options) {
|
|
932
|
-
const nitDir = findNitDir(options?.projectDir);
|
|
933
|
-
const remoteName = options?.remoteName || "origin";
|
|
934
|
-
await setRemoteCredential(nitDir, remoteName, credential);
|
|
935
|
-
}
|
|
936
950
|
|
|
937
951
|
export {
|
|
938
952
|
formatPublicKeyField,
|
|
939
953
|
parsePublicKeyField,
|
|
940
954
|
signChallenge,
|
|
941
955
|
verifySignature,
|
|
956
|
+
signMessage,
|
|
957
|
+
NIT_NAMESPACE,
|
|
958
|
+
deriveAgentId,
|
|
959
|
+
loadAgentId,
|
|
942
960
|
diffCards,
|
|
943
961
|
formatDiff,
|
|
944
962
|
fetchBranchCard,
|
|
945
963
|
findNitDir,
|
|
946
964
|
init,
|
|
947
965
|
status,
|
|
966
|
+
sign2 as sign,
|
|
967
|
+
loginPayload,
|
|
948
968
|
commit,
|
|
949
969
|
log,
|
|
950
970
|
diff,
|
|
951
971
|
branch,
|
|
952
972
|
checkout,
|
|
953
973
|
push,
|
|
954
|
-
remote
|
|
955
|
-
setCredential
|
|
974
|
+
remote
|
|
956
975
|
};
|
package/dist/cli.js
CHANGED
|
@@ -8,11 +8,12 @@ import {
|
|
|
8
8
|
formatDiff,
|
|
9
9
|
init,
|
|
10
10
|
log,
|
|
11
|
+
loginPayload,
|
|
11
12
|
push,
|
|
12
13
|
remote,
|
|
13
|
-
|
|
14
|
+
sign,
|
|
14
15
|
status
|
|
15
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-AEWDQDBM.js";
|
|
16
17
|
|
|
17
18
|
// src/cli.ts
|
|
18
19
|
var bold = (s) => `\x1B[1m${s}\x1B[0m`;
|
|
@@ -48,6 +49,9 @@ async function main() {
|
|
|
48
49
|
case "push":
|
|
49
50
|
await cmdPush(args);
|
|
50
51
|
break;
|
|
52
|
+
case "sign":
|
|
53
|
+
await cmdSign(args);
|
|
54
|
+
break;
|
|
51
55
|
case "remote":
|
|
52
56
|
await cmdRemote(args);
|
|
53
57
|
break;
|
|
@@ -71,10 +75,9 @@ async function cmdInit() {
|
|
|
71
75
|
const result = await init();
|
|
72
76
|
console.log(bold("Initialized nit repository"));
|
|
73
77
|
console.log();
|
|
74
|
-
console.log(`
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
}
|
|
78
|
+
console.log(` Agent ID: ${green(result.agentId)}`);
|
|
79
|
+
console.log(` Public key: ${dim(result.publicKey)}`);
|
|
80
|
+
console.log(` Card URL: ${result.cardUrl}`);
|
|
78
81
|
if (result.skillsFound.length > 0) {
|
|
79
82
|
console.log(` Skills: ${result.skillsFound.join(", ")}`);
|
|
80
83
|
} else {
|
|
@@ -82,11 +85,16 @@ async function cmdInit() {
|
|
|
82
85
|
}
|
|
83
86
|
console.log();
|
|
84
87
|
console.log(dim("Created .nit/ with initial commit on main."));
|
|
88
|
+
console.log();
|
|
89
|
+
console.log(`Next: open ${bold("agent-card.json")} and set your name, description, and skills.`);
|
|
85
90
|
}
|
|
86
91
|
async function cmdStatus() {
|
|
87
92
|
const s = await status();
|
|
88
93
|
console.log(`On branch ${bold(s.branch)}`);
|
|
89
|
-
console.log(
|
|
94
|
+
console.log();
|
|
95
|
+
console.log(` Agent ID: ${green(s.agentId)}`);
|
|
96
|
+
console.log(` Public key: ${dim(s.publicKey)}`);
|
|
97
|
+
console.log(` Card URL: ${s.cardUrl}`);
|
|
90
98
|
console.log();
|
|
91
99
|
if (s.uncommittedChanges) {
|
|
92
100
|
console.log(yellow("Uncommitted changes:"));
|
|
@@ -177,23 +185,32 @@ async function cmdPush(args) {
|
|
|
177
185
|
}
|
|
178
186
|
}
|
|
179
187
|
async function cmdRemote(args) {
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
188
|
+
const info = await remote();
|
|
189
|
+
console.log(`${bold(info.name)}`);
|
|
190
|
+
console.log(` URL: ${info.url}`);
|
|
191
|
+
console.log(` Agent ID: ${info.agentId}`);
|
|
192
|
+
console.log(` Auth: ${green("Ed25519 keypair")}`);
|
|
193
|
+
}
|
|
194
|
+
async function cmdSign(args) {
|
|
195
|
+
const loginIndex = args.indexOf("--login");
|
|
196
|
+
if (loginIndex !== -1) {
|
|
197
|
+
const domain = args[loginIndex + 1];
|
|
198
|
+
if (!domain) {
|
|
199
|
+
console.error("Usage: nit sign --login <domain>");
|
|
185
200
|
process.exit(1);
|
|
186
201
|
}
|
|
187
|
-
await
|
|
188
|
-
console.log(
|
|
202
|
+
const payload = await loginPayload(domain);
|
|
203
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
189
204
|
return;
|
|
190
205
|
}
|
|
191
|
-
const
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
206
|
+
const message = args[0];
|
|
207
|
+
if (!message) {
|
|
208
|
+
console.error('Usage: nit sign "message"');
|
|
209
|
+
console.error(" nit sign --login <domain>");
|
|
210
|
+
process.exit(1);
|
|
211
|
+
}
|
|
212
|
+
const signature = await sign(message);
|
|
213
|
+
console.log(signature);
|
|
197
214
|
}
|
|
198
215
|
function printUsage() {
|
|
199
216
|
console.log(`
|
|
@@ -203,21 +220,22 @@ ${bold("Usage:")} nit <command> [options]
|
|
|
203
220
|
|
|
204
221
|
${bold("Commands:")}
|
|
205
222
|
init Initialize .nit/ in current directory
|
|
206
|
-
status Show
|
|
223
|
+
status Show identity, branch, and uncommitted changes
|
|
207
224
|
commit -m "msg" Snapshot agent-card.json
|
|
208
225
|
log Show commit history
|
|
209
226
|
diff [target] Compare card vs HEAD, branch, or commit
|
|
210
227
|
branch [name] List branches or create a new one
|
|
211
228
|
checkout <branch> Switch branch (overwrites agent-card.json)
|
|
212
229
|
push [--all] Push branch(es) to remote
|
|
230
|
+
sign "message" Sign a message with your Ed25519 key
|
|
231
|
+
sign --login <dom> Generate login payload for an app
|
|
213
232
|
remote Show remote info
|
|
214
|
-
remote set-credential <key> Set push credential (agent key)
|
|
215
233
|
|
|
216
234
|
${bold("Examples:")}
|
|
217
235
|
nit init
|
|
218
236
|
nit branch faam.io
|
|
219
237
|
nit checkout faam.io
|
|
220
|
-
${dim("# edit agent-card.json for
|
|
238
|
+
${dim("# edit agent-card.json for this platform...")}
|
|
221
239
|
nit commit -m "FAAM config"
|
|
222
240
|
nit push --all
|
|
223
241
|
`.trim());
|
package/dist/index.d.ts
CHANGED
|
@@ -27,7 +27,7 @@ interface NitHead {
|
|
|
27
27
|
}
|
|
28
28
|
/** Remote configuration for a single named remote. */
|
|
29
29
|
interface NitRemoteConfig {
|
|
30
|
-
/**
|
|
30
|
+
/** Legacy field — push auth is now via Ed25519 keypair */
|
|
31
31
|
credential?: string;
|
|
32
32
|
}
|
|
33
33
|
/** Full .nit/config file contents. */
|
|
@@ -101,6 +101,8 @@ interface PushResult {
|
|
|
101
101
|
}
|
|
102
102
|
/** Result returned by the status command. */
|
|
103
103
|
interface StatusResult {
|
|
104
|
+
agentId: string;
|
|
105
|
+
cardUrl: string;
|
|
104
106
|
branch: string;
|
|
105
107
|
publicKey: string;
|
|
106
108
|
uncommittedChanges: DiffResult | null;
|
|
@@ -110,6 +112,13 @@ interface StatusResult {
|
|
|
110
112
|
behind: number;
|
|
111
113
|
}>;
|
|
112
114
|
}
|
|
115
|
+
/** Result of generating a login payload for app authentication. */
|
|
116
|
+
interface LoginPayload {
|
|
117
|
+
agent_id: string;
|
|
118
|
+
domain: string;
|
|
119
|
+
timestamp: number;
|
|
120
|
+
signature: string;
|
|
121
|
+
}
|
|
113
122
|
|
|
114
123
|
/**
|
|
115
124
|
* Compare two agent cards and return a structured diff.
|
|
@@ -140,15 +149,38 @@ declare function signChallenge(nitDir: string, challenge: string): Promise<strin
|
|
|
140
149
|
* for challenge-response flows.
|
|
141
150
|
*/
|
|
142
151
|
declare function verifySignature(pubBase64: string, challenge: string, signatureBase64: string): boolean;
|
|
152
|
+
/**
|
|
153
|
+
* Sign an arbitrary message with the agent's private key.
|
|
154
|
+
* Returns a standard base64-encoded signature.
|
|
155
|
+
*/
|
|
156
|
+
declare function signMessage(nitDir: string, message: string): Promise<string>;
|
|
157
|
+
/**
|
|
158
|
+
* Fixed namespace UUID for nit agent ID derivation.
|
|
159
|
+
* Generated once, hardcoded forever. Changing this would change ALL agent IDs.
|
|
160
|
+
* Must match the server-side constant in apps/agent-cards/src/api/agent-id.ts.
|
|
161
|
+
*/
|
|
162
|
+
declare const NIT_NAMESPACE = "801ba518-f326-47e5-97c9-d1efd1865a19";
|
|
163
|
+
/**
|
|
164
|
+
* Derive a deterministic agent ID (UUID) from an Ed25519 public key field.
|
|
165
|
+
* Uses UUIDv5: SHA-1 hash of NIT_NAMESPACE + publicKeyField.
|
|
166
|
+
*
|
|
167
|
+
* @param publicKeyField "ed25519:<base64>" format string
|
|
168
|
+
* @returns UUID string (lowercase, with hyphens)
|
|
169
|
+
*/
|
|
170
|
+
declare function deriveAgentId(publicKeyField: string): string;
|
|
171
|
+
/**
|
|
172
|
+
* Load the agent ID from .nit/identity/agent-id.
|
|
173
|
+
*/
|
|
174
|
+
declare function loadAgentId(nitDir: string): Promise<string>;
|
|
143
175
|
|
|
144
176
|
/**
|
|
145
177
|
* Fetch an agent card from a remote URL.
|
|
146
178
|
*
|
|
147
179
|
* For the main branch, this is a simple public GET.
|
|
148
180
|
* For other branches, this performs the challenge-response flow:
|
|
149
|
-
* 1. Request branch
|
|
181
|
+
* 1. Request branch -> 401 with challenge
|
|
150
182
|
* 2. Sign challenge with agent's private key
|
|
151
|
-
* 3. Re-request with signature
|
|
183
|
+
* 3. Re-request with signature -> get branch card
|
|
152
184
|
*
|
|
153
185
|
* @param cardUrl The agent's card URL (e.g. https://agent-{uuid}.newtype-ai.org)
|
|
154
186
|
* @param branch Branch to fetch ("main" for public, others need auth)
|
|
@@ -162,8 +194,9 @@ declare function fetchBranchCard(cardUrl: string, branch: string, nitDir?: strin
|
|
|
162
194
|
*/
|
|
163
195
|
declare function findNitDir(startDir?: string): string;
|
|
164
196
|
interface InitResult {
|
|
197
|
+
agentId: string;
|
|
165
198
|
publicKey: string;
|
|
166
|
-
cardUrl: string
|
|
199
|
+
cardUrl: string;
|
|
167
200
|
skillsFound: string[];
|
|
168
201
|
}
|
|
169
202
|
/**
|
|
@@ -183,6 +216,21 @@ declare function init(options?: {
|
|
|
183
216
|
declare function status(options?: {
|
|
184
217
|
projectDir?: string;
|
|
185
218
|
}): Promise<StatusResult>;
|
|
219
|
+
/**
|
|
220
|
+
* Sign an arbitrary message with the agent's Ed25519 private key.
|
|
221
|
+
* Returns a base64-encoded signature.
|
|
222
|
+
*/
|
|
223
|
+
declare function sign(message: string, options?: {
|
|
224
|
+
projectDir?: string;
|
|
225
|
+
}): Promise<string>;
|
|
226
|
+
/**
|
|
227
|
+
* Generate a login payload for app authentication.
|
|
228
|
+
* Constructs the canonical message ({agent_id}\n{domain}\n{timestamp}),
|
|
229
|
+
* signs it, and returns the full payload ready to send to an app.
|
|
230
|
+
*/
|
|
231
|
+
declare function loginPayload(domain: string, options?: {
|
|
232
|
+
projectDir?: string;
|
|
233
|
+
}): Promise<LoginPayload>;
|
|
186
234
|
/**
|
|
187
235
|
* Snapshot the current agent-card.json as a new commit.
|
|
188
236
|
* Resolves skill pointers from SKILL.md before committing.
|
|
@@ -231,20 +279,13 @@ declare function push(options?: {
|
|
|
231
279
|
interface RemoteInfo {
|
|
232
280
|
name: string;
|
|
233
281
|
url: string;
|
|
234
|
-
|
|
282
|
+
agentId: string;
|
|
235
283
|
}
|
|
236
284
|
/**
|
|
237
|
-
* Show remote info. URL comes from agent-card.json,
|
|
285
|
+
* Show remote info. URL comes from agent-card.json, agent ID from .nit/identity/.
|
|
238
286
|
*/
|
|
239
287
|
declare function remote(options?: {
|
|
240
288
|
projectDir?: string;
|
|
241
289
|
}): Promise<RemoteInfo>;
|
|
242
|
-
/**
|
|
243
|
-
* Set the push credential for a remote.
|
|
244
|
-
*/
|
|
245
|
-
declare function setCredential(credential: string, options?: {
|
|
246
|
-
projectDir?: string;
|
|
247
|
-
remoteName?: string;
|
|
248
|
-
}): Promise<void>;
|
|
249
290
|
|
|
250
|
-
export { type AgentCard, type AgentCardSkill, type DiffResult, type FieldDiff, type InitResult, type NitBranch, type NitCommit, type NitConfig, type NitHead, type NitRemoteConfig, type PushResult, type RemoteInfo, type SkillMetadata, type StatusResult, branch, checkout, commit, diff, diffCards, fetchBranchCard, findNitDir, formatDiff, formatPublicKeyField, init, log, parsePublicKeyField, push, remote,
|
|
291
|
+
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, sign, signChallenge, signMessage, status, verifySignature };
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
// nit — version control for agent cards
|
|
2
2
|
import {
|
|
3
|
+
NIT_NAMESPACE,
|
|
3
4
|
branch,
|
|
4
5
|
checkout,
|
|
5
6
|
commit,
|
|
7
|
+
deriveAgentId,
|
|
6
8
|
diff,
|
|
7
9
|
diffCards,
|
|
8
10
|
fetchBranchCard,
|
|
@@ -10,19 +12,24 @@ import {
|
|
|
10
12
|
formatDiff,
|
|
11
13
|
formatPublicKeyField,
|
|
12
14
|
init,
|
|
15
|
+
loadAgentId,
|
|
13
16
|
log,
|
|
17
|
+
loginPayload,
|
|
14
18
|
parsePublicKeyField,
|
|
15
19
|
push,
|
|
16
20
|
remote,
|
|
17
|
-
|
|
21
|
+
sign,
|
|
18
22
|
signChallenge,
|
|
23
|
+
signMessage,
|
|
19
24
|
status,
|
|
20
25
|
verifySignature
|
|
21
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-AEWDQDBM.js";
|
|
22
27
|
export {
|
|
28
|
+
NIT_NAMESPACE,
|
|
23
29
|
branch,
|
|
24
30
|
checkout,
|
|
25
31
|
commit,
|
|
32
|
+
deriveAgentId,
|
|
26
33
|
diff,
|
|
27
34
|
diffCards,
|
|
28
35
|
fetchBranchCard,
|
|
@@ -30,12 +37,15 @@ export {
|
|
|
30
37
|
formatDiff,
|
|
31
38
|
formatPublicKeyField,
|
|
32
39
|
init,
|
|
40
|
+
loadAgentId,
|
|
33
41
|
log,
|
|
42
|
+
loginPayload,
|
|
34
43
|
parsePublicKeyField,
|
|
35
44
|
push,
|
|
36
45
|
remote,
|
|
37
|
-
|
|
46
|
+
sign,
|
|
38
47
|
signChallenge,
|
|
48
|
+
signMessage,
|
|
39
49
|
status,
|
|
40
50
|
verifySignature
|
|
41
51
|
};
|