@newtype-ai/nit 0.2.2 → 0.2.4
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 +8 -2
- package/dist/{chunk-AEWDQDBM.js → chunk-U6PEH4IJ.js} +238 -35
- package/dist/cli.js +43 -3
- package/dist/index.d.ts +30 -7
- package/dist/index.js +5 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -59,6 +59,8 @@ nit push --all
|
|
|
59
59
|
| `nit sign "msg"` | Sign a message with your Ed25519 key |
|
|
60
60
|
| `nit sign --login <domain>` | Generate login payload for an app |
|
|
61
61
|
| `nit remote` | Show remote URL and credential status |
|
|
62
|
+
| `nit remote add <name> <url>` | Add a new remote |
|
|
63
|
+
| `nit remote set-url <name> <url>` | Change a remote's URL |
|
|
62
64
|
|
|
63
65
|
## How It Works
|
|
64
66
|
|
|
@@ -102,7 +104,11 @@ GET /.well-known/agent-card.json?branch=faam.io → 401 { challenge }
|
|
|
102
104
|
GET ... + X-Nit-Signature + X-Nit-Challenge → branch card
|
|
103
105
|
```
|
|
104
106
|
|
|
105
|
-
nit is the client. Any server can implement the protocol. [newtype-ai.org](https://newtype-ai.org)
|
|
107
|
+
nit is the client. Any server can implement the protocol. [newtype-ai.org](https://newtype-ai.org) is the recommended free hosting service, but you can point to any compatible server:
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
nit remote set-url origin https://my-server.com
|
|
111
|
+
```
|
|
106
112
|
|
|
107
113
|
## Directory Structure
|
|
108
114
|
|
|
@@ -110,7 +116,7 @@ nit is the client. Any server can implement the protocol. [newtype-ai.org](https
|
|
|
110
116
|
your-project/
|
|
111
117
|
├── .nit/ # nit repository (gitignored)
|
|
112
118
|
│ ├── HEAD # Current branch ref
|
|
113
|
-
│ ├── config # Remote credentials
|
|
119
|
+
│ ├── config # Remote URL and credentials
|
|
114
120
|
│ ├── identity/
|
|
115
121
|
│ │ ├── agent.pub # Ed25519 public key
|
|
116
122
|
│ │ └── agent.key # Ed25519 private key (0600)
|
|
@@ -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 fs6, statSync } from "fs";
|
|
5
|
+
import { join as join6, basename as basename2, resolve as resolve2 } from "path";
|
|
6
6
|
|
|
7
7
|
// src/objects.ts
|
|
8
8
|
import { createHash } from "crypto";
|
|
@@ -312,8 +312,70 @@ function uuidv5(name, namespace) {
|
|
|
312
312
|
|
|
313
313
|
// src/skills.ts
|
|
314
314
|
import { promises as fs4 } from "fs";
|
|
315
|
-
import { join as join4 } from "path";
|
|
315
|
+
import { join as join4, resolve } from "path";
|
|
316
316
|
import { homedir } from "os";
|
|
317
|
+
var FRAMEWORK_MARKERS = [
|
|
318
|
+
{ marker: ".claude", skillsPath: ".claude/skills" },
|
|
319
|
+
{ marker: ".cursor", skillsPath: ".cursor/skills" },
|
|
320
|
+
{ marker: ".codex", skillsPath: ".codex/skills" },
|
|
321
|
+
{ marker: ".windsurf", skillsPath: ".windsurf/skills" },
|
|
322
|
+
{ marker: ".openclaw", skillsPath: ".agents/skills" }
|
|
323
|
+
];
|
|
324
|
+
var GLOBAL_SKILLS_DIRS = [
|
|
325
|
+
{ marker: ".claude", skillsPath: ".claude/skills" },
|
|
326
|
+
{ marker: ".codex", skillsPath: ".codex/skills" },
|
|
327
|
+
{ marker: ".codeium", skillsPath: ".codeium/windsurf/skills" }
|
|
328
|
+
];
|
|
329
|
+
async function discoverSkillsDir(projectDir2) {
|
|
330
|
+
const absProject = resolve(projectDir2);
|
|
331
|
+
const home = homedir();
|
|
332
|
+
for (const { marker, skillsPath } of FRAMEWORK_MARKERS) {
|
|
333
|
+
if (absProject.includes(`/${marker}/`) || absProject.includes(`/${marker}`)) {
|
|
334
|
+
return join4(absProject, skillsPath);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
for (const { marker, skillsPath } of FRAMEWORK_MARKERS) {
|
|
338
|
+
try {
|
|
339
|
+
const stat = await fs4.stat(join4(absProject, marker));
|
|
340
|
+
if (stat.isDirectory()) {
|
|
341
|
+
return join4(absProject, skillsPath);
|
|
342
|
+
}
|
|
343
|
+
} catch {
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
for (const { marker, skillsPath } of GLOBAL_SKILLS_DIRS) {
|
|
347
|
+
try {
|
|
348
|
+
const stat = await fs4.stat(join4(home, marker));
|
|
349
|
+
if (stat.isDirectory()) {
|
|
350
|
+
return join4(home, skillsPath);
|
|
351
|
+
}
|
|
352
|
+
} catch {
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
return join4(absProject, ".agents", "skills");
|
|
356
|
+
}
|
|
357
|
+
async function createSkillTemplate(skillsDir, domain) {
|
|
358
|
+
const skillId = domain.replace(/\./g, "-");
|
|
359
|
+
const skillDir = join4(skillsDir, skillId);
|
|
360
|
+
const skillPath = join4(skillDir, "SKILL.md");
|
|
361
|
+
try {
|
|
362
|
+
await fs4.access(skillPath);
|
|
363
|
+
return skillId;
|
|
364
|
+
} catch {
|
|
365
|
+
}
|
|
366
|
+
await fs4.mkdir(skillDir, { recursive: true });
|
|
367
|
+
const template = `---
|
|
368
|
+
name: ${skillId}
|
|
369
|
+
description: Skills and context for ${domain}
|
|
370
|
+
---
|
|
371
|
+
|
|
372
|
+
# ${skillId}
|
|
373
|
+
|
|
374
|
+
Configure your skills and context for ${domain} here.
|
|
375
|
+
`;
|
|
376
|
+
await fs4.writeFile(skillPath, template, "utf-8");
|
|
377
|
+
return skillId;
|
|
378
|
+
}
|
|
317
379
|
async function discoverSkills(projectDir2) {
|
|
318
380
|
const home = homedir();
|
|
319
381
|
const searchDirs = [
|
|
@@ -512,7 +574,6 @@ function formatValue(val) {
|
|
|
512
574
|
|
|
513
575
|
// src/remote.ts
|
|
514
576
|
import { createHash as createHash3 } from "crypto";
|
|
515
|
-
var API_BASE = "https://api.newtype-ai.org";
|
|
516
577
|
function sha256Hex(data) {
|
|
517
578
|
return createHash3("sha256").update(data, "utf-8").digest("hex");
|
|
518
579
|
}
|
|
@@ -534,12 +595,12 @@ ${sha256Hex(body)}`;
|
|
|
534
595
|
"X-Nit-Signature": signature
|
|
535
596
|
};
|
|
536
597
|
}
|
|
537
|
-
async function pushBranch(nitDir,
|
|
598
|
+
async function pushBranch(nitDir, apiBase, branch2, cardJson, commitHash) {
|
|
538
599
|
const path = `/agent-card/branches/${encodeURIComponent(branch2)}`;
|
|
539
600
|
const body = JSON.stringify({ card_json: cardJson, commit_hash: commitHash });
|
|
540
601
|
try {
|
|
541
602
|
const authHeaders = await buildAuthHeaders(nitDir, "PUT", path, body);
|
|
542
|
-
const res = await fetch(`${
|
|
603
|
+
const res = await fetch(`${apiBase}${path}`, {
|
|
543
604
|
method: "PUT",
|
|
544
605
|
headers: {
|
|
545
606
|
"Content-Type": "application/json",
|
|
@@ -552,17 +613,17 @@ async function pushBranch(nitDir, remoteName, branch2, cardJson, commitHash) {
|
|
|
552
613
|
return {
|
|
553
614
|
branch: branch2,
|
|
554
615
|
commitHash,
|
|
555
|
-
remoteUrl:
|
|
616
|
+
remoteUrl: apiBase,
|
|
556
617
|
success: false,
|
|
557
618
|
error: `HTTP ${res.status}: ${text}`
|
|
558
619
|
};
|
|
559
620
|
}
|
|
560
|
-
return { branch: branch2, commitHash, remoteUrl:
|
|
621
|
+
return { branch: branch2, commitHash, remoteUrl: apiBase, success: true };
|
|
561
622
|
} catch (err) {
|
|
562
623
|
return {
|
|
563
624
|
branch: branch2,
|
|
564
625
|
commitHash,
|
|
565
|
-
remoteUrl:
|
|
626
|
+
remoteUrl: apiBase,
|
|
566
627
|
success: false,
|
|
567
628
|
error: err instanceof Error ? err.message : String(err)
|
|
568
629
|
};
|
|
@@ -603,19 +664,106 @@ async function fetchBranchCard(cardUrl, branch2, nitDir) {
|
|
|
603
664
|
throw new Error(`Failed to fetch card: HTTP ${res.status}`);
|
|
604
665
|
}
|
|
605
666
|
|
|
667
|
+
// src/config.ts
|
|
668
|
+
import { promises as fs5 } from "fs";
|
|
669
|
+
import { join as join5 } from "path";
|
|
670
|
+
var CONFIG_FILE = "config";
|
|
671
|
+
async function readConfig(nitDir) {
|
|
672
|
+
const configPath = join5(nitDir, CONFIG_FILE);
|
|
673
|
+
let raw;
|
|
674
|
+
try {
|
|
675
|
+
raw = await fs5.readFile(configPath, "utf-8");
|
|
676
|
+
} catch {
|
|
677
|
+
return { remotes: {} };
|
|
678
|
+
}
|
|
679
|
+
return parseConfig(raw);
|
|
680
|
+
}
|
|
681
|
+
async function writeConfig(nitDir, config) {
|
|
682
|
+
const configPath = join5(nitDir, CONFIG_FILE);
|
|
683
|
+
await fs5.writeFile(configPath, serializeConfig(config), "utf-8");
|
|
684
|
+
}
|
|
685
|
+
async function getRemoteUrl(nitDir, remoteName) {
|
|
686
|
+
const config = await readConfig(nitDir);
|
|
687
|
+
return config.remotes[remoteName]?.url ?? null;
|
|
688
|
+
}
|
|
689
|
+
async function getSkillsDir(nitDir) {
|
|
690
|
+
const config = await readConfig(nitDir);
|
|
691
|
+
return config.skillsDir ?? null;
|
|
692
|
+
}
|
|
693
|
+
function parseConfig(raw) {
|
|
694
|
+
const remotes = {};
|
|
695
|
+
let currentSection = null;
|
|
696
|
+
let currentRemote = null;
|
|
697
|
+
let skillsDir;
|
|
698
|
+
for (const line of raw.split("\n")) {
|
|
699
|
+
const trimmed = line.trim();
|
|
700
|
+
if (trimmed === "" || trimmed.startsWith("#")) continue;
|
|
701
|
+
const remoteMatch = trimmed.match(/^\[remote\s+"([^"]+)"\]$/);
|
|
702
|
+
if (remoteMatch) {
|
|
703
|
+
currentSection = "remote";
|
|
704
|
+
currentRemote = remoteMatch[1];
|
|
705
|
+
if (!remotes[currentRemote]) {
|
|
706
|
+
remotes[currentRemote] = {};
|
|
707
|
+
}
|
|
708
|
+
continue;
|
|
709
|
+
}
|
|
710
|
+
if (trimmed === "[skills]") {
|
|
711
|
+
currentSection = "skills";
|
|
712
|
+
currentRemote = null;
|
|
713
|
+
continue;
|
|
714
|
+
}
|
|
715
|
+
const kvMatch = trimmed.match(/^(\w+)\s*=\s*(.+)$/);
|
|
716
|
+
if (kvMatch) {
|
|
717
|
+
const [, key, value] = kvMatch;
|
|
718
|
+
if (currentSection === "remote" && currentRemote !== null) {
|
|
719
|
+
if (key === "url") {
|
|
720
|
+
remotes[currentRemote].url = value.trim();
|
|
721
|
+
} else if (key === "credential") {
|
|
722
|
+
remotes[currentRemote].credential = value.trim();
|
|
723
|
+
}
|
|
724
|
+
} else if (currentSection === "skills") {
|
|
725
|
+
if (key === "dir") {
|
|
726
|
+
skillsDir = value.trim();
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
return { remotes, skillsDir };
|
|
732
|
+
}
|
|
733
|
+
function serializeConfig(config) {
|
|
734
|
+
const lines = [];
|
|
735
|
+
for (const [name, remote2] of Object.entries(config.remotes)) {
|
|
736
|
+
lines.push(`[remote "${name}"]`);
|
|
737
|
+
if (remote2.url) {
|
|
738
|
+
lines.push(` url = ${remote2.url}`);
|
|
739
|
+
}
|
|
740
|
+
if (remote2.credential) {
|
|
741
|
+
lines.push(` credential = ${remote2.credential}`);
|
|
742
|
+
}
|
|
743
|
+
lines.push("");
|
|
744
|
+
}
|
|
745
|
+
if (config.skillsDir) {
|
|
746
|
+
lines.push("[skills]");
|
|
747
|
+
lines.push(` dir = ${config.skillsDir}`);
|
|
748
|
+
lines.push("");
|
|
749
|
+
}
|
|
750
|
+
return lines.join("\n");
|
|
751
|
+
}
|
|
752
|
+
|
|
606
753
|
// src/index.ts
|
|
607
754
|
var NIT_DIR = ".nit";
|
|
608
755
|
var CARD_FILE = "agent-card.json";
|
|
756
|
+
var DEFAULT_API_BASE = "https://api.newtype-ai.org";
|
|
609
757
|
function findNitDir(startDir) {
|
|
610
|
-
let dir =
|
|
758
|
+
let dir = resolve2(startDir || process.cwd());
|
|
611
759
|
while (true) {
|
|
612
|
-
const candidate =
|
|
760
|
+
const candidate = join6(dir, NIT_DIR);
|
|
613
761
|
try {
|
|
614
762
|
const s = statSync(candidate);
|
|
615
763
|
if (s.isDirectory()) return candidate;
|
|
616
764
|
} catch {
|
|
617
765
|
}
|
|
618
|
-
const parent =
|
|
766
|
+
const parent = resolve2(dir, "..");
|
|
619
767
|
if (parent === dir) {
|
|
620
768
|
throw new Error(
|
|
621
769
|
"Not a nit repository (or any parent directory). Run `nit init` first."
|
|
@@ -625,20 +773,20 @@ function findNitDir(startDir) {
|
|
|
625
773
|
}
|
|
626
774
|
}
|
|
627
775
|
function projectDir(nitDir) {
|
|
628
|
-
return
|
|
776
|
+
return resolve2(nitDir, "..");
|
|
629
777
|
}
|
|
630
778
|
async function readWorkingCard(nitDir) {
|
|
631
|
-
const cardPath =
|
|
779
|
+
const cardPath = join6(projectDir(nitDir), CARD_FILE);
|
|
632
780
|
try {
|
|
633
|
-
const raw = await
|
|
781
|
+
const raw = await fs6.readFile(cardPath, "utf-8");
|
|
634
782
|
return JSON.parse(raw);
|
|
635
783
|
} catch {
|
|
636
784
|
throw new Error(`Cannot read ${CARD_FILE}. Does it exist?`);
|
|
637
785
|
}
|
|
638
786
|
}
|
|
639
787
|
async function writeWorkingCard(nitDir, card) {
|
|
640
|
-
const cardPath =
|
|
641
|
-
await
|
|
788
|
+
const cardPath = join6(projectDir(nitDir), CARD_FILE);
|
|
789
|
+
await fs6.writeFile(cardPath, JSON.stringify(card, null, 2) + "\n", "utf-8");
|
|
642
790
|
}
|
|
643
791
|
async function getCardAtCommit(nitDir, commitHash) {
|
|
644
792
|
const commitRaw = await readObject(nitDir, commitHash);
|
|
@@ -655,28 +803,28 @@ async function getAuthorName(nitDir) {
|
|
|
655
803
|
}
|
|
656
804
|
}
|
|
657
805
|
async function init(options) {
|
|
658
|
-
const projDir =
|
|
659
|
-
const nitDir =
|
|
806
|
+
const projDir = resolve2(options?.projectDir || process.cwd());
|
|
807
|
+
const nitDir = join6(projDir, NIT_DIR);
|
|
660
808
|
try {
|
|
661
|
-
await
|
|
809
|
+
await fs6.access(nitDir);
|
|
662
810
|
throw new Error("Already initialized. .nit/ directory exists.");
|
|
663
811
|
} catch (err) {
|
|
664
812
|
if (err instanceof Error && err.message.startsWith("Already")) throw err;
|
|
665
813
|
}
|
|
666
|
-
await
|
|
667
|
-
await
|
|
668
|
-
await
|
|
669
|
-
await
|
|
670
|
-
await
|
|
814
|
+
await fs6.mkdir(join6(nitDir, "objects"), { recursive: true });
|
|
815
|
+
await fs6.mkdir(join6(nitDir, "refs", "heads"), { recursive: true });
|
|
816
|
+
await fs6.mkdir(join6(nitDir, "refs", "remote"), { recursive: true });
|
|
817
|
+
await fs6.mkdir(join6(nitDir, "identity"), { recursive: true });
|
|
818
|
+
await fs6.mkdir(join6(nitDir, "logs"), { recursive: true });
|
|
671
819
|
const { publicKey: pubBase64 } = await generateKeypair(nitDir);
|
|
672
820
|
const publicKeyField = formatPublicKeyField(pubBase64);
|
|
673
821
|
const agentId = deriveAgentId(publicKeyField);
|
|
674
822
|
await saveAgentId(nitDir, agentId);
|
|
675
|
-
const cardPath =
|
|
823
|
+
const cardPath = join6(projDir, CARD_FILE);
|
|
676
824
|
let card;
|
|
677
825
|
let skillsFound = [];
|
|
678
826
|
try {
|
|
679
|
-
const raw = await
|
|
827
|
+
const raw = await fs6.readFile(cardPath, "utf-8");
|
|
680
828
|
card = JSON.parse(raw);
|
|
681
829
|
card.publicKey = publicKeyField;
|
|
682
830
|
skillsFound = card.skills.map((s) => s.id);
|
|
@@ -715,13 +863,18 @@ async function init(options) {
|
|
|
715
863
|
const commitHash = await writeObject(nitDir, "commit", commitContent);
|
|
716
864
|
await setBranch(nitDir, "main", commitHash);
|
|
717
865
|
await setHead(nitDir, "main");
|
|
718
|
-
await
|
|
719
|
-
|
|
866
|
+
await fs6.writeFile(join6(nitDir, "logs", "HEAD"), "", "utf-8");
|
|
867
|
+
const skillsDir = await discoverSkillsDir(projDir);
|
|
868
|
+
await writeConfig(nitDir, {
|
|
869
|
+
remotes: { origin: { url: DEFAULT_API_BASE } },
|
|
870
|
+
skillsDir
|
|
871
|
+
});
|
|
720
872
|
return {
|
|
721
873
|
agentId,
|
|
722
874
|
publicKey: publicKeyField,
|
|
723
875
|
cardUrl: card.url,
|
|
724
|
-
skillsFound
|
|
876
|
+
skillsFound,
|
|
877
|
+
skillsDir
|
|
725
878
|
};
|
|
726
879
|
}
|
|
727
880
|
async function status(options) {
|
|
@@ -790,13 +943,38 @@ async function sign2(message, options) {
|
|
|
790
943
|
}
|
|
791
944
|
async function loginPayload(domain, options) {
|
|
792
945
|
const nitDir = findNitDir(options?.projectDir);
|
|
946
|
+
let switchedBranch;
|
|
947
|
+
let createdSkill;
|
|
948
|
+
const currentBranch = await getCurrentBranch(nitDir);
|
|
949
|
+
if (currentBranch !== domain) {
|
|
950
|
+
const isNew = !await getBranch(nitDir, domain);
|
|
951
|
+
if (isNew) {
|
|
952
|
+
const headHash = await resolveHead(nitDir);
|
|
953
|
+
await setBranch(nitDir, domain, headHash);
|
|
954
|
+
}
|
|
955
|
+
await checkout(domain, options);
|
|
956
|
+
switchedBranch = domain;
|
|
957
|
+
if (isNew) {
|
|
958
|
+
const skillsDir = await getSkillsDir(nitDir);
|
|
959
|
+
if (skillsDir) {
|
|
960
|
+
const skillId = await createSkillTemplate(skillsDir, domain);
|
|
961
|
+
const card = await readWorkingCard(nitDir);
|
|
962
|
+
const hasSkill = card.skills.some((s) => s.id === skillId);
|
|
963
|
+
if (!hasSkill) {
|
|
964
|
+
card.skills.push({ id: skillId });
|
|
965
|
+
await writeWorkingCard(nitDir, card);
|
|
966
|
+
createdSkill = skillId;
|
|
967
|
+
}
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
}
|
|
793
971
|
const agentId = await loadAgentId(nitDir);
|
|
794
972
|
const timestamp = Math.floor(Date.now() / 1e3);
|
|
795
973
|
const message = `${agentId}
|
|
796
974
|
${domain}
|
|
797
975
|
${timestamp}`;
|
|
798
976
|
const signature = await signMessage(nitDir, message);
|
|
799
|
-
return { agent_id: agentId, domain, timestamp, signature };
|
|
977
|
+
return { agent_id: agentId, domain, timestamp, signature, switchedBranch, createdSkill };
|
|
800
978
|
}
|
|
801
979
|
async function commit(message, options) {
|
|
802
980
|
const nitDir = findNitDir(options?.projectDir);
|
|
@@ -912,6 +1090,7 @@ async function checkout(branchName, options) {
|
|
|
912
1090
|
async function push(options) {
|
|
913
1091
|
const nitDir = findNitDir(options?.projectDir);
|
|
914
1092
|
const remoteName = options?.remoteName || "origin";
|
|
1093
|
+
const apiBase = await getRemoteUrl(nitDir, remoteName) || DEFAULT_API_BASE;
|
|
915
1094
|
const branches = await listBranches(nitDir);
|
|
916
1095
|
const currentBranch = await getCurrentBranch(nitDir);
|
|
917
1096
|
const toPush = options?.all ? branches : branches.filter((b) => b.name === currentBranch);
|
|
@@ -925,7 +1104,7 @@ async function push(options) {
|
|
|
925
1104
|
const cardJson = await readObject(nitDir, c.card);
|
|
926
1105
|
const result = await pushBranch(
|
|
927
1106
|
nitDir,
|
|
928
|
-
|
|
1107
|
+
apiBase,
|
|
929
1108
|
b.name,
|
|
930
1109
|
cardJson,
|
|
931
1110
|
b.commitHash
|
|
@@ -939,14 +1118,36 @@ async function push(options) {
|
|
|
939
1118
|
}
|
|
940
1119
|
async function remote(options) {
|
|
941
1120
|
const nitDir = findNitDir(options?.projectDir);
|
|
942
|
-
const
|
|
1121
|
+
const remoteUrl = await getRemoteUrl(nitDir, "origin");
|
|
943
1122
|
const agentId = await loadAgentId(nitDir);
|
|
944
1123
|
return {
|
|
945
1124
|
name: "origin",
|
|
946
|
-
url:
|
|
1125
|
+
url: remoteUrl || DEFAULT_API_BASE,
|
|
947
1126
|
agentId
|
|
948
1127
|
};
|
|
949
1128
|
}
|
|
1129
|
+
async function remoteAdd(name, url, options) {
|
|
1130
|
+
const nitDir = findNitDir(options?.projectDir);
|
|
1131
|
+
const config = await readConfig(nitDir);
|
|
1132
|
+
if (config.remotes[name]) {
|
|
1133
|
+
throw new Error(
|
|
1134
|
+
`Remote "${name}" already exists. Use 'nit remote set-url ${name} <url>' to change it.`
|
|
1135
|
+
);
|
|
1136
|
+
}
|
|
1137
|
+
config.remotes[name] = { url };
|
|
1138
|
+
await writeConfig(nitDir, config);
|
|
1139
|
+
}
|
|
1140
|
+
async function remoteSetUrl(name, url, options) {
|
|
1141
|
+
const nitDir = findNitDir(options?.projectDir);
|
|
1142
|
+
const config = await readConfig(nitDir);
|
|
1143
|
+
if (!config.remotes[name]) {
|
|
1144
|
+
throw new Error(
|
|
1145
|
+
`Remote "${name}" does not exist. Use 'nit remote add ${name} <url>' to create it.`
|
|
1146
|
+
);
|
|
1147
|
+
}
|
|
1148
|
+
config.remotes[name].url = url;
|
|
1149
|
+
await writeConfig(nitDir, config);
|
|
1150
|
+
}
|
|
950
1151
|
|
|
951
1152
|
export {
|
|
952
1153
|
formatPublicKeyField,
|
|
@@ -971,5 +1172,7 @@ export {
|
|
|
971
1172
|
branch,
|
|
972
1173
|
checkout,
|
|
973
1174
|
push,
|
|
974
|
-
remote
|
|
1175
|
+
remote,
|
|
1176
|
+
remoteAdd,
|
|
1177
|
+
remoteSetUrl
|
|
975
1178
|
};
|
package/dist/cli.js
CHANGED
|
@@ -11,9 +11,11 @@ import {
|
|
|
11
11
|
loginPayload,
|
|
12
12
|
push,
|
|
13
13
|
remote,
|
|
14
|
+
remoteAdd,
|
|
15
|
+
remoteSetUrl,
|
|
14
16
|
sign,
|
|
15
17
|
status
|
|
16
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-U6PEH4IJ.js";
|
|
17
19
|
|
|
18
20
|
// src/cli.ts
|
|
19
21
|
var bold = (s) => `\x1B[1m${s}\x1B[0m`;
|
|
@@ -78,6 +80,7 @@ async function cmdInit() {
|
|
|
78
80
|
console.log(` Agent ID: ${green(result.agentId)}`);
|
|
79
81
|
console.log(` Public key: ${dim(result.publicKey)}`);
|
|
80
82
|
console.log(` Card URL: ${result.cardUrl}`);
|
|
83
|
+
console.log(` Skills dir: ${dim(result.skillsDir)}`);
|
|
81
84
|
if (result.skillsFound.length > 0) {
|
|
82
85
|
console.log(` Skills: ${result.skillsFound.join(", ")}`);
|
|
83
86
|
} else {
|
|
@@ -185,6 +188,34 @@ async function cmdPush(args) {
|
|
|
185
188
|
}
|
|
186
189
|
}
|
|
187
190
|
async function cmdRemote(args) {
|
|
191
|
+
const subcommand = args[0];
|
|
192
|
+
if (subcommand === "set-url") {
|
|
193
|
+
const name = args[1];
|
|
194
|
+
const url = args[2];
|
|
195
|
+
if (!name || !url) {
|
|
196
|
+
console.error("Usage: nit remote set-url <name> <url>");
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
199
|
+
await remoteSetUrl(name, url);
|
|
200
|
+
console.log(`Set URL for '${name}' to ${url}`);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
if (subcommand === "add") {
|
|
204
|
+
const name = args[1];
|
|
205
|
+
const url = args[2];
|
|
206
|
+
if (!name || !url) {
|
|
207
|
+
console.error("Usage: nit remote add <name> <url>");
|
|
208
|
+
process.exit(1);
|
|
209
|
+
}
|
|
210
|
+
await remoteAdd(name, url);
|
|
211
|
+
console.log(`Added remote '${green(name)}' \u2192 ${url}`);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (subcommand) {
|
|
215
|
+
console.error(`nit remote: unknown subcommand '${subcommand}'`);
|
|
216
|
+
console.error("Usage: nit remote [set-url <name> <url> | add <name> <url>]");
|
|
217
|
+
process.exit(1);
|
|
218
|
+
}
|
|
188
219
|
const info = await remote();
|
|
189
220
|
console.log(`${bold(info.name)}`);
|
|
190
221
|
console.log(` URL: ${info.url}`);
|
|
@@ -200,7 +231,14 @@ async function cmdSign(args) {
|
|
|
200
231
|
process.exit(1);
|
|
201
232
|
}
|
|
202
233
|
const payload = await loginPayload(domain);
|
|
203
|
-
|
|
234
|
+
if (payload.switchedBranch) {
|
|
235
|
+
console.error(`Switched to branch '${payload.switchedBranch}'`);
|
|
236
|
+
}
|
|
237
|
+
if (payload.createdSkill) {
|
|
238
|
+
console.error(`Created skill template '${payload.createdSkill}'`);
|
|
239
|
+
}
|
|
240
|
+
const { switchedBranch: _s, createdSkill: _c, ...output } = payload;
|
|
241
|
+
console.log(JSON.stringify(output, null, 2));
|
|
204
242
|
return;
|
|
205
243
|
}
|
|
206
244
|
const message = args[0];
|
|
@@ -228,8 +266,10 @@ ${bold("Commands:")}
|
|
|
228
266
|
checkout <branch> Switch branch (overwrites agent-card.json)
|
|
229
267
|
push [--all] Push branch(es) to remote
|
|
230
268
|
sign "message" Sign a message with your Ed25519 key
|
|
231
|
-
sign --login <dom>
|
|
269
|
+
sign --login <dom> Switch to domain branch + generate login payload
|
|
232
270
|
remote Show remote info
|
|
271
|
+
remote add <n> <u> Add a new remote
|
|
272
|
+
remote set-url <n> <u> Change remote URL
|
|
233
273
|
|
|
234
274
|
${bold("Examples:")}
|
|
235
275
|
nit init
|
package/dist/index.d.ts
CHANGED
|
@@ -27,6 +27,8 @@ interface NitHead {
|
|
|
27
27
|
}
|
|
28
28
|
/** Remote configuration for a single named remote. */
|
|
29
29
|
interface NitRemoteConfig {
|
|
30
|
+
/** API base URL (e.g. "https://api.newtype-ai.org") */
|
|
31
|
+
url?: string;
|
|
30
32
|
/** Legacy field — push auth is now via Ed25519 keypair */
|
|
31
33
|
credential?: string;
|
|
32
34
|
}
|
|
@@ -34,6 +36,8 @@ interface NitRemoteConfig {
|
|
|
34
36
|
interface NitConfig {
|
|
35
37
|
/** Keyed by remote name (e.g. "origin") */
|
|
36
38
|
remotes: Record<string, NitRemoteConfig>;
|
|
39
|
+
/** Discovered skills directory path */
|
|
40
|
+
skillsDir?: string;
|
|
37
41
|
}
|
|
38
42
|
/** A2A-compatible agent card. */
|
|
39
43
|
interface AgentCard {
|
|
@@ -54,11 +58,13 @@ interface AgentCard {
|
|
|
54
58
|
url?: string;
|
|
55
59
|
};
|
|
56
60
|
}
|
|
57
|
-
/** A single skill entry in an agent card.
|
|
61
|
+
/** A single skill entry in an agent card.
|
|
62
|
+
* Can be a full skill (all fields) or a pointer (just id).
|
|
63
|
+
* At commit time, pointers are resolved from SKILL.md files. */
|
|
58
64
|
interface AgentCardSkill {
|
|
59
65
|
id: string;
|
|
60
|
-
name
|
|
61
|
-
description
|
|
66
|
+
name?: string;
|
|
67
|
+
description?: string;
|
|
62
68
|
tags?: string[];
|
|
63
69
|
examples?: string[];
|
|
64
70
|
inputModes?: string[];
|
|
@@ -198,6 +204,7 @@ interface InitResult {
|
|
|
198
204
|
publicKey: string;
|
|
199
205
|
cardUrl: string;
|
|
200
206
|
skillsFound: string[];
|
|
207
|
+
skillsDir: string;
|
|
201
208
|
}
|
|
202
209
|
/**
|
|
203
210
|
* Initialize a new nit repository in the project directory.
|
|
@@ -225,12 +232,16 @@ declare function sign(message: string, options?: {
|
|
|
225
232
|
}): Promise<string>;
|
|
226
233
|
/**
|
|
227
234
|
* Generate a login payload for app authentication.
|
|
228
|
-
*
|
|
235
|
+
* Automatically switches to (or creates) the domain branch,
|
|
236
|
+
* then constructs the canonical message ({agent_id}\n{domain}\n{timestamp}),
|
|
229
237
|
* signs it, and returns the full payload ready to send to an app.
|
|
230
238
|
*/
|
|
231
239
|
declare function loginPayload(domain: string, options?: {
|
|
232
240
|
projectDir?: string;
|
|
233
|
-
}): Promise<LoginPayload
|
|
241
|
+
}): Promise<LoginPayload & {
|
|
242
|
+
switchedBranch?: string;
|
|
243
|
+
createdSkill?: string;
|
|
244
|
+
}>;
|
|
234
245
|
/**
|
|
235
246
|
* Snapshot the current agent-card.json as a new commit.
|
|
236
247
|
* Resolves skill pointers from SKILL.md before committing.
|
|
@@ -282,10 +293,22 @@ interface RemoteInfo {
|
|
|
282
293
|
agentId: string;
|
|
283
294
|
}
|
|
284
295
|
/**
|
|
285
|
-
* Show remote info. URL comes from
|
|
296
|
+
* Show remote info. URL comes from .nit/config, agent ID from .nit/identity/.
|
|
286
297
|
*/
|
|
287
298
|
declare function remote(options?: {
|
|
288
299
|
projectDir?: string;
|
|
289
300
|
}): Promise<RemoteInfo>;
|
|
301
|
+
/**
|
|
302
|
+
* Add a new named remote with a URL.
|
|
303
|
+
*/
|
|
304
|
+
declare function remoteAdd(name: string, url: string, options?: {
|
|
305
|
+
projectDir?: string;
|
|
306
|
+
}): Promise<void>;
|
|
307
|
+
/**
|
|
308
|
+
* Change the URL for an existing remote.
|
|
309
|
+
*/
|
|
310
|
+
declare function remoteSetUrl(name: string, url: string, options?: {
|
|
311
|
+
projectDir?: string;
|
|
312
|
+
}): Promise<void>;
|
|
290
313
|
|
|
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 };
|
|
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, log, loginPayload, parsePublicKeyField, push, remote, remoteAdd, remoteSetUrl, sign, signChallenge, signMessage, status, verifySignature };
|
package/dist/index.js
CHANGED
|
@@ -18,12 +18,14 @@ import {
|
|
|
18
18
|
parsePublicKeyField,
|
|
19
19
|
push,
|
|
20
20
|
remote,
|
|
21
|
+
remoteAdd,
|
|
22
|
+
remoteSetUrl,
|
|
21
23
|
sign,
|
|
22
24
|
signChallenge,
|
|
23
25
|
signMessage,
|
|
24
26
|
status,
|
|
25
27
|
verifySignature
|
|
26
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-U6PEH4IJ.js";
|
|
27
29
|
export {
|
|
28
30
|
NIT_NAMESPACE,
|
|
29
31
|
branch,
|
|
@@ -43,6 +45,8 @@ export {
|
|
|
43
45
|
parsePublicKeyField,
|
|
44
46
|
push,
|
|
45
47
|
remote,
|
|
48
|
+
remoteAdd,
|
|
49
|
+
remoteSetUrl,
|
|
46
50
|
sign,
|
|
47
51
|
signChallenge,
|
|
48
52
|
signMessage,
|