@knolo/cli 6.2.2 → 6.2.3
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 -6
- package/bin/knolo.mjs +11 -8
- package/bin/setup.mjs +43 -16
- package/bin/skill.mjs +50 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,8 +13,8 @@ reader.
|
|
|
13
13
|
|
|
14
14
|
`knolo compose`, `knolo attest`, and `knolo verify-attestation` cite local
|
|
15
15
|
Knowledge Images and write an attestation sidecar. This tree's `@knolo/cli`
|
|
16
|
-
package file is `6.2.
|
|
17
|
-
`latest` is `6.2.
|
|
16
|
+
package file is `6.2.3` and depends on `@knolo/core` `^6.2.0`. npm
|
|
17
|
+
`latest` is `6.2.2` until `6.2.3` is published. They do not fetch Hub. Verification
|
|
18
18
|
recomputes `compositionRoot` and checks bundled excerpt bytes. A keyring key
|
|
19
19
|
must fall inside `notBefore`, `notAfter`, and `revokedAt`. `used` means a
|
|
20
20
|
query or evaluation ran. It does not mean `SATISFIED`, and it does not mean a
|
|
@@ -188,9 +188,11 @@ every `publisher/name`. `check` reads a source directory that contains
|
|
|
188
188
|
when `knolo-box` is on `PATH`. It scans `~/.grok` when that directory
|
|
189
189
|
exists, plus every `--root`. It does not require `~/.grok`. It always
|
|
190
190
|
includes `~/.knolo/skills`. An existing `~/.knolo/box/box.config.json` is
|
|
191
|
-
kept and rebuilt. `--reset` replaces it. `--agent
|
|
192
|
-
`--
|
|
193
|
-
|
|
191
|
+
kept and rebuilt. `--reset` replaces it. `--agent`, `--auto-rebuild`,
|
|
192
|
+
`--root`, and the scope paths merge into that file. `--root` may be the
|
|
193
|
+
parent of `agent-data`, or the `agent-data` directory itself. `--agent` sets
|
|
194
|
+
the caller name. `--auto-rebuild` turns on a rebuild at the next search.
|
|
195
|
+
Setup does not prompt,
|
|
194
196
|
install a pack, or call the hub. A missing `knolo-box` is one line, and
|
|
195
197
|
setup still exits 0. It prints the config path, `knolo skill install <publisher>/<name>`,
|
|
196
198
|
and `knolo-box mcp --config <path>`. The install path, auth, trust, and
|
|
@@ -359,7 +361,7 @@ This ensures:
|
|
|
359
361
|
## 🗺 Roadmap
|
|
360
362
|
|
|
361
363
|
The active roadmap is maintained in [`../../docs/ROADMAP.md`](../../docs/ROADMAP.md).
|
|
362
|
-
The published npm release is `6.2.
|
|
364
|
+
The published npm release is `6.2.2`. This package file is `6.2.3` until that version is published. It preserves the V4 CLI, keeps V5 verification,
|
|
363
365
|
migration, diagnostics, and read-only Studio management, and adds offline
|
|
364
366
|
composition, attestation, and `knolo cortex`. Cortex verification does not
|
|
365
367
|
mean an answer is true.
|
package/bin/knolo.mjs
CHANGED
|
@@ -225,9 +225,12 @@ function printCommandHelp(command) {
|
|
|
225
225
|
|
|
226
226
|
Resolves the hub the same way as the other hub commands.
|
|
227
227
|
Scans ~/.grok when that directory exists, plus every --root.
|
|
228
|
+
--root may be the parent of agent-data, or the agent-data directory itself.
|
|
228
229
|
Runs knolo-box init --tolerant, then knolo-box build --config.
|
|
229
230
|
Keeps an existing config and rebuilds it. Pass --reset to replace it.
|
|
231
|
+
--agent, --auto-rebuild, --root, and the scope paths merge into an existing config.
|
|
230
232
|
Always includes ~/.knolo/skills as a skills root.
|
|
233
|
+
When no skill folder is found, setup names the roots it scanned.
|
|
231
234
|
Pass --agent to set the caller name. Pass --auto-rebuild to rebuild on the next search.
|
|
232
235
|
Does not prompt. Does not install a pack or call the hub.
|
|
233
236
|
A missing knolo-box is printed, and setup still exits 0.
|
|
@@ -1902,27 +1905,27 @@ async function main() {
|
|
|
1902
1905
|
return printCommandHelp(command);
|
|
1903
1906
|
if (command === 'kar') {
|
|
1904
1907
|
const { runKarCli } = await import('./kar.mjs');
|
|
1905
|
-
return runKarCli(commandArgs);
|
|
1908
|
+
return await runKarCli(commandArgs);
|
|
1906
1909
|
}
|
|
1907
1910
|
if (command === 'compose' || command === 'attest' || command === 'verify-attestation') {
|
|
1908
1911
|
const { runComposeCli } = await import('./compose.mjs');
|
|
1909
|
-
return runComposeCli(command, commandArgs);
|
|
1912
|
+
return await runComposeCli(command, commandArgs);
|
|
1910
1913
|
}
|
|
1911
1914
|
if (command === 'cortex') {
|
|
1912
1915
|
const { runCortexCli } = await import('./cortex.mjs');
|
|
1913
|
-
return runCortexCli(commandArgs);
|
|
1916
|
+
return await runCortexCli(commandArgs);
|
|
1914
1917
|
}
|
|
1915
1918
|
if (command === 'flight') {
|
|
1916
1919
|
const { runFlightCli } = await import('./flight.mjs');
|
|
1917
|
-
return runFlightCli(commandArgs);
|
|
1920
|
+
return await runFlightCli(commandArgs);
|
|
1918
1921
|
}
|
|
1919
1922
|
if (command === 'skill') {
|
|
1920
1923
|
const { runSkillCli } = await import('./skill.mjs');
|
|
1921
|
-
return runSkillCli(commandArgs);
|
|
1924
|
+
return await runSkillCli(commandArgs);
|
|
1922
1925
|
}
|
|
1923
1926
|
if (command === 'setup') {
|
|
1924
1927
|
const { runKnoloSetup } = await import('./setup.mjs');
|
|
1925
|
-
return runKnoloSetup(commandArgs);
|
|
1928
|
+
return await runKnoloSetup(commandArgs);
|
|
1926
1929
|
}
|
|
1927
1930
|
if (command === 'init') return await cmdInit();
|
|
1928
1931
|
if (command === 'add') return await cmdAdd(commandArgs);
|
|
@@ -1931,8 +1934,8 @@ async function main() {
|
|
|
1931
1934
|
if (command === 'login') return await runHubLogin(commandArgs);
|
|
1932
1935
|
if (command === 'whoami') return await runHubWhoami(commandArgs);
|
|
1933
1936
|
if (command === 'logout') return await runHubLogout(commandArgs);
|
|
1934
|
-
if (command === 'publish') return runHubPublish(commandArgs);
|
|
1935
|
-
if (command === 'yank') return runHubYank(commandArgs);
|
|
1937
|
+
if (command === 'publish') return await runHubPublish(commandArgs);
|
|
1938
|
+
if (command === 'yank') return await runHubYank(commandArgs);
|
|
1936
1939
|
|
|
1937
1940
|
const core = await loadCore();
|
|
1938
1941
|
if (command === 'build') return await cmdBuild(core);
|
package/bin/setup.mjs
CHANGED
|
@@ -16,9 +16,12 @@ export const SETUP_HELP = `Usage: knolo setup [--registry <url>] [--root <dir>]
|
|
|
16
16
|
|
|
17
17
|
Resolves the hub the same way as the other hub commands.
|
|
18
18
|
Scans ~/.grok when that directory exists, plus every --root.
|
|
19
|
+
--root may be the parent of agent-data, or the agent-data directory itself.
|
|
19
20
|
Runs knolo-box init --tolerant, then knolo-box build --config.
|
|
20
21
|
Keeps an existing config and rebuilds it. Pass --reset to replace it.
|
|
22
|
+
--agent, --auto-rebuild, --root, and the scope paths merge into an existing config.
|
|
21
23
|
Always includes ~/.knolo/skills as a skills root.
|
|
24
|
+
When no skill folder is found, setup names the roots it scanned.
|
|
22
25
|
Pass --agent to set the caller name. Pass --auto-rebuild to rebuild on the next search.
|
|
23
26
|
Does not prompt. Does not install a pack or call the hub.
|
|
24
27
|
A missing knolo-box is printed, and setup still exits 0.
|
|
@@ -51,18 +54,16 @@ export async function runKnoloSetup(
|
|
|
51
54
|
for (const root of parsed.roots) roots.push(root);
|
|
52
55
|
|
|
53
56
|
const existing = await fileExists(configPath);
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
`Config already exists: ${configPath}. Pass --reset to replace it.`
|
|
57
|
-
);
|
|
58
|
-
}
|
|
57
|
+
const hubPresent =
|
|
58
|
+
existing && (await configHasHubRoot(configPath, hubSkills, homeDir));
|
|
59
59
|
const replace = !existing || parsed.reset === true;
|
|
60
|
+
const merge = existing && !parsed.reset && (requestsNewConfig(parsed) || !hubPresent);
|
|
60
61
|
|
|
61
62
|
let built = false;
|
|
62
63
|
if (!box) {
|
|
63
64
|
printError('knolo-box is not on PATH. Skipping the local index.');
|
|
64
65
|
if (existing && !parsed.reset) printError('Kept the existing config.');
|
|
65
|
-
} else if (!replace) {
|
|
66
|
+
} else if (existing && !replace && !merge) {
|
|
66
67
|
const buildStatus = await spawnImpl(
|
|
67
68
|
box,
|
|
68
69
|
['build', '--config', configPath],
|
|
@@ -82,6 +83,8 @@ export async function runKnoloSetup(
|
|
|
82
83
|
'--hub-skills',
|
|
83
84
|
hubSkills,
|
|
84
85
|
];
|
|
86
|
+
if (parsed.reset && existing) initArgs.push('--force');
|
|
87
|
+
if (merge) initArgs.push('--merge');
|
|
85
88
|
for (const root of roots) initArgs.push('--root', root);
|
|
86
89
|
if (parsed.agent) initArgs.push('--agent', parsed.agent);
|
|
87
90
|
if (parsed.autoRebuild) initArgs.push('--auto-rebuild');
|
|
@@ -102,8 +105,13 @@ export async function runKnoloSetup(
|
|
|
102
105
|
throw new Error(`knolo-box build exited with status ${buildStatus}.`);
|
|
103
106
|
}
|
|
104
107
|
built = true;
|
|
105
|
-
if (
|
|
108
|
+
if (existing && !parsed.reset) printError('Kept the existing config.');
|
|
109
|
+
if ((await skillRootsBesideHub(configPath, hubSkills, homeDir)).length === 0) {
|
|
106
110
|
printError('No Grok skill folder was found.');
|
|
111
|
+
const scanned = roots.length > 0 ? roots.join(', ') : grokHome;
|
|
112
|
+
printError(
|
|
113
|
+
`Scanned ${scanned}. A root needs to contain agent-data or sand-data, or be the skill folder itself.`
|
|
114
|
+
);
|
|
107
115
|
}
|
|
108
116
|
}
|
|
109
117
|
|
|
@@ -217,7 +225,27 @@ async function directoryExists(candidate) {
|
|
|
217
225
|
}
|
|
218
226
|
}
|
|
219
227
|
|
|
220
|
-
async function
|
|
228
|
+
async function configHasHubRoot(configPath, hubSkills, homeDir) {
|
|
229
|
+
const roots = await skillRoots(configPath);
|
|
230
|
+
const hub = await canonicalDir(hubSkills);
|
|
231
|
+
for (const root of roots) {
|
|
232
|
+
if ((await canonicalDir(expandHome(root, homeDir))) === hub) return true;
|
|
233
|
+
}
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
async function skillRootsBesideHub(configPath, hubSkills, homeDir) {
|
|
238
|
+
const roots = await skillRoots(configPath);
|
|
239
|
+
const hub = await canonicalDir(hubSkills);
|
|
240
|
+
const others = [];
|
|
241
|
+
for (const root of roots) {
|
|
242
|
+
if ((await canonicalDir(expandHome(root, homeDir))) === hub) continue;
|
|
243
|
+
others.push(root);
|
|
244
|
+
}
|
|
245
|
+
return others;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async function skillRoots(configPath) {
|
|
221
249
|
let config;
|
|
222
250
|
try {
|
|
223
251
|
config = JSON.parse(await readFile(configPath, 'utf8'));
|
|
@@ -226,14 +254,13 @@ async function skillRootsBesideHub(configPath, hubSkills) {
|
|
|
226
254
|
}
|
|
227
255
|
const roots = config?.scopes?.skills?.roots;
|
|
228
256
|
if (!Array.isArray(roots)) return [];
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
return others;
|
|
257
|
+
return roots.filter((root) => typeof root === 'string' && root.length > 0);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function expandHome(input, homeDir) {
|
|
261
|
+
if (input === '~') return homeDir;
|
|
262
|
+
if (input.startsWith('~/')) return path.join(homeDir, input.slice(2));
|
|
263
|
+
return input;
|
|
237
264
|
}
|
|
238
265
|
|
|
239
266
|
async function canonicalDir(candidate) {
|
package/bin/skill.mjs
CHANGED
|
@@ -8,7 +8,17 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { spawn } from 'node:child_process';
|
|
10
10
|
import { createHash } from 'node:crypto';
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
chmod,
|
|
13
|
+
lstat,
|
|
14
|
+
mkdir,
|
|
15
|
+
mkdtemp,
|
|
16
|
+
readdir,
|
|
17
|
+
readFile,
|
|
18
|
+
realpath,
|
|
19
|
+
rm,
|
|
20
|
+
writeFile,
|
|
21
|
+
} from 'node:fs/promises';
|
|
12
22
|
import { homedir, tmpdir } from 'node:os';
|
|
13
23
|
import path from 'node:path';
|
|
14
24
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
@@ -341,8 +351,19 @@ async function indexInstalledSkill(destRoot, flags) {
|
|
|
341
351
|
} catch {
|
|
342
352
|
config = null;
|
|
343
353
|
}
|
|
344
|
-
const roots = skillRootsFromConfig(config, env, home);
|
|
345
354
|
const dest = await canonicalDir(destRoot);
|
|
355
|
+
const hubSkills = path.resolve(path.join(home, '.knolo', 'skills'));
|
|
356
|
+
if (config && (await canonicalDir(hubSkills)) === dest) {
|
|
357
|
+
const added = await ensureHubSkillsRoot(configPath, config, dest, env, home);
|
|
358
|
+
if (added) {
|
|
359
|
+
try {
|
|
360
|
+
config = JSON.parse(await readFile(configPath, 'utf8'));
|
|
361
|
+
} catch {
|
|
362
|
+
config = null;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
const roots = skillRootsFromConfig(config, env, home);
|
|
346
367
|
let indexed = false;
|
|
347
368
|
for (const root of roots) {
|
|
348
369
|
if (typeof root !== 'string' || root.length === 0) continue;
|
|
@@ -370,6 +391,33 @@ async function indexInstalledSkill(destRoot, flags) {
|
|
|
370
391
|
}
|
|
371
392
|
}
|
|
372
393
|
|
|
394
|
+
async function ensureHubSkillsRoot(configPath, config, dest, env, home) {
|
|
395
|
+
const roots = skillRootsFromConfig(config, env, home);
|
|
396
|
+
for (const root of roots) {
|
|
397
|
+
if ((await canonicalDir(root)) === dest) return false;
|
|
398
|
+
}
|
|
399
|
+
const next = { ...config };
|
|
400
|
+
const scopes =
|
|
401
|
+
next.scopes && typeof next.scopes === 'object' && !Array.isArray(next.scopes)
|
|
402
|
+
? { ...next.scopes }
|
|
403
|
+
: {};
|
|
404
|
+
const skills =
|
|
405
|
+
scopes.skills && typeof scopes.skills === 'object' && !Array.isArray(scopes.skills)
|
|
406
|
+
? { ...scopes.skills }
|
|
407
|
+
: {};
|
|
408
|
+
const hadRoots = Array.isArray(skills.roots);
|
|
409
|
+
const prior = hadRoots
|
|
410
|
+
? skills.roots.filter((item) => typeof item === 'string' && item.length > 0)
|
|
411
|
+
: roots;
|
|
412
|
+
skills.roots = [...prior, dest];
|
|
413
|
+
scopes.skills = skills;
|
|
414
|
+
next.scopes = scopes;
|
|
415
|
+
await mkdir(path.dirname(configPath), { recursive: true, mode: 0o700 });
|
|
416
|
+
await writeFile(configPath, `${JSON.stringify(next, null, 2)}\n`, { mode: 0o600 });
|
|
417
|
+
await chmod(configPath, 0o600);
|
|
418
|
+
return true;
|
|
419
|
+
}
|
|
420
|
+
|
|
373
421
|
function skillRootsFromConfig(config, env, home) {
|
|
374
422
|
if (!config || typeof config !== 'object' || Array.isArray(config)) return [];
|
|
375
423
|
const fallbackGrok = path.resolve(
|