@knolo/cli 5.5.0 → 5.5.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 +18 -2
- package/bin/knolo.mjs +184 -20
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# @knolo/cli
|
|
2
2
|
|
|
3
|
-
The CLI preserves the Knolo V4 pack workflow and adds
|
|
4
|
-
Image inspection. Normal builds emit V4; the ICP legacy build path explicitly
|
|
3
|
+
The CLI preserves the Knolo V4 pack workflow and adds explicit V5 Knowledge
|
|
4
|
+
Image inspection and querying. Normal builds emit V4; the ICP legacy build path explicitly
|
|
5
5
|
emits V3 until the canister gains a V4 profile. V5 Studio output is a verified
|
|
6
6
|
management snapshot, not a mutation interface.
|
|
7
7
|
|
|
@@ -134,6 +134,22 @@ knolo explain receipt.json --pack dist/knowledge.knolo
|
|
|
134
134
|
knolo diff old.knolo new.knolo
|
|
135
135
|
```
|
|
136
136
|
|
|
137
|
+
`knolo build` produces a V4 pack, so its natural-language query and receipt
|
|
138
|
+
contract applies to that artifact. V5 images are verified Knowledge Images and
|
|
139
|
+
use bounded EQL with roots-bound query results:
|
|
140
|
+
|
|
141
|
+
```bash
|
|
142
|
+
knolo query 'FROM chunk SEARCH "billing" LIMIT 5' \
|
|
143
|
+
--pack ./dist/knowledge.v5 --json --receipt ./query-result.json
|
|
144
|
+
knolo v5 query ./dist/knowledge.v5 \
|
|
145
|
+
'FROM chunk SEARCH "billing" LIMIT 5' --json
|
|
146
|
+
knolo explain ./query-result.json --pack ./dist/knowledge.v5
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
V5 images are never mounted through the V4 pack query API. Semantic sidecars
|
|
150
|
+
currently target V4 packs and are rejected for V5 images with a contract-specific
|
|
151
|
+
message.
|
|
152
|
+
|
|
137
153
|
V5 runtime diagnostics:
|
|
138
154
|
|
|
139
155
|
```bash
|
package/bin/knolo.mjs
CHANGED
|
@@ -146,7 +146,7 @@ Commands:
|
|
|
146
146
|
publish Publish a pack with a Hub Bearer token
|
|
147
147
|
yank <pack>@<version> Yank a released pack with a Hub Bearer token
|
|
148
148
|
build Build a .knolo pack from configured sources
|
|
149
|
-
query <question>
|
|
149
|
+
query <question|EQL> Query a V4 pack or a V5 image using its declared contract
|
|
150
150
|
inspect <pack> Inspect pack format, sections, and manifest
|
|
151
151
|
migrate <pack> Migrate a legacy pack to v4
|
|
152
152
|
verify <pack> Verify pack structure and cryptographic digests
|
|
@@ -179,7 +179,7 @@ function printCommandHelp(command) {
|
|
|
179
179
|
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
180
|
build: 'Usage: knolo build',
|
|
181
181
|
query:
|
|
182
|
-
'Usage: knolo query <question> [--pack <path>] [--k <number>] [--receipt <file>] [--json]',
|
|
182
|
+
'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
183
|
inspect: 'Usage: knolo inspect <pack.knolo>',
|
|
184
184
|
migrate:
|
|
185
185
|
'Usage: knolo migrate <pack.knolo> --out <new-pack.knolo> [--to 4]',
|
|
@@ -187,7 +187,7 @@ function printCommandHelp(command) {
|
|
|
187
187
|
explain: 'Usage: knolo explain <receipt.json> --pack <pack.knolo>',
|
|
188
188
|
diff: 'Usage: knolo diff <pack-a.knolo> <pack-b.knolo>',
|
|
189
189
|
dev: 'Usage: knolo dev',
|
|
190
|
-
v5: 'Usage: knolo v5 <info|health|studio|compress|decompress> <image.v5> [--out <file>] [--mode fast|balanced|max] [--attach-index]',
|
|
190
|
+
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>].',
|
|
191
191
|
icp: 'Usage: knolo icp <command> [options]',
|
|
192
192
|
'semantic:index':
|
|
193
193
|
'Usage: knolo semantic:index --pack <path> [--out <path>] [--model <id>] [--endpoint <url>]',
|
|
@@ -877,6 +877,13 @@ async function cmdQuery(core, args) {
|
|
|
877
877
|
|
|
878
878
|
const packBuffer = readFileSync(packPath);
|
|
879
879
|
const bytes = Uint8Array.from(packBuffer);
|
|
880
|
+
if (core.isKnowledgeImageV5?.(bytes)) {
|
|
881
|
+
if (opts.k !== undefined)
|
|
882
|
+
throw createError(
|
|
883
|
+
'V5 queries use EQL LIMIT. Put LIMIT in the query instead of --k.'
|
|
884
|
+
);
|
|
885
|
+
return await cmdQueryV5(core, bytes, packPath, question, opts);
|
|
886
|
+
}
|
|
880
887
|
const kb = await mountPackFromBytes(core, bytes);
|
|
881
888
|
const receiptResult =
|
|
882
889
|
opts.receipt && core.queryWithReceipt
|
|
@@ -936,6 +943,66 @@ async function cmdQuery(core, args) {
|
|
|
936
943
|
});
|
|
937
944
|
}
|
|
938
945
|
|
|
946
|
+
async function cmdQueryV5(core, bytes, imagePath, expression, opts) {
|
|
947
|
+
const image = core.mountKnowledgeImageV5(bytes);
|
|
948
|
+
const result = core.queryKnowledgeImageV5(image, expression);
|
|
949
|
+
if (opts.receipt) {
|
|
950
|
+
writeFileSync(
|
|
951
|
+
path.resolve(process.cwd(), opts.receipt),
|
|
952
|
+
`${JSON.stringify(result, null, 2)}\n`
|
|
953
|
+
);
|
|
954
|
+
}
|
|
955
|
+
const objects = new Map(image.objects.map((object) => [object.id, object]));
|
|
956
|
+
const hits = result.hits.map((hit) => {
|
|
957
|
+
const object = objects.get(hit.objectId);
|
|
958
|
+
const text = object
|
|
959
|
+
? new TextDecoder()
|
|
960
|
+
.decode(object.bytes)
|
|
961
|
+
.replace(/\s+/gu, ' ')
|
|
962
|
+
.slice(0, 200)
|
|
963
|
+
: '';
|
|
964
|
+
const metadata = object?.meta ?? {};
|
|
965
|
+
const source =
|
|
966
|
+
typeof metadata.source === 'string'
|
|
967
|
+
? metadata.source
|
|
968
|
+
: typeof metadata.docId === 'string'
|
|
969
|
+
? metadata.docId
|
|
970
|
+
: hit.objectId;
|
|
971
|
+
return {
|
|
972
|
+
objectId: hit.objectId,
|
|
973
|
+
kind: hit.kind,
|
|
974
|
+
source,
|
|
975
|
+
snippet: text,
|
|
976
|
+
joinedObjectIds: hit.joinedObjectIds,
|
|
977
|
+
};
|
|
978
|
+
});
|
|
979
|
+
const payload = {
|
|
980
|
+
contract: 'v5-knowledge-image',
|
|
981
|
+
query: expression,
|
|
982
|
+
imagePath,
|
|
983
|
+
stateRoot: result.stateRoot,
|
|
984
|
+
planRoot: result.planRoot,
|
|
985
|
+
resultRoot: result.resultRoot,
|
|
986
|
+
plan: result.plan,
|
|
987
|
+
hits,
|
|
988
|
+
queryResult: result,
|
|
989
|
+
};
|
|
990
|
+
if (opts.json) {
|
|
991
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
992
|
+
return;
|
|
993
|
+
}
|
|
994
|
+
if (!hits.length) {
|
|
995
|
+
console.log('No V5 hits found.');
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
console.log(`V5 query returned ${hits.length} hit(s):`);
|
|
999
|
+
hits.forEach((hit, index) => {
|
|
1000
|
+
console.log(`\n${index + 1}. ${hit.kind} ${hit.objectId}`);
|
|
1001
|
+
console.log(` source: ${hit.source}`);
|
|
1002
|
+
console.log(` snippet: ${hit.snippet}`);
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
|
|
939
1006
|
function parsePackCommandArgs(args, command) {
|
|
940
1007
|
const positional = [];
|
|
941
1008
|
const flags = {};
|
|
@@ -961,6 +1028,24 @@ async function cmdInspect(core, args) {
|
|
|
961
1028
|
`Pack file not found at ${path.relative(process.cwd(), packPath)}.`
|
|
962
1029
|
);
|
|
963
1030
|
const bytes = Uint8Array.from(readFileSync(packPath));
|
|
1031
|
+
if (core.isKnowledgeImageV5?.(bytes)) {
|
|
1032
|
+
const image = core.mountKnowledgeImageV5(bytes);
|
|
1033
|
+
console.log(
|
|
1034
|
+
JSON.stringify(
|
|
1035
|
+
{
|
|
1036
|
+
format: 'v5-knowledge-image',
|
|
1037
|
+
version: 5,
|
|
1038
|
+
stateRoot: image.stateRoot,
|
|
1039
|
+
commitDigest: image.commitDigest,
|
|
1040
|
+
objects: image.objects.length,
|
|
1041
|
+
events: image.events.length,
|
|
1042
|
+
},
|
|
1043
|
+
null,
|
|
1044
|
+
2
|
|
1045
|
+
)
|
|
1046
|
+
);
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
964
1049
|
const pack = await mountPackFromBytes(core, bytes);
|
|
965
1050
|
const result = {
|
|
966
1051
|
format:
|
|
@@ -988,6 +1073,23 @@ async function cmdVerify(core, args) {
|
|
|
988
1073
|
`Pack file not found at ${path.relative(process.cwd(), packPath)}.`
|
|
989
1074
|
);
|
|
990
1075
|
const bytes = Uint8Array.from(readFileSync(packPath));
|
|
1076
|
+
if (core.isKnowledgeImageV5?.(bytes)) {
|
|
1077
|
+
const verification = core.verifyKnowledgeImageV5(bytes);
|
|
1078
|
+
console.log(
|
|
1079
|
+
JSON.stringify(
|
|
1080
|
+
{
|
|
1081
|
+
verified: verification.valid,
|
|
1082
|
+
format: 'v5-knowledge-image',
|
|
1083
|
+
version: 5,
|
|
1084
|
+
stateRoot: verification.stateRoot,
|
|
1085
|
+
commitDigest: verification.commitDigest,
|
|
1086
|
+
},
|
|
1087
|
+
null,
|
|
1088
|
+
2
|
|
1089
|
+
)
|
|
1090
|
+
);
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
991
1093
|
const pack = await mountPackFromBytes(core, bytes);
|
|
992
1094
|
console.log(
|
|
993
1095
|
JSON.stringify(
|
|
@@ -1043,16 +1145,47 @@ async function cmdExplain(core, args) {
|
|
|
1043
1145
|
const receiptPath = positional[0];
|
|
1044
1146
|
if (!receiptPath || (!flags.out && !flags.pack))
|
|
1045
1147
|
throw createError(
|
|
1046
|
-
'Usage: knolo explain <receipt.json> --pack <pack.knolo>'
|
|
1148
|
+
'Usage: knolo explain <receipt.json> --pack <pack.knolo|image.v5>'
|
|
1047
1149
|
);
|
|
1048
1150
|
const packPath = path.resolve(process.cwd(), flags.pack);
|
|
1049
1151
|
const receipt = JSON.parse(
|
|
1050
1152
|
readFileSync(path.resolve(process.cwd(), receiptPath), 'utf8')
|
|
1051
1153
|
);
|
|
1052
|
-
const
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1154
|
+
const bytes = Uint8Array.from(readFileSync(packPath));
|
|
1155
|
+
if (core.isKnowledgeImageV5?.(bytes)) {
|
|
1156
|
+
core.verifyKnowledgeQueryResultV5(bytes, receipt);
|
|
1157
|
+
const image = core.mountKnowledgeImageV5(bytes);
|
|
1158
|
+
const objects = new Map(image.objects.map((object) => [object.id, object]));
|
|
1159
|
+
console.log(
|
|
1160
|
+
JSON.stringify(
|
|
1161
|
+
{
|
|
1162
|
+
verified: true,
|
|
1163
|
+
contract: 'v5-knowledge-image',
|
|
1164
|
+
query: receipt.plan,
|
|
1165
|
+
stateRoot: receipt.stateRoot,
|
|
1166
|
+
planRoot: receipt.planRoot,
|
|
1167
|
+
resultRoot: receipt.resultRoot,
|
|
1168
|
+
hits: receipt.hits.map((hit) => {
|
|
1169
|
+
const object = objects.get(hit.objectId);
|
|
1170
|
+
return {
|
|
1171
|
+
objectId: hit.objectId,
|
|
1172
|
+
kind: hit.kind,
|
|
1173
|
+
source:
|
|
1174
|
+
typeof object?.meta?.source === 'string'
|
|
1175
|
+
? object.meta.source
|
|
1176
|
+
: typeof object?.meta?.docId === 'string'
|
|
1177
|
+
? object.meta.docId
|
|
1178
|
+
: undefined,
|
|
1179
|
+
};
|
|
1180
|
+
}),
|
|
1181
|
+
},
|
|
1182
|
+
null,
|
|
1183
|
+
2
|
|
1184
|
+
)
|
|
1185
|
+
);
|
|
1186
|
+
return;
|
|
1187
|
+
}
|
|
1188
|
+
const pack = await mountPackFromBytes(core, bytes);
|
|
1056
1189
|
core.verifyReceipt(receipt, pack);
|
|
1057
1190
|
console.log(
|
|
1058
1191
|
JSON.stringify(
|
|
@@ -1120,6 +1253,8 @@ function parseV5Args(args) {
|
|
|
1120
1253
|
for (let i = 0; i < args.length; i++) {
|
|
1121
1254
|
const arg = args[i];
|
|
1122
1255
|
if (arg === '--attach-index') flags.attachIndex = true;
|
|
1256
|
+
else if (arg === '--json') flags.json = true;
|
|
1257
|
+
else if (arg === '--receipt') flags.receipt = args[++i];
|
|
1123
1258
|
else if (
|
|
1124
1259
|
arg === '--image' ||
|
|
1125
1260
|
arg === '--index' ||
|
|
@@ -1139,17 +1274,40 @@ function parseV5Args(args) {
|
|
|
1139
1274
|
|
|
1140
1275
|
async function cmdV5(core, args) {
|
|
1141
1276
|
const subcommand = args[0] || 'info';
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1277
|
+
const subcommands = [
|
|
1278
|
+
'info',
|
|
1279
|
+
'health',
|
|
1280
|
+
'studio',
|
|
1281
|
+
'query',
|
|
1282
|
+
'compress',
|
|
1283
|
+
'decompress',
|
|
1284
|
+
];
|
|
1285
|
+
if (!subcommands.includes(subcommand))
|
|
1145
1286
|
throw createError(`Unknown V5 command: ${subcommand}`);
|
|
1146
1287
|
const { positional, flags } = parseV5Args(
|
|
1147
|
-
args.slice(
|
|
1148
|
-
['info', 'health', 'studio', 'compress', 'decompress'].includes(args[0])
|
|
1149
|
-
? 1
|
|
1150
|
-
: 0
|
|
1151
|
-
)
|
|
1288
|
+
args.slice(subcommands.includes(args[0]) ? 1 : 0)
|
|
1152
1289
|
);
|
|
1290
|
+
if (subcommand === 'query') {
|
|
1291
|
+
const imagePath = path.resolve(
|
|
1292
|
+
process.cwd(),
|
|
1293
|
+
flags.image || positional[0] || 'dist/knowledge.v5'
|
|
1294
|
+
);
|
|
1295
|
+
if (!existsSync(imagePath))
|
|
1296
|
+
throw createError(
|
|
1297
|
+
`V5 image file not found at ${path.relative(process.cwd(), imagePath)}.`
|
|
1298
|
+
);
|
|
1299
|
+
const expression = (flags.image ? positional : positional.slice(1))
|
|
1300
|
+
.join(' ')
|
|
1301
|
+
.trim();
|
|
1302
|
+
if (!expression)
|
|
1303
|
+
throw createError(
|
|
1304
|
+
'Usage: knolo v5 query <image.v5> <EQL> [--json] [--receipt <file>]'
|
|
1305
|
+
);
|
|
1306
|
+
const bytes = Uint8Array.from(readFileSync(imagePath));
|
|
1307
|
+
if (!core.isKnowledgeImageV5?.(bytes))
|
|
1308
|
+
throw createError('V5 query requires a V5 Knowledge Image input.');
|
|
1309
|
+
return await cmdQueryV5(core, bytes, imagePath, expression, flags);
|
|
1310
|
+
}
|
|
1153
1311
|
const imagePath = path.resolve(
|
|
1154
1312
|
process.cwd(),
|
|
1155
1313
|
flags.image || positional[0] || 'dist/knowledge.v5'
|
|
@@ -1282,6 +1440,10 @@ async function cmdSemanticIndex(core, args) {
|
|
|
1282
1440
|
);
|
|
1283
1441
|
|
|
1284
1442
|
const bytes = Uint8Array.from(readFileSync(packPath));
|
|
1443
|
+
if (core.isKnowledgeImageV5?.(bytes))
|
|
1444
|
+
throw createError(
|
|
1445
|
+
'semantic:index targets V4 packs. Query V5 images with bounded EQL through knolo query.'
|
|
1446
|
+
);
|
|
1285
1447
|
const pack = await mountPackFromBytes(core, bytes);
|
|
1286
1448
|
const OllamaEmbeddingProvider = await loadOllamaProvider();
|
|
1287
1449
|
const provider = new OllamaEmbeddingProvider({ modelId, endpoint });
|
|
@@ -1332,10 +1494,12 @@ async function cmdSemanticValidate(core, args) {
|
|
|
1332
1494
|
const sidecarPath = path.resolve(process.cwd(), flags.sidecar);
|
|
1333
1495
|
const modelId = flags.model;
|
|
1334
1496
|
if (!modelId) throw createError('semantic:validate requires --model <id>.');
|
|
1335
|
-
const
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1497
|
+
const packBytes = Uint8Array.from(readFileSync(packPath));
|
|
1498
|
+
if (core.isKnowledgeImageV5?.(packBytes))
|
|
1499
|
+
throw createError(
|
|
1500
|
+
'semantic:validate targets V4 packs. Query V5 images with bounded EQL through knolo query.'
|
|
1501
|
+
);
|
|
1502
|
+
const pack = await mountPackFromBytes(core, packBytes);
|
|
1339
1503
|
const sidecar = core.parseSidecar(readFileSync(sidecarPath, 'utf8'));
|
|
1340
1504
|
core.validateSidecarForPack({ sidecar, pack, modelId });
|
|
1341
1505
|
if (sidecar.blocks.length !== pack.blocks.length)
|