@aikdna/kdna-cli 0.16.10 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +158 -75
- package/package.json +5 -5
- package/skills/kdna-loader/SKILL.md +5 -6
- package/src/agent.js +489 -79
- package/src/cli.js +112 -62
- package/src/cmds/_common.js +32 -16
- package/src/cmds/badge.js +7 -7
- package/src/cmds/changelog.js +1 -1
- package/src/cmds/cluster.js +16 -48
- package/src/cmds/doctor.js +10 -27
- package/src/cmds/domain.js +213 -443
- package/src/cmds/explain.js +122 -0
- package/src/cmds/legacy.js +8 -8
- package/src/cmds/license.js +483 -26
- package/src/cmds/quality.js +14 -2
- package/src/cmds/registry.js +15 -67
- package/src/cmds/studio.js +4 -5
- package/src/cmds/test.js +4 -4
- package/src/cmds/trace.js +11 -7
- package/src/compare.js +28 -22
- package/src/diff.js +11 -13
- package/src/init.js +2 -2
- package/src/install.js +138 -460
- package/src/loader.js +10 -10
- package/src/package-store.js +229 -0
- package/src/paths.js +44 -0
- package/src/publish.js +184 -22
- package/src/registry.js +76 -9
- package/src/setup.js +19 -20
- package/src/verify.js +275 -121
- package/templates/standard-domain/kdna.json +2 -1
- package/validators/kdna-lint.js +37 -3
- package/validators/kdna-validate.js +3 -2
- package/src/cmds/encrypt.js +0 -199
package/src/registry.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* RegistryResolver — KDNA
|
|
2
|
+
* RegistryResolver — KDNA asset-first registry client.
|
|
3
3
|
*
|
|
4
4
|
* Responsibilities:
|
|
5
5
|
* 1. Resolve names: bare → @aikdna/bare, validate @scope/name format
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* 3. Cache registry metadata locally
|
|
8
8
|
* 4. Surface scope trust info to install/publish
|
|
9
9
|
*
|
|
10
|
-
* Schema
|
|
10
|
+
* Schema v3.0 — see kdna-registry/SCHEMA.md
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
const fs = require('fs');
|
|
@@ -22,6 +22,7 @@ const DEFAULT_OFFICIAL_SCOPE = '@aikdna';
|
|
|
22
22
|
const CANONICAL_REGISTRY_URL =
|
|
23
23
|
process.env.KDNA_REGISTRY_URL ||
|
|
24
24
|
'https://raw.githubusercontent.com/aikdna/kdna-registry/main/domains.json';
|
|
25
|
+
const REQUIRED_SCHEMA_VERSION = '3.0';
|
|
25
26
|
|
|
26
27
|
const NAME_RE = /^@([a-z][a-z0-9-]*)\/([a-z][a-z0-9_]*)$/;
|
|
27
28
|
const BARE_NAME_RE = /^[a-z][a-z0-9_]*$/;
|
|
@@ -39,6 +40,60 @@ function writeJson(file, data) {
|
|
|
39
40
|
fs.writeFileSync(file, JSON.stringify(data, null, 2) + '\n');
|
|
40
41
|
}
|
|
41
42
|
|
|
43
|
+
function parseDate(value) {
|
|
44
|
+
const date = value ? new Date(value) : null;
|
|
45
|
+
return date && !Number.isNaN(date.getTime()) ? date : null;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function registryTrustIssues(registry, { now = new Date() } = {}) {
|
|
49
|
+
const issues = [];
|
|
50
|
+
const trust = registry?.trust || {};
|
|
51
|
+
|
|
52
|
+
if (!registry || registry.schema_version !== REQUIRED_SCHEMA_VERSION) {
|
|
53
|
+
issues.push(
|
|
54
|
+
`Registry schema_version must be ${REQUIRED_SCHEMA_VERSION}, got ${JSON.stringify(registry?.schema_version)}`,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (!trust.model) issues.push('registry.trust.model is required');
|
|
59
|
+
if (!trust.snapshot) issues.push('registry.trust.snapshot is required');
|
|
60
|
+
if (!trust.timestamp) issues.push('registry.trust.timestamp is required');
|
|
61
|
+
|
|
62
|
+
const snapshotVersion = trust.snapshot?.registry_version;
|
|
63
|
+
if (snapshotVersion && snapshotVersion !== registry.registry_version) {
|
|
64
|
+
issues.push(
|
|
65
|
+
`registry.trust.snapshot.registry_version ${snapshotVersion} does not match registry_version ${registry.registry_version}`,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const snapshotExpires = parseDate(trust.snapshot?.expires_at);
|
|
70
|
+
const timestampExpires = parseDate(trust.timestamp?.expires_at);
|
|
71
|
+
if (!snapshotExpires) issues.push('registry.trust.snapshot.expires_at must be an ISO timestamp');
|
|
72
|
+
if (!timestampExpires) issues.push('registry.trust.timestamp.expires_at must be an ISO timestamp');
|
|
73
|
+
if (snapshotExpires && snapshotExpires <= now) {
|
|
74
|
+
issues.push(`registry snapshot expired at ${trust.snapshot.expires_at}`);
|
|
75
|
+
}
|
|
76
|
+
if (timestampExpires && timestampExpires <= now) {
|
|
77
|
+
issues.push(`registry timestamp expired at ${trust.timestamp.expires_at}`);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return issues;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function registryRevocations(registry) {
|
|
84
|
+
return registry?.trust?.revocations || [];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function isEntryRevoked(registry, entry) {
|
|
88
|
+
const revocations = registryRevocations(registry);
|
|
89
|
+
return revocations.find((rev) => {
|
|
90
|
+
if (rev.name && rev.name !== entry.name) return false;
|
|
91
|
+
if (rev.version && rev.version !== entry.version) return false;
|
|
92
|
+
if (rev.asset_digest && rev.asset_digest !== entry.asset_digest) return false;
|
|
93
|
+
return rev.name || rev.asset_digest;
|
|
94
|
+
}) || null;
|
|
95
|
+
}
|
|
96
|
+
|
|
42
97
|
// ─── Name parsing ───────────────────────────────────────────────────────
|
|
43
98
|
|
|
44
99
|
/**
|
|
@@ -151,7 +206,15 @@ class RegistryResolver {
|
|
|
151
206
|
_loadRegistryForScope(scopeName) {
|
|
152
207
|
if (this._registries.has(scopeName)) return this._registries.get(scopeName);
|
|
153
208
|
const source = this._sourceForScope(scopeName);
|
|
154
|
-
|
|
209
|
+
let data = source.load({ allowNetwork: this.allowNetwork, refresh: this.refresh });
|
|
210
|
+
let trustIssues = data ? registryTrustIssues(data) : [];
|
|
211
|
+
if (trustIssues.length && this.allowNetwork && !this.refresh) {
|
|
212
|
+
data = source.load({ allowNetwork: true, refresh: true });
|
|
213
|
+
trustIssues = data ? registryTrustIssues(data) : [];
|
|
214
|
+
}
|
|
215
|
+
if (trustIssues.length) {
|
|
216
|
+
throw new Error(`Registry trust check failed:\n${trustIssues.map((i) => `- ${i}`).join('\n')}`);
|
|
217
|
+
}
|
|
155
218
|
this._registries.set(scopeName, data);
|
|
156
219
|
return data;
|
|
157
220
|
}
|
|
@@ -186,12 +249,6 @@ class RegistryResolver {
|
|
|
186
249
|
);
|
|
187
250
|
}
|
|
188
251
|
|
|
189
|
-
if (registry.schema_version && registry.schema_version !== '2.0') {
|
|
190
|
-
throw new Error(
|
|
191
|
-
`Registry schema_version ${registry.schema_version} not supported. This CLI requires 2.0.`,
|
|
192
|
-
);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
252
|
const scope = registry.scopes?.[parsed.scope];
|
|
196
253
|
if (!scope) {
|
|
197
254
|
throw new Error(`Scope ${parsed.scope} not registered in registry.`);
|
|
@@ -215,6 +272,13 @@ class RegistryResolver {
|
|
|
215
272
|
throw new Error(`${entry.name}@${entry.version} has been yanked${when}.${reason}${replace}`);
|
|
216
273
|
}
|
|
217
274
|
|
|
275
|
+
const revocation = isEntryRevoked(registry, entry);
|
|
276
|
+
if (revocation) {
|
|
277
|
+
const reason = revocation.reason ? `\nReason: ${revocation.reason}` : '';
|
|
278
|
+
const when = revocation.revoked_at ? ` (revoked ${revocation.revoked_at.slice(0, 10)})` : '';
|
|
279
|
+
throw new Error(`${entry.name}@${entry.version} has been revoked${when}.${reason}`);
|
|
280
|
+
}
|
|
281
|
+
|
|
218
282
|
return { parsed, scope, entry, registry };
|
|
219
283
|
}
|
|
220
284
|
|
|
@@ -250,6 +314,9 @@ function fetchRegistry() {
|
|
|
250
314
|
module.exports = {
|
|
251
315
|
RegistryResolver,
|
|
252
316
|
parseName,
|
|
317
|
+
REQUIRED_SCHEMA_VERSION,
|
|
318
|
+
registryTrustIssues,
|
|
319
|
+
isEntryRevoked,
|
|
253
320
|
loadRegistry,
|
|
254
321
|
fetchRegistry,
|
|
255
322
|
CANONICAL_REGISTRY_URL,
|
package/src/setup.js
CHANGED
|
@@ -15,9 +15,11 @@
|
|
|
15
15
|
const fs = require('fs');
|
|
16
16
|
const path = require('path');
|
|
17
17
|
|
|
18
|
-
const
|
|
19
|
-
|
|
20
|
-
const
|
|
18
|
+
const PATHS = require('./paths');
|
|
19
|
+
|
|
20
|
+
const USER_KDNA_DIR = PATHS.root;
|
|
21
|
+
const DOMAINS_DIR = PATHS.domains.root;
|
|
22
|
+
const CLUSTERS_DIR = PATHS.clusters;
|
|
21
23
|
const SKILLS_REPO = 'https://raw.githubusercontent.com/aikdna/kdna-skills/main';
|
|
22
24
|
|
|
23
25
|
const AGENTS = [
|
|
@@ -119,15 +121,23 @@ async function cmdSetup() {
|
|
|
119
121
|
const pkg = require(path.join(__dirname, '..', 'package.json'));
|
|
120
122
|
log(`KDNA CLI v${pkg.version}`);
|
|
121
123
|
|
|
122
|
-
// 2. KDNA data root
|
|
123
|
-
ensureDir(
|
|
124
|
+
// 2. KDNA data root — .kdna asset store
|
|
125
|
+
ensureDir(PATHS.root);
|
|
126
|
+
ensureDir(PATHS.packages);
|
|
124
127
|
ensureDir(CLUSTERS_DIR);
|
|
128
|
+
ensureDir(PATHS.registry);
|
|
129
|
+
ensureDir(PATHS.traces);
|
|
130
|
+
ensureDir(PATHS.feedback);
|
|
131
|
+
ensureDir(PATHS.evals);
|
|
132
|
+
ensureDir(PATHS.cache);
|
|
133
|
+
ensureDir(PATHS.identity);
|
|
134
|
+
ensureDir(PATHS.licenses);
|
|
125
135
|
log(`Data root: ${USER_KDNA_DIR}/`);
|
|
126
136
|
|
|
127
|
-
// 2b.
|
|
137
|
+
// 2b. Directory installs are not part of the runtime model.
|
|
128
138
|
if (fs.existsSync(DOMAINS_DIR)) {
|
|
129
139
|
const legacy = fs.readdirSync(DOMAINS_DIR).filter((e) => {
|
|
130
|
-
if (e.startsWith('
|
|
140
|
+
if (e.startsWith('.')) return false;
|
|
131
141
|
try {
|
|
132
142
|
return fs.statSync(path.join(DOMAINS_DIR, e)).isDirectory();
|
|
133
143
|
} catch {
|
|
@@ -136,19 +146,8 @@ async function cmdSetup() {
|
|
|
136
146
|
});
|
|
137
147
|
if (legacy.length) {
|
|
138
148
|
console.log('');
|
|
139
|
-
warn(
|
|
140
|
-
|
|
141
|
-
);
|
|
142
|
-
for (const d of legacy) {
|
|
143
|
-
const dPath = path.join(DOMAINS_DIR, d);
|
|
144
|
-
try {
|
|
145
|
-
fs.rmSync(dPath, { recursive: true, force: true });
|
|
146
|
-
log(` removed ~/.kdna/domains/${d}/`);
|
|
147
|
-
} catch (e) {
|
|
148
|
-
warn(` could not remove ~/.kdna/domains/${d}/ — ${e.message}`);
|
|
149
|
-
console.log(` To remove manually: rm -rf ~/.kdna/domains/${d}/`);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
149
|
+
warn(`Ignoring ${legacy.length} legacy domain director${legacy.length > 1 ? 'ies' : 'y'}.`);
|
|
150
|
+
console.log(' Runtime assets now live under ~/.kdna/packages/ as .kdna files.');
|
|
152
151
|
console.log(' Re-install with: kdna install @aikdna/<name>');
|
|
153
152
|
console.log('');
|
|
154
153
|
}
|