@knolo/cli 5.5.0 → 6.0.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/bin/knolo.mjs CHANGED
@@ -72,6 +72,7 @@ const SUBCOMMANDS = new Set([
72
72
  'semantic:index',
73
73
  'semantic:inspect',
74
74
  'semantic:validate',
75
+ 'kar',
75
76
  ]);
76
77
  const ICP_SUBCOMMANDS = new Set([
77
78
  'init',
@@ -128,6 +129,18 @@ async function loadCore() {
128
129
  );
129
130
  }
130
131
 
132
+ function printVersion() {
133
+ const cli = JSON.parse(readFileSync(path.join(__dirname, '../package.json'), 'utf8'));
134
+ let coreVersion = 'unavailable';
135
+ try {
136
+ coreVersion = JSON.parse(readFileSync(require.resolve('@knolo/core/package.json'), 'utf8')).version;
137
+ } catch {
138
+ coreVersion = 'unavailable';
139
+ }
140
+ console.log(`@knolo/cli ${cli.version}`);
141
+ console.log(`@knolo/core ${coreVersion}`);
142
+ }
143
+
131
144
  function printRootHelp() {
132
145
  console.log(`KnoLo CLI
133
146
 
@@ -146,7 +159,7 @@ Commands:
146
159
  publish Publish a pack with a Hub Bearer token
147
160
  yank <pack>@<version> Yank a released pack with a Hub Bearer token
148
161
  build Build a .knolo pack from configured sources
149
- query <question> Query a built pack and print top hits
162
+ query <question|EQL> Query a V4 pack or a V5 image using its declared contract
150
163
  inspect <pack> Inspect pack format, sections, and manifest
151
164
  migrate <pack> Migrate a legacy pack to v4
152
165
  verify <pack> Verify pack structure and cryptographic digests
@@ -154,11 +167,13 @@ Commands:
154
167
  diff <pack-a> <pack-b> Compare pack identities and source/chunk counts
155
168
  dev Watch config/sources and rebuild on change
156
169
  v5 Inspect, compress, decompress, and health-check a V5 Knowledge Image
170
+ kar KAR evidence-set retrieval over a Committed Evidence Graph
157
171
  icp Scaffold and operate an ICP-native knowledge canister
158
172
 
159
173
  Global options:
160
174
  --debug Print stack traces for errors
161
175
  -h, --help Show help
176
+ -v, --version Print the CLI and @knolo/core versions
162
177
 
163
178
  Run "knolo <command> --help" for command details.`);
164
179
  }
@@ -179,7 +194,7 @@ function printCommandHelp(command) {
179
194
  yank: 'Usage: knolo yank <publisher>/<slug>@<version> [--json] [--registry <url>]\n\nUses the stored token as Authorization: Bearer kno_…. Yank is owner-only and leaves the public Blob in place.',
180
195
  build: 'Usage: knolo build',
181
196
  query:
182
- 'Usage: knolo query <question> [--pack <path>] [--k <number>] [--receipt <file>] [--json]',
197
+ 'Usage: knolo query <question|EQL> [--pack <path>] [--k <number>] [--receipt <file>] [--json]\n\nV4 packs accept natural-language queries. V5 images require bounded EQL such as: FROM chunk SEARCH "billing" LIMIT 5.',
183
198
  inspect: 'Usage: knolo inspect <pack.knolo>',
184
199
  migrate:
185
200
  'Usage: knolo migrate <pack.knolo> --out <new-pack.knolo> [--to 4]',
@@ -187,7 +202,8 @@ function printCommandHelp(command) {
187
202
  explain: 'Usage: knolo explain <receipt.json> --pack <pack.knolo>',
188
203
  diff: 'Usage: knolo diff <pack-a.knolo> <pack-b.knolo>',
189
204
  dev: 'Usage: knolo dev',
190
- v5: 'Usage: knolo v5 <info|health|studio|compress|decompress> <image.v5> [--out <file>] [--mode fast|balanced|max] [--attach-index]',
205
+ v5: 'Usage: knolo v5 <info|health|studio|query|compress|decompress> <image.v5> [--out <file>] [--mode fast|balanced|max] [--attach-index]\n\nFor query: knolo v5 query <image.v5> <EQL> [--json] [--receipt <file>].',
206
+ kar: 'Usage: knolo kar <evaluate|verify|explain|inspect|graph|package|produce|domain|dossier|coverage|benchmark|demo|conformance> [options]\n\nKnolo Adversarial Retrieval. Semantics are frozen at kar-1-research-1.\n\n knolo kar evaluate --image knowledge.knolo --graph knowledge.kar.json --plan plan.json --query "..."\n knolo kar verify --image knowledge.knolo --graph knowledge.kar.json --plan plan.json --query "..." --result result.json\n knolo kar explain --result result.json\n knolo kar inspect --image knowledge.knolo --graph knowledge.kar.json [--plan plan.json]\n knolo kar graph build --image knowledge.knolo --source knowledge.ceg.yaml --out knowledge.kar.json\n knolo kar package --image knowledge.knolo --graph knowledge.kar.json --out dist/my-knowledge-kar/\n knolo kar produce rules --image knowledge.knolo --domain domains/contracts-v4 --out proposals.json\n knolo kar domain validate domains/contracts-v4\n knolo kar dossier build --image knowledge.knolo --graph knowledge.kar.json --plan plan.json --query "..." --out dossier/\n knolo kar dossier verify dossier/\n knolo kar coverage build --image knowledge.knolo --graph knowledge.kar.json --plan plan.json --query "..." --out coverage/\n knolo kar coverage verify coverage/\n knolo kar benchmark annotate --packet packet.json --decisions decisions.json --out labels.json --role human --reviewer-id <id>\n knolo kar demo\n knolo kar conformance\n\n--json prints the machine-readable result. verify exits non-zero when the certificate does not match. Abstention is a result and exits 0. graph and package author a Committed Evidence Graph without changing retrieval. produce proposes CEG Source and does not write committed bytes. dossier packages one evaluation and fails closed when committed fields change. coverage reports met requirements, gaps, and plans that did not decide. A gap is not a false claim. benchmark labels gold without showing producer output. demo and conformance run from a knolo-core checkout.',
191
207
  icp: 'Usage: knolo icp <command> [options]',
192
208
  'semantic:index':
193
209
  'Usage: knolo semantic:index --pack <path> [--out <path>] [--model <id>] [--endpoint <url>]',
@@ -288,6 +304,8 @@ function parseArgv(argv) {
288
304
  }
289
305
  if (args[0] === '--help' || args[0] === '-h')
290
306
  return { global, command: 'help', commandArgs: [] };
307
+ if (args[0] === '--version' || args[0] === '-v')
308
+ return { global, command: 'version', commandArgs: [] };
291
309
  return { global, command: args[0] || 'help', commandArgs: args.slice(1) };
292
310
  }
293
311
 
@@ -877,6 +895,13 @@ async function cmdQuery(core, args) {
877
895
 
878
896
  const packBuffer = readFileSync(packPath);
879
897
  const bytes = Uint8Array.from(packBuffer);
898
+ if (core.isKnowledgeImageV5?.(bytes)) {
899
+ if (opts.k !== undefined)
900
+ throw createError(
901
+ 'V5 queries use EQL LIMIT. Put LIMIT in the query instead of --k.'
902
+ );
903
+ return await cmdQueryV5(core, bytes, packPath, question, opts);
904
+ }
880
905
  const kb = await mountPackFromBytes(core, bytes);
881
906
  const receiptResult =
882
907
  opts.receipt && core.queryWithReceipt
@@ -936,6 +961,66 @@ async function cmdQuery(core, args) {
936
961
  });
937
962
  }
938
963
 
964
+ async function cmdQueryV5(core, bytes, imagePath, expression, opts) {
965
+ const image = core.mountKnowledgeImageV5(bytes);
966
+ const result = core.queryKnowledgeImageV5(image, expression);
967
+ if (opts.receipt) {
968
+ writeFileSync(
969
+ path.resolve(process.cwd(), opts.receipt),
970
+ `${JSON.stringify(result, null, 2)}\n`
971
+ );
972
+ }
973
+ const objects = new Map(image.objects.map((object) => [object.id, object]));
974
+ const hits = result.hits.map((hit) => {
975
+ const object = objects.get(hit.objectId);
976
+ const text = object
977
+ ? new TextDecoder()
978
+ .decode(object.bytes)
979
+ .replace(/\s+/gu, ' ')
980
+ .slice(0, 200)
981
+ : '';
982
+ const metadata = object?.meta ?? {};
983
+ const source =
984
+ typeof metadata.source === 'string'
985
+ ? metadata.source
986
+ : typeof metadata.docId === 'string'
987
+ ? metadata.docId
988
+ : hit.objectId;
989
+ return {
990
+ objectId: hit.objectId,
991
+ kind: hit.kind,
992
+ source,
993
+ snippet: text,
994
+ joinedObjectIds: hit.joinedObjectIds,
995
+ };
996
+ });
997
+ const payload = {
998
+ contract: 'v5-knowledge-image',
999
+ query: expression,
1000
+ imagePath,
1001
+ stateRoot: result.stateRoot,
1002
+ planRoot: result.planRoot,
1003
+ resultRoot: result.resultRoot,
1004
+ plan: result.plan,
1005
+ hits,
1006
+ queryResult: result,
1007
+ };
1008
+ if (opts.json) {
1009
+ console.log(JSON.stringify(payload, null, 2));
1010
+ return;
1011
+ }
1012
+ if (!hits.length) {
1013
+ console.log('No V5 hits found.');
1014
+ return;
1015
+ }
1016
+ console.log(`V5 query returned ${hits.length} hit(s):`);
1017
+ hits.forEach((hit, index) => {
1018
+ console.log(`\n${index + 1}. ${hit.kind} ${hit.objectId}`);
1019
+ console.log(` source: ${hit.source}`);
1020
+ console.log(` snippet: ${hit.snippet}`);
1021
+ });
1022
+ }
1023
+
939
1024
  function parsePackCommandArgs(args, command) {
940
1025
  const positional = [];
941
1026
  const flags = {};
@@ -961,6 +1046,24 @@ async function cmdInspect(core, args) {
961
1046
  `Pack file not found at ${path.relative(process.cwd(), packPath)}.`
962
1047
  );
963
1048
  const bytes = Uint8Array.from(readFileSync(packPath));
1049
+ if (core.isKnowledgeImageV5?.(bytes)) {
1050
+ const image = core.mountKnowledgeImageV5(bytes);
1051
+ console.log(
1052
+ JSON.stringify(
1053
+ {
1054
+ format: 'v5-knowledge-image',
1055
+ version: 5,
1056
+ stateRoot: image.stateRoot,
1057
+ commitDigest: image.commitDigest,
1058
+ objects: image.objects.length,
1059
+ events: image.events.length,
1060
+ },
1061
+ null,
1062
+ 2
1063
+ )
1064
+ );
1065
+ return;
1066
+ }
964
1067
  const pack = await mountPackFromBytes(core, bytes);
965
1068
  const result = {
966
1069
  format:
@@ -988,6 +1091,23 @@ async function cmdVerify(core, args) {
988
1091
  `Pack file not found at ${path.relative(process.cwd(), packPath)}.`
989
1092
  );
990
1093
  const bytes = Uint8Array.from(readFileSync(packPath));
1094
+ if (core.isKnowledgeImageV5?.(bytes)) {
1095
+ const verification = core.verifyKnowledgeImageV5(bytes);
1096
+ console.log(
1097
+ JSON.stringify(
1098
+ {
1099
+ verified: verification.valid,
1100
+ format: 'v5-knowledge-image',
1101
+ version: 5,
1102
+ stateRoot: verification.stateRoot,
1103
+ commitDigest: verification.commitDigest,
1104
+ },
1105
+ null,
1106
+ 2
1107
+ )
1108
+ );
1109
+ return;
1110
+ }
991
1111
  const pack = await mountPackFromBytes(core, bytes);
992
1112
  console.log(
993
1113
  JSON.stringify(
@@ -1043,16 +1163,47 @@ async function cmdExplain(core, args) {
1043
1163
  const receiptPath = positional[0];
1044
1164
  if (!receiptPath || (!flags.out && !flags.pack))
1045
1165
  throw createError(
1046
- 'Usage: knolo explain <receipt.json> --pack <pack.knolo>'
1166
+ 'Usage: knolo explain <receipt.json> --pack <pack.knolo|image.v5>'
1047
1167
  );
1048
1168
  const packPath = path.resolve(process.cwd(), flags.pack);
1049
1169
  const receipt = JSON.parse(
1050
1170
  readFileSync(path.resolve(process.cwd(), receiptPath), 'utf8')
1051
1171
  );
1052
- const pack = await mountPackFromBytes(
1053
- core,
1054
- Uint8Array.from(readFileSync(packPath))
1055
- );
1172
+ const bytes = Uint8Array.from(readFileSync(packPath));
1173
+ if (core.isKnowledgeImageV5?.(bytes)) {
1174
+ core.verifyKnowledgeQueryResultV5(bytes, receipt);
1175
+ const image = core.mountKnowledgeImageV5(bytes);
1176
+ const objects = new Map(image.objects.map((object) => [object.id, object]));
1177
+ console.log(
1178
+ JSON.stringify(
1179
+ {
1180
+ verified: true,
1181
+ contract: 'v5-knowledge-image',
1182
+ query: receipt.plan,
1183
+ stateRoot: receipt.stateRoot,
1184
+ planRoot: receipt.planRoot,
1185
+ resultRoot: receipt.resultRoot,
1186
+ hits: receipt.hits.map((hit) => {
1187
+ const object = objects.get(hit.objectId);
1188
+ return {
1189
+ objectId: hit.objectId,
1190
+ kind: hit.kind,
1191
+ source:
1192
+ typeof object?.meta?.source === 'string'
1193
+ ? object.meta.source
1194
+ : typeof object?.meta?.docId === 'string'
1195
+ ? object.meta.docId
1196
+ : undefined,
1197
+ };
1198
+ }),
1199
+ },
1200
+ null,
1201
+ 2
1202
+ )
1203
+ );
1204
+ return;
1205
+ }
1206
+ const pack = await mountPackFromBytes(core, bytes);
1056
1207
  core.verifyReceipt(receipt, pack);
1057
1208
  console.log(
1058
1209
  JSON.stringify(
@@ -1120,6 +1271,8 @@ function parseV5Args(args) {
1120
1271
  for (let i = 0; i < args.length; i++) {
1121
1272
  const arg = args[i];
1122
1273
  if (arg === '--attach-index') flags.attachIndex = true;
1274
+ else if (arg === '--json') flags.json = true;
1275
+ else if (arg === '--receipt') flags.receipt = args[++i];
1123
1276
  else if (
1124
1277
  arg === '--image' ||
1125
1278
  arg === '--index' ||
@@ -1139,17 +1292,40 @@ function parseV5Args(args) {
1139
1292
 
1140
1293
  async function cmdV5(core, args) {
1141
1294
  const subcommand = args[0] || 'info';
1142
- if (
1143
- !['info', 'health', 'studio', 'compress', 'decompress'].includes(subcommand)
1144
- )
1295
+ const subcommands = [
1296
+ 'info',
1297
+ 'health',
1298
+ 'studio',
1299
+ 'query',
1300
+ 'compress',
1301
+ 'decompress',
1302
+ ];
1303
+ if (!subcommands.includes(subcommand))
1145
1304
  throw createError(`Unknown V5 command: ${subcommand}`);
1146
1305
  const { positional, flags } = parseV5Args(
1147
- args.slice(
1148
- ['info', 'health', 'studio', 'compress', 'decompress'].includes(args[0])
1149
- ? 1
1150
- : 0
1151
- )
1306
+ args.slice(subcommands.includes(args[0]) ? 1 : 0)
1152
1307
  );
1308
+ if (subcommand === 'query') {
1309
+ const imagePath = path.resolve(
1310
+ process.cwd(),
1311
+ flags.image || positional[0] || 'dist/knowledge.v5'
1312
+ );
1313
+ if (!existsSync(imagePath))
1314
+ throw createError(
1315
+ `V5 image file not found at ${path.relative(process.cwd(), imagePath)}.`
1316
+ );
1317
+ const expression = (flags.image ? positional : positional.slice(1))
1318
+ .join(' ')
1319
+ .trim();
1320
+ if (!expression)
1321
+ throw createError(
1322
+ 'Usage: knolo v5 query <image.v5> <EQL> [--json] [--receipt <file>]'
1323
+ );
1324
+ const bytes = Uint8Array.from(readFileSync(imagePath));
1325
+ if (!core.isKnowledgeImageV5?.(bytes))
1326
+ throw createError('V5 query requires a V5 Knowledge Image input.');
1327
+ return await cmdQueryV5(core, bytes, imagePath, expression, flags);
1328
+ }
1153
1329
  const imagePath = path.resolve(
1154
1330
  process.cwd(),
1155
1331
  flags.image || positional[0] || 'dist/knowledge.v5'
@@ -1282,6 +1458,10 @@ async function cmdSemanticIndex(core, args) {
1282
1458
  );
1283
1459
 
1284
1460
  const bytes = Uint8Array.from(readFileSync(packPath));
1461
+ if (core.isKnowledgeImageV5?.(bytes))
1462
+ throw createError(
1463
+ 'semantic:index targets V4 packs. Query V5 images with bounded EQL through knolo query.'
1464
+ );
1285
1465
  const pack = await mountPackFromBytes(core, bytes);
1286
1466
  const OllamaEmbeddingProvider = await loadOllamaProvider();
1287
1467
  const provider = new OllamaEmbeddingProvider({ modelId, endpoint });
@@ -1332,10 +1512,12 @@ async function cmdSemanticValidate(core, args) {
1332
1512
  const sidecarPath = path.resolve(process.cwd(), flags.sidecar);
1333
1513
  const modelId = flags.model;
1334
1514
  if (!modelId) throw createError('semantic:validate requires --model <id>.');
1335
- const pack = await mountPackFromBytes(
1336
- core,
1337
- Uint8Array.from(readFileSync(packPath))
1338
- );
1515
+ const packBytes = Uint8Array.from(readFileSync(packPath));
1516
+ if (core.isKnowledgeImageV5?.(packBytes))
1517
+ throw createError(
1518
+ 'semantic:validate targets V4 packs. Query V5 images with bounded EQL through knolo query.'
1519
+ );
1520
+ const pack = await mountPackFromBytes(core, packBytes);
1339
1521
  const sidecar = core.parseSidecar(readFileSync(sidecarPath, 'utf8'));
1340
1522
  core.validateSidecarForPack({ sidecar, pack, modelId });
1341
1523
  if (sidecar.blocks.length !== pack.blocks.length)
@@ -1652,6 +1834,7 @@ async function main() {
1652
1834
  const { global, command, commandArgs } = parseArgv(process.argv.slice(2));
1653
1835
  try {
1654
1836
  if (command === 'help') return printRootHelp();
1837
+ if (command === 'version') return printVersion();
1655
1838
  if (command === 'icp') {
1656
1839
  if (
1657
1840
  commandArgs.length === 0 ||
@@ -1666,6 +1849,10 @@ async function main() {
1666
1849
  if (SUBCOMMANDS.has(command)) {
1667
1850
  if (commandArgs.includes('--help') || commandArgs.includes('-h'))
1668
1851
  return printCommandHelp(command);
1852
+ if (command === 'kar') {
1853
+ const { runKarCli } = await import('./kar.mjs');
1854
+ return runKarCli(commandArgs);
1855
+ }
1669
1856
  if (command === 'init') return await cmdInit();
1670
1857
  if (command === 'add') return await cmdAdd(commandArgs);
1671
1858
  if (command === 'search') return await runHubSearch(commandArgs);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@knolo/cli",
3
- "version": "5.5.0",
3
+ "version": "6.0.0",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "knolo": "bin/knolo.mjs"
@@ -9,7 +9,7 @@
9
9
  "bin/",
10
10
  "templates/"
11
11
  ],
12
- "description": "CLI for building, inspecting, verifying, and operating Knolo V4 packs and V5 Knowledge Images.",
12
+ "description": "CLI for Knolo V4 packs, V5 Knowledge Images, and KAR evidence-set retrieval.",
13
13
  "license": "Apache-2.0",
14
14
  "repository": {
15
15
  "type": "git",
@@ -28,11 +28,11 @@
28
28
  },
29
29
  "scripts": {
30
30
  "build": "node -e \"console.log('cli is executable via bin/knolo.mjs')\"",
31
- "test": "npm run build --prefix ../core && node --test test/cli.test.mjs test/hub.integration.test.mjs",
31
+ "test": "npm run build --prefix ../core && node --test test/cli.test.mjs test/kar.test.mjs test/kar-graph.test.mjs test/kar-produce.test.mjs test/hub.integration.test.mjs",
32
32
  "test:hub": "node --test test/hub.integration.test.mjs"
33
33
  },
34
34
  "dependencies": {
35
- "@knolo/core": "^5.5.0"
35
+ "@knolo/core": "^6.0.0"
36
36
  },
37
37
  "private": false
38
38
  }
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "knolo-core-rust"
3
- version = "5.5.0"
3
+ version = "5.5.1"
4
4
  edition = "2021"
5
5
  license = "Apache-2.0"
6
6
  description = "Rust runtime support for reading and querying .knolo packs"
@@ -245,11 +245,11 @@ dependencies = [
245
245
 
246
246
  [[package]]
247
247
  name = "knolo-core-rust"
248
- version = "5.5.0"
248
+ version = "5.5.1"
249
249
 
250
250
  [[package]]
251
251
  name = "knolo-icp-canister"
252
- version = "5.5.0"
252
+ version = "5.5.1"
253
253
  dependencies = [
254
254
  "candid",
255
255
  "ic-cdk",
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "knolo-icp-canister"
3
- version = "5.5.0"
3
+ version = "5.5.1"
4
4
  edition = "2021"
5
5
  license = "Apache-2.0"
6
6
  description = "ICP canister adapter for Knolo retrieval over Candid"
@@ -16,5 +16,5 @@ path = "src/lib.rs"
16
16
  candid = "0.10"
17
17
  ic-cdk = "0.17"
18
18
  ic-cdk-macros = "0.17"
19
- knolo-core-rust = { path = "../knolo-core-rust", version = "5.5.0" }
19
+ knolo-core-rust = { path = "../knolo-core-rust", version = "5.5.1" }
20
20
  serde = { version = "1", features = ["derive"] }