capdag 0.124.274 → 0.127.280
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/capdag.js +230 -87
- package/capdag.test.js +112 -73
- package/package.json +1 -1
package/capdag.js
CHANGED
|
@@ -789,18 +789,22 @@ const MEDIA_OBJECT = 'media:record';
|
|
|
789
789
|
// Media URN for binary data - the most general media type (no constraints)
|
|
790
790
|
const MEDIA_IDENTITY = 'media:';
|
|
791
791
|
|
|
792
|
-
//
|
|
793
|
-
// Media URN for
|
|
794
|
-
const
|
|
795
|
-
// Media URN for
|
|
796
|
-
const
|
|
797
|
-
// Media URN for
|
|
798
|
-
const
|
|
799
|
-
// Media URN for
|
|
800
|
-
const
|
|
801
|
-
// Media URN for
|
|
802
|
-
|
|
803
|
-
|
|
792
|
+
// List types - URNs must match base.toml definitions
|
|
793
|
+
// Media URN for generic list type
|
|
794
|
+
const MEDIA_LIST = 'media:list';
|
|
795
|
+
// Media URN for textable list type
|
|
796
|
+
const MEDIA_TEXTABLE_LIST = 'media:list;textable';
|
|
797
|
+
// Media URN for string list type - textable with list marker
|
|
798
|
+
const MEDIA_STRING_LIST = 'media:list;textable';
|
|
799
|
+
// Media URN for integer list type - textable, numeric with list marker
|
|
800
|
+
const MEDIA_INTEGER_LIST = 'media:integer;list;textable;numeric';
|
|
801
|
+
// Media URN for number list type - textable, numeric with list marker
|
|
802
|
+
const MEDIA_NUMBER_LIST = 'media:list;numeric;textable';
|
|
803
|
+
// Media URN for boolean list type - uses "bool" with list marker
|
|
804
|
+
const MEDIA_BOOLEAN_LIST = 'media:bool;list;textable';
|
|
805
|
+
// Media URN for object list type - list of records (NOT textable)
|
|
806
|
+
// Use a specific format like JSON array for textable object lists.
|
|
807
|
+
const MEDIA_OBJECT_LIST = 'media:list;record';
|
|
804
808
|
|
|
805
809
|
// Semantic media types for specialized content
|
|
806
810
|
// Media URN for PNG image data
|
|
@@ -813,8 +817,6 @@ const MEDIA_VIDEO = 'media:video';
|
|
|
813
817
|
// Semantic AI input types - distinguished by their purpose/context
|
|
814
818
|
// Media URN for audio input containing speech for transcription (Whisper)
|
|
815
819
|
const MEDIA_AUDIO_SPEECH = 'media:audio;wav;speech';
|
|
816
|
-
// Media URN for thumbnail image output
|
|
817
|
-
const MEDIA_IMAGE_THUMBNAIL = 'media:image;png;thumbnail';
|
|
818
820
|
|
|
819
821
|
// Document types (PRIMARY naming - type IS the format)
|
|
820
822
|
// Media URN for PDF documents
|
|
@@ -842,6 +844,18 @@ const MEDIA_JSON_SCHEMA = 'media:json;json-schema;record;textable';
|
|
|
842
844
|
// Media URN for YAML data - has record marker (structured key-value)
|
|
843
845
|
const MEDIA_YAML = 'media:record;textable;yaml';
|
|
844
846
|
|
|
847
|
+
// Format-specific variants for JSON, YAML, CSV
|
|
848
|
+
const MEDIA_JSON_VALUE = 'media:json;textable';
|
|
849
|
+
const MEDIA_JSON_RECORD = 'media:json;record;textable';
|
|
850
|
+
const MEDIA_JSON_LIST = 'media:json;list;textable';
|
|
851
|
+
const MEDIA_JSON_LIST_RECORD = 'media:json;list;record;textable';
|
|
852
|
+
const MEDIA_YAML_VALUE = 'media:textable;yaml';
|
|
853
|
+
const MEDIA_YAML_RECORD = 'media:record;textable;yaml';
|
|
854
|
+
const MEDIA_YAML_LIST = 'media:list;textable;yaml';
|
|
855
|
+
const MEDIA_YAML_LIST_RECORD = 'media:list;record;textable;yaml';
|
|
856
|
+
const MEDIA_CSV = 'media:csv;list;record;textable';
|
|
857
|
+
const MEDIA_CSV_LIST = 'media:csv;list;textable';
|
|
858
|
+
|
|
845
859
|
// File path types - for arguments that represent filesystem paths
|
|
846
860
|
// Media URN for a single file path - textable, scalar by default (no list marker)
|
|
847
861
|
const MEDIA_FILE_PATH = 'media:file-path;textable';
|
|
@@ -849,8 +863,6 @@ const MEDIA_FILE_PATH = 'media:file-path;textable';
|
|
|
849
863
|
const MEDIA_FILE_PATH_ARRAY = 'media:file-path;list;textable';
|
|
850
864
|
|
|
851
865
|
// Semantic text input types - distinguished by their purpose/context
|
|
852
|
-
// Media URN for frontmatter text (book metadata) - scalar by default
|
|
853
|
-
const MEDIA_FRONTMATTER_TEXT = 'media:frontmatter;textable';
|
|
854
866
|
// Media URN for model spec (provider:model format, HuggingFace name, etc.) - scalar by default
|
|
855
867
|
const MEDIA_MODEL_SPEC = 'media:model-spec;textable';
|
|
856
868
|
// Media URN for MLX model path - scalar by default
|
|
@@ -877,20 +889,17 @@ const MEDIA_PATH_OUTPUT = 'media:model-path;record;textable';
|
|
|
877
889
|
const MEDIA_EMBEDDING_VECTOR = 'media:embedding-vector;record;textable';
|
|
878
890
|
// Media URN for LLM inference output - has record marker
|
|
879
891
|
const MEDIA_LLM_INFERENCE_OUTPUT = 'media:generated-text;record;textable';
|
|
880
|
-
// Media URN for extracted metadata - has record marker
|
|
881
|
-
const MEDIA_FILE_METADATA = 'media:file-metadata;record;textable';
|
|
882
|
-
// Media URN for extracted outline - has record marker
|
|
883
|
-
const MEDIA_DOCUMENT_OUTLINE = 'media:document-outline;record;textable';
|
|
884
|
-
// Media URN for disbound page - has list marker (array of page objects)
|
|
885
|
-
const MEDIA_DISBOUND_PAGE = 'media:disbound-page;list;textable';
|
|
886
892
|
// Media URN for vision inference output - textable, scalar by default
|
|
887
893
|
const MEDIA_IMAGE_DESCRIPTION = 'media:image-description;textable';
|
|
888
894
|
// Media URN for transcription output - has record marker
|
|
889
895
|
const MEDIA_TRANSCRIPTION_OUTPUT = 'media:record;textable;transcription';
|
|
890
|
-
// Media URN for decision output
|
|
891
|
-
const MEDIA_DECISION = 'media:
|
|
892
|
-
// Media URN for
|
|
893
|
-
const
|
|
896
|
+
// Media URN for decision output - JSON record with textable
|
|
897
|
+
const MEDIA_DECISION = 'media:decision;json;record;textable';
|
|
898
|
+
// Media URN for textable page output
|
|
899
|
+
const MEDIA_TEXTABLE_PAGE = 'media:textable;page';
|
|
900
|
+
// Collection types
|
|
901
|
+
const MEDIA_COLLECTION = 'media:collection;record';
|
|
902
|
+
const MEDIA_COLLECTION_LIST = 'media:collection;list;record';
|
|
894
903
|
|
|
895
904
|
// =============================================================================
|
|
896
905
|
// STANDARD CAP URN CONSTANTS
|
|
@@ -956,8 +965,10 @@ class MediaUrn {
|
|
|
956
965
|
// =========================================================================
|
|
957
966
|
|
|
958
967
|
/**
|
|
959
|
-
* Returns true if this media
|
|
960
|
-
*
|
|
968
|
+
* Returns true if this media URN describes list-type data (has `list` marker tag).
|
|
969
|
+
* This is a semantic type check — it means "the data format IS a list/array".
|
|
970
|
+
* This does NOT indicate input cardinality (single vs multiple items).
|
|
971
|
+
* Cardinality is tracked by is_sequence on the wire protocol, not by URN tags.
|
|
961
972
|
* @returns {boolean}
|
|
962
973
|
*/
|
|
963
974
|
isList() { return this._hasMarkerTag('list'); }
|
|
@@ -1027,6 +1038,22 @@ class MediaUrn {
|
|
|
1027
1038
|
/** @returns {boolean} True if the "bool" marker tag is present */
|
|
1028
1039
|
isBool() { return this._urn.getTag('bool') !== undefined; }
|
|
1029
1040
|
|
|
1041
|
+
/**
|
|
1042
|
+
* Returns true if this media URN describes YAML representation.
|
|
1043
|
+
* @returns {boolean}
|
|
1044
|
+
*/
|
|
1045
|
+
isYaml() {
|
|
1046
|
+
return this._hasMarkerTag('yaml');
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
/**
|
|
1050
|
+
* Returns true if this media URN describes CSV representation.
|
|
1051
|
+
* @returns {boolean}
|
|
1052
|
+
*/
|
|
1053
|
+
isCsv() {
|
|
1054
|
+
return this._hasMarkerTag('csv');
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1030
1057
|
/**
|
|
1031
1058
|
* Check if this represents a single file path type (not array).
|
|
1032
1059
|
* Returns true if the "file-path" marker tag is present AND no list marker.
|
|
@@ -1048,6 +1075,13 @@ class MediaUrn {
|
|
|
1048
1075
|
*/
|
|
1049
1076
|
isAnyFilePath() { return this._hasMarkerTag('file-path'); }
|
|
1050
1077
|
|
|
1078
|
+
/**
|
|
1079
|
+
* Check if this represents a collection type.
|
|
1080
|
+
* Returns true if the "collection" marker tag is present.
|
|
1081
|
+
* @returns {boolean}
|
|
1082
|
+
*/
|
|
1083
|
+
isCollection() { return this._hasMarkerTag('collection'); }
|
|
1084
|
+
|
|
1051
1085
|
/**
|
|
1052
1086
|
* Check if this media URN conforms to another (pattern).
|
|
1053
1087
|
* @param {MediaUrn} pattern
|
|
@@ -1123,20 +1157,67 @@ class MediaUrn {
|
|
|
1123
1157
|
// =============================================================================
|
|
1124
1158
|
|
|
1125
1159
|
/**
|
|
1126
|
-
* Build URN for LLM
|
|
1127
|
-
* @param {string} langCode - Language code (e.g., "en", "fr")
|
|
1160
|
+
* Build URN for LLM generate-text capability
|
|
1128
1161
|
* @returns {CapUrn}
|
|
1129
1162
|
*/
|
|
1130
|
-
function
|
|
1163
|
+
function llmGenerateTextUrn() {
|
|
1131
1164
|
return new CapUrnBuilder()
|
|
1132
|
-
.tag('op', '
|
|
1133
|
-
.tag('
|
|
1134
|
-
.tag('
|
|
1165
|
+
.tag('op', 'generate_text')
|
|
1166
|
+
.tag('llm', '*')
|
|
1167
|
+
.tag('ml-model', '*')
|
|
1135
1168
|
.inSpec(MEDIA_STRING)
|
|
1136
|
-
.outSpec(
|
|
1169
|
+
.outSpec(MEDIA_STRING)
|
|
1137
1170
|
.build();
|
|
1138
1171
|
}
|
|
1139
1172
|
|
|
1173
|
+
/**
|
|
1174
|
+
* Build URN for render-page-image capability
|
|
1175
|
+
* @param {string} inputMedia - The input media URN string
|
|
1176
|
+
* @returns {CapUrn}
|
|
1177
|
+
*/
|
|
1178
|
+
function renderPageImageUrn(inputMedia) {
|
|
1179
|
+
return new CapUrnBuilder()
|
|
1180
|
+
.tag('op', 'render_page_image')
|
|
1181
|
+
.inSpec(inputMedia)
|
|
1182
|
+
.outSpec(MEDIA_PNG)
|
|
1183
|
+
.build();
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
/**
|
|
1187
|
+
* Build URN for format conversion capability
|
|
1188
|
+
* @param {string} inMedia - The input media URN string
|
|
1189
|
+
* @param {string} outMedia - The output media URN string
|
|
1190
|
+
* @returns {CapUrn}
|
|
1191
|
+
*/
|
|
1192
|
+
function formatConversionUrn(inMedia, outMedia) {
|
|
1193
|
+
return new CapUrnBuilder()
|
|
1194
|
+
.tag('op', 'convert_format')
|
|
1195
|
+
.inSpec(inMedia)
|
|
1196
|
+
.outSpec(outMedia)
|
|
1197
|
+
.build();
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
/**
|
|
1201
|
+
* Map a primitive type name to the corresponding media URN string.
|
|
1202
|
+
* @param {string} typeName - The type name (e.g., 'string', 'integer', 'string-list')
|
|
1203
|
+
* @returns {string|null} The media URN string, or null if not recognized
|
|
1204
|
+
*/
|
|
1205
|
+
function mediaUrnForType(typeName) {
|
|
1206
|
+
switch (typeName) {
|
|
1207
|
+
case 'string': return MEDIA_STRING;
|
|
1208
|
+
case 'integer': return MEDIA_INTEGER;
|
|
1209
|
+
case 'number': return MEDIA_NUMBER;
|
|
1210
|
+
case 'boolean': return MEDIA_BOOLEAN;
|
|
1211
|
+
case 'object': return MEDIA_OBJECT;
|
|
1212
|
+
case 'string-list': return MEDIA_STRING_LIST;
|
|
1213
|
+
case 'integer-list': return MEDIA_INTEGER_LIST;
|
|
1214
|
+
case 'number-list': return MEDIA_NUMBER_LIST;
|
|
1215
|
+
case 'boolean-list': return MEDIA_BOOLEAN_LIST;
|
|
1216
|
+
case 'object-list': return MEDIA_OBJECT_LIST;
|
|
1217
|
+
default: return null;
|
|
1218
|
+
}
|
|
1219
|
+
}
|
|
1220
|
+
|
|
1140
1221
|
/**
|
|
1141
1222
|
* Build URN for model-availability capability
|
|
1142
1223
|
* @returns {CapUrn}
|
|
@@ -2933,6 +3014,56 @@ class CapValidator {
|
|
|
2933
3014
|
// CAP ARGUMENT VALUE - Unified argument type
|
|
2934
3015
|
// ============================================================================
|
|
2935
3016
|
|
|
3017
|
+
/**
|
|
3018
|
+
* Result from a cap execution.
|
|
3019
|
+
*
|
|
3020
|
+
* Scalar outputs carry raw materialized bytes (e.g. UTF-8 text, raw binary).
|
|
3021
|
+
* List outputs carry a CBOR sequence of values, one per list item.
|
|
3022
|
+
* Empty represents a void cap with no output.
|
|
3023
|
+
*/
|
|
3024
|
+
class CapResult {
|
|
3025
|
+
static KIND_SCALAR = 'scalar';
|
|
3026
|
+
static KIND_LIST = 'list';
|
|
3027
|
+
static KIND_EMPTY = 'empty';
|
|
3028
|
+
|
|
3029
|
+
/**
|
|
3030
|
+
* @param {'scalar'|'list'|'empty'} kind
|
|
3031
|
+
* @param {Uint8Array|null} data - Bytes for scalar or CBOR sequence for list, null for empty
|
|
3032
|
+
*/
|
|
3033
|
+
constructor(kind, data = null) {
|
|
3034
|
+
this.kind = kind;
|
|
3035
|
+
this.data = data;
|
|
3036
|
+
}
|
|
3037
|
+
|
|
3038
|
+
/** Create a CapResult carrying raw bytes (scalar output). */
|
|
3039
|
+
static scalar(data) {
|
|
3040
|
+
const bytes = data instanceof Uint8Array ? data : new Uint8Array(data || []);
|
|
3041
|
+
return new CapResult(CapResult.KIND_SCALAR, bytes);
|
|
3042
|
+
}
|
|
3043
|
+
|
|
3044
|
+
/** Create a CapResult carrying a CBOR sequence (list output). */
|
|
3045
|
+
static list(cborSequence) {
|
|
3046
|
+
const bytes = cborSequence instanceof Uint8Array ? cborSequence : new Uint8Array(cborSequence || []);
|
|
3047
|
+
return new CapResult(CapResult.KIND_LIST, bytes);
|
|
3048
|
+
}
|
|
3049
|
+
|
|
3050
|
+
/** Create a CapResult for void caps. */
|
|
3051
|
+
static empty() {
|
|
3052
|
+
return new CapResult(CapResult.KIND_EMPTY, null);
|
|
3053
|
+
}
|
|
3054
|
+
|
|
3055
|
+
/** Returns true if this is a scalar result. */
|
|
3056
|
+
isScalar() { return this.kind === CapResult.KIND_SCALAR; }
|
|
3057
|
+
|
|
3058
|
+
/** Returns true if this is a list result. */
|
|
3059
|
+
isList() { return this.kind === CapResult.KIND_LIST; }
|
|
3060
|
+
|
|
3061
|
+
/** Returns true if this is an empty result. */
|
|
3062
|
+
isEmpty() { return this.kind === CapResult.KIND_EMPTY; }
|
|
3063
|
+
}
|
|
3064
|
+
|
|
3065
|
+
// ============================================================================
|
|
3066
|
+
|
|
2936
3067
|
/**
|
|
2937
3068
|
* Unified argument type - arguments are identified by media_urn.
|
|
2938
3069
|
* The cap definition's sources specify how to extract values (stdin, position, cli_flag).
|
|
@@ -3189,7 +3320,7 @@ class CompositeCapSet {
|
|
|
3189
3320
|
* Execute a capability by finding the best match and delegating
|
|
3190
3321
|
* @param {string} capUrn - The capability URN to execute
|
|
3191
3322
|
* @param {CapArgumentValue[]} args - Arguments identified by media_urn
|
|
3192
|
-
* @returns {Promise<
|
|
3323
|
+
* @returns {Promise<CapResult>}
|
|
3193
3324
|
*/
|
|
3194
3325
|
async executeCap(capUrn, args) {
|
|
3195
3326
|
let request;
|
|
@@ -3903,12 +4034,8 @@ class CartridgeInfo {
|
|
|
3903
4034
|
this.caps = (data.caps || []).map(c => new CartridgeCapSummary(c.urn, c.title, c.description || ''));
|
|
3904
4035
|
this.categories = data.categories || [];
|
|
3905
4036
|
this.tags = data.tags || [];
|
|
3906
|
-
|
|
3907
|
-
|
|
3908
|
-
this.platform = data.platform || '';
|
|
3909
|
-
this.packageName = data.packageName || '';
|
|
3910
|
-
this.packageSha256 = data.packageSha256 || '';
|
|
3911
|
-
this.packageSize = data.packageSize || 0;
|
|
4037
|
+
// Versions with platform-specific builds
|
|
4038
|
+
this.versions = data.versions || {};
|
|
3912
4039
|
this.availableVersions = data.availableVersions || [];
|
|
3913
4040
|
}
|
|
3914
4041
|
|
|
@@ -3920,10 +4047,25 @@ class CartridgeInfo {
|
|
|
3920
4047
|
}
|
|
3921
4048
|
|
|
3922
4049
|
/**
|
|
3923
|
-
*
|
|
4050
|
+
* Get the build for a specific platform from the latest version
|
|
3924
4051
|
*/
|
|
3925
|
-
|
|
3926
|
-
|
|
4052
|
+
buildForPlatform(platform) {
|
|
4053
|
+
const latestVersionData = this.versions[this.version];
|
|
4054
|
+
if (!latestVersionData) return null;
|
|
4055
|
+
return (latestVersionData.builds || []).find(b => b.platform === platform) || null;
|
|
4056
|
+
}
|
|
4057
|
+
|
|
4058
|
+
/**
|
|
4059
|
+
* Get all platforms available across all versions
|
|
4060
|
+
*/
|
|
4061
|
+
availablePlatforms() {
|
|
4062
|
+
const platforms = new Set();
|
|
4063
|
+
for (const versionData of Object.values(this.versions)) {
|
|
4064
|
+
for (const build of (versionData.builds || [])) {
|
|
4065
|
+
platforms.add(build.platform);
|
|
4066
|
+
}
|
|
4067
|
+
}
|
|
4068
|
+
return Array.from(platforms).sort();
|
|
3927
4069
|
}
|
|
3928
4070
|
}
|
|
3929
4071
|
|
|
@@ -4145,8 +4287,8 @@ class CartridgeRepoServer {
|
|
|
4145
4287
|
if (!this.registry) {
|
|
4146
4288
|
throw new Error('Registry is required');
|
|
4147
4289
|
}
|
|
4148
|
-
if (this.registry.schemaVersion !== '
|
|
4149
|
-
throw new Error(`Unsupported registry schema version: ${this.registry.schemaVersion}. Required:
|
|
4290
|
+
if (this.registry.schemaVersion !== '4.0') {
|
|
4291
|
+
throw new Error(`Unsupported registry schema version: ${this.registry.schemaVersion}. Required: 4.0`);
|
|
4150
4292
|
}
|
|
4151
4293
|
if (!this.registry.cartridges || typeof this.registry.cartridges !== 'object') {
|
|
4152
4294
|
throw new Error('Registry must have cartridges object');
|
|
@@ -4157,11 +4299,17 @@ class CartridgeRepoServer {
|
|
|
4157
4299
|
* Validate version data has all required fields
|
|
4158
4300
|
*/
|
|
4159
4301
|
validateVersionData(id, version, versionData) {
|
|
4160
|
-
if (!versionData.
|
|
4161
|
-
throw new Error(`Cartridge ${id} v${version}:
|
|
4302
|
+
if (!Array.isArray(versionData.builds) || versionData.builds.length === 0) {
|
|
4303
|
+
throw new Error(`Cartridge ${id} v${version}: no builds`);
|
|
4162
4304
|
}
|
|
4163
|
-
|
|
4164
|
-
|
|
4305
|
+
for (let i = 0; i < versionData.builds.length; i++) {
|
|
4306
|
+
const build = versionData.builds[i];
|
|
4307
|
+
if (!build.platform) {
|
|
4308
|
+
throw new Error(`Cartridge ${id} v${version}: build[${i}] missing platform`);
|
|
4309
|
+
}
|
|
4310
|
+
if (!build.package || !build.package.name) {
|
|
4311
|
+
throw new Error(`Cartridge ${id} v${version}: build[${i}] (${build.platform}) missing package.name`);
|
|
4312
|
+
}
|
|
4165
4313
|
}
|
|
4166
4314
|
}
|
|
4167
4315
|
|
|
@@ -4182,19 +4330,6 @@ class CartridgeRepoServer {
|
|
|
4182
4330
|
return 0;
|
|
4183
4331
|
}
|
|
4184
4332
|
|
|
4185
|
-
/**
|
|
4186
|
-
* Build changelog map from versions
|
|
4187
|
-
*/
|
|
4188
|
-
buildChangelogMap(versions) {
|
|
4189
|
-
const changelog = {};
|
|
4190
|
-
for (const [version, versionData] of Object.entries(versions)) {
|
|
4191
|
-
if (versionData.changelog && Array.isArray(versionData.changelog)) {
|
|
4192
|
-
changelog[version] = versionData.changelog;
|
|
4193
|
-
}
|
|
4194
|
-
}
|
|
4195
|
-
return changelog;
|
|
4196
|
-
}
|
|
4197
|
-
|
|
4198
4333
|
/**
|
|
4199
4334
|
* Transform registry to flat cartridge array
|
|
4200
4335
|
*/
|
|
@@ -4218,28 +4353,20 @@ class CartridgeRepoServer {
|
|
|
4218
4353
|
return this.compareVersions(b, a);
|
|
4219
4354
|
});
|
|
4220
4355
|
|
|
4221
|
-
// Build flat cartridge object with latest version data
|
|
4222
|
-
const packageUrl = `https://machinefabric.com/cartridges/packages/${versionData.package.name}`;
|
|
4223
4356
|
cartridges.push({
|
|
4224
4357
|
id,
|
|
4225
4358
|
name: cartridge.name,
|
|
4226
4359
|
version: latestVersion,
|
|
4227
4360
|
description: cartridge.description,
|
|
4228
4361
|
author: cartridge.author,
|
|
4229
|
-
pageUrl: cartridge.pageUrl ||
|
|
4362
|
+
pageUrl: cartridge.pageUrl || '',
|
|
4230
4363
|
teamId: cartridge.teamId,
|
|
4231
4364
|
signedAt: versionData.releaseDate,
|
|
4232
4365
|
minAppVersion: versionData.minAppVersion || cartridge.minAppVersion,
|
|
4233
4366
|
caps: cartridge.caps || [],
|
|
4234
4367
|
categories: cartridge.categories,
|
|
4235
4368
|
tags: cartridge.tags,
|
|
4236
|
-
|
|
4237
|
-
// Distribution fields
|
|
4238
|
-
platform: versionData.platform,
|
|
4239
|
-
packageName: versionData.package.name,
|
|
4240
|
-
packageSha256: versionData.package.sha256,
|
|
4241
|
-
packageSize: versionData.package.size,
|
|
4242
|
-
// All available versions
|
|
4369
|
+
versions: cartridge.versions,
|
|
4243
4370
|
availableVersions
|
|
4244
4371
|
});
|
|
4245
4372
|
}
|
|
@@ -5439,18 +5566,20 @@ module.exports = {
|
|
|
5439
5566
|
MEDIA_NUMBER,
|
|
5440
5567
|
MEDIA_BOOLEAN,
|
|
5441
5568
|
MEDIA_OBJECT,
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5569
|
+
// List types
|
|
5570
|
+
MEDIA_LIST,
|
|
5571
|
+
MEDIA_TEXTABLE_LIST,
|
|
5572
|
+
MEDIA_STRING_LIST,
|
|
5573
|
+
MEDIA_INTEGER_LIST,
|
|
5574
|
+
MEDIA_NUMBER_LIST,
|
|
5575
|
+
MEDIA_BOOLEAN_LIST,
|
|
5576
|
+
MEDIA_OBJECT_LIST,
|
|
5447
5577
|
MEDIA_IDENTITY,
|
|
5448
5578
|
MEDIA_VOID,
|
|
5449
5579
|
MEDIA_PNG,
|
|
5450
5580
|
MEDIA_AUDIO,
|
|
5451
5581
|
MEDIA_VIDEO,
|
|
5452
5582
|
MEDIA_AUDIO_SPEECH,
|
|
5453
|
-
MEDIA_IMAGE_THUMBNAIL,
|
|
5454
5583
|
// Document types (PRIMARY naming)
|
|
5455
5584
|
MEDIA_PDF,
|
|
5456
5585
|
MEDIA_EPUB,
|
|
@@ -5464,11 +5593,22 @@ module.exports = {
|
|
|
5464
5593
|
MEDIA_JSON,
|
|
5465
5594
|
MEDIA_JSON_SCHEMA,
|
|
5466
5595
|
MEDIA_YAML,
|
|
5596
|
+
// Format-specific variants
|
|
5597
|
+
MEDIA_JSON_VALUE,
|
|
5598
|
+
MEDIA_JSON_RECORD,
|
|
5599
|
+
MEDIA_JSON_LIST,
|
|
5600
|
+
MEDIA_JSON_LIST_RECORD,
|
|
5601
|
+
MEDIA_YAML_VALUE,
|
|
5602
|
+
MEDIA_YAML_RECORD,
|
|
5603
|
+
MEDIA_YAML_LIST,
|
|
5604
|
+
MEDIA_YAML_LIST_RECORD,
|
|
5605
|
+
MEDIA_CSV,
|
|
5606
|
+
MEDIA_CSV_LIST,
|
|
5467
5607
|
MEDIA_MODEL_SPEC,
|
|
5468
5608
|
MEDIA_MODEL_REPO,
|
|
5469
5609
|
MEDIA_MODEL_DIM,
|
|
5470
5610
|
MEDIA_DECISION,
|
|
5471
|
-
|
|
5611
|
+
MEDIA_TEXTABLE_PAGE,
|
|
5472
5612
|
// Semantic output types - model management
|
|
5473
5613
|
MEDIA_DOWNLOAD_OUTPUT,
|
|
5474
5614
|
MEDIA_LIST_OUTPUT,
|
|
@@ -5479,21 +5619,24 @@ module.exports = {
|
|
|
5479
5619
|
// Semantic output types - inference
|
|
5480
5620
|
MEDIA_EMBEDDING_VECTOR,
|
|
5481
5621
|
MEDIA_LLM_INFERENCE_OUTPUT,
|
|
5482
|
-
MEDIA_FILE_METADATA,
|
|
5483
|
-
MEDIA_DOCUMENT_OUTLINE,
|
|
5484
|
-
MEDIA_DISBOUND_PAGE,
|
|
5485
5622
|
MEDIA_IMAGE_DESCRIPTION,
|
|
5486
5623
|
MEDIA_TRANSCRIPTION_OUTPUT,
|
|
5487
5624
|
// File path types
|
|
5488
5625
|
MEDIA_FILE_PATH,
|
|
5489
5626
|
MEDIA_FILE_PATH_ARRAY,
|
|
5490
|
-
// Semantic text input types
|
|
5491
|
-
MEDIA_FRONTMATTER_TEXT,
|
|
5492
5627
|
MEDIA_MLX_MODEL_PATH,
|
|
5628
|
+
// Collection types
|
|
5629
|
+
MEDIA_COLLECTION,
|
|
5630
|
+
MEDIA_COLLECTION_LIST,
|
|
5631
|
+
// Cap execution result
|
|
5632
|
+
CapResult,
|
|
5493
5633
|
// Unified argument type
|
|
5494
5634
|
CapArgumentValue,
|
|
5495
5635
|
// Standard cap URN builders
|
|
5496
|
-
|
|
5636
|
+
llmGenerateTextUrn,
|
|
5637
|
+
renderPageImageUrn,
|
|
5638
|
+
formatConversionUrn,
|
|
5639
|
+
mediaUrnForType,
|
|
5497
5640
|
modelAvailabilityUrn,
|
|
5498
5641
|
modelPathUrn,
|
|
5499
5642
|
CapMatrixError,
|
package/capdag.test.js
CHANGED
|
@@ -13,12 +13,12 @@ const {
|
|
|
13
13
|
StdinSource, StdinSourceKind,
|
|
14
14
|
validateNoMediaSpecRedefinitionSync,
|
|
15
15
|
CapArgumentValue,
|
|
16
|
-
|
|
16
|
+
llmGenerateTextUrn, modelAvailabilityUrn, modelPathUrn,
|
|
17
17
|
MachineSyntaxError, MachineSyntaxErrorCodes, MachineEdge, Machine, MachineBuilder, parseMachine, parseMachineWithAST,
|
|
18
18
|
CapRegistryEntry, MediaRegistryEntry, CapRegistryClient,
|
|
19
19
|
MEDIA_STRING, MEDIA_INTEGER, MEDIA_NUMBER, MEDIA_BOOLEAN,
|
|
20
|
-
MEDIA_OBJECT,
|
|
21
|
-
|
|
20
|
+
MEDIA_OBJECT, MEDIA_STRING_LIST, MEDIA_INTEGER_LIST,
|
|
21
|
+
MEDIA_NUMBER_LIST, MEDIA_BOOLEAN_LIST, MEDIA_OBJECT_LIST,
|
|
22
22
|
MEDIA_IDENTITY, MEDIA_VOID, MEDIA_PNG, MEDIA_AUDIO, MEDIA_VIDEO,
|
|
23
23
|
MEDIA_PDF, MEDIA_EPUB, MEDIA_MD, MEDIA_TXT, MEDIA_RST, MEDIA_LOG,
|
|
24
24
|
MEDIA_HTML, MEDIA_XML, MEDIA_JSON, MEDIA_YAML, MEDIA_JSON_SCHEMA,
|
|
@@ -26,8 +26,8 @@ const {
|
|
|
26
26
|
MEDIA_LLM_INFERENCE_OUTPUT,
|
|
27
27
|
MEDIA_FILE_PATH, MEDIA_FILE_PATH_ARRAY,
|
|
28
28
|
MEDIA_COLLECTION, MEDIA_COLLECTION_LIST,
|
|
29
|
-
MEDIA_DECISION,
|
|
30
|
-
MEDIA_AUDIO_SPEECH
|
|
29
|
+
MEDIA_DECISION,
|
|
30
|
+
MEDIA_AUDIO_SPEECH
|
|
31
31
|
} = require('./capdag.js');
|
|
32
32
|
|
|
33
33
|
// ============================================================================
|
|
@@ -827,7 +827,7 @@ function test061_isBinary() {
|
|
|
827
827
|
assert(!MediaUrn.fromString(MEDIA_MD).isBinary(), 'MEDIA_MD should not be binary');
|
|
828
828
|
}
|
|
829
829
|
|
|
830
|
-
// TEST062: isMap true for MEDIA_OBJECT (record); false for MEDIA_STRING (form=scalar),
|
|
830
|
+
// TEST062: isMap true for MEDIA_OBJECT (record); false for MEDIA_STRING (form=scalar), MEDIA_STRING_LIST (list)
|
|
831
831
|
// TEST062: is_record returns true if record marker tag is present (key-value structure)
|
|
832
832
|
function test062_isRecord() {
|
|
833
833
|
assert(MediaUrn.fromString(MEDIA_OBJECT).isRecord(), 'MEDIA_OBJECT should be record');
|
|
@@ -836,7 +836,7 @@ function test062_isRecord() {
|
|
|
836
836
|
// Without record marker, is_record is false
|
|
837
837
|
assert(!MediaUrn.fromString('media:textable').isRecord(), 'plain textable should not be record');
|
|
838
838
|
assert(!MediaUrn.fromString(MEDIA_STRING).isRecord(), 'MEDIA_STRING should not be record');
|
|
839
|
-
assert(!MediaUrn.fromString(
|
|
839
|
+
assert(!MediaUrn.fromString(MEDIA_STRING_LIST).isRecord(), 'MEDIA_STRING_LIST should not be record');
|
|
840
840
|
}
|
|
841
841
|
|
|
842
842
|
// TEST063: is_scalar returns true if NO list marker (scalar is default cardinality)
|
|
@@ -848,16 +848,16 @@ function test063_isScalar() {
|
|
|
848
848
|
assert(MediaUrn.fromString(MEDIA_OBJECT).isScalar(), 'MEDIA_OBJECT (record but scalar) should be scalar');
|
|
849
849
|
assert(MediaUrn.fromString('media:textable').isScalar(), 'plain textable should be scalar');
|
|
850
850
|
// With list marker, is_scalar is false
|
|
851
|
-
assert(!MediaUrn.fromString(
|
|
852
|
-
assert(!MediaUrn.fromString(
|
|
851
|
+
assert(!MediaUrn.fromString(MEDIA_STRING_LIST).isScalar(), 'MEDIA_STRING_LIST should not be scalar');
|
|
852
|
+
assert(!MediaUrn.fromString(MEDIA_OBJECT_LIST).isScalar(), 'MEDIA_OBJECT_LIST should not be scalar');
|
|
853
853
|
}
|
|
854
854
|
|
|
855
|
-
// TEST064: isList true for
|
|
855
|
+
// TEST064: isList true for MEDIA_STRING_LIST, MEDIA_INTEGER_LIST, MEDIA_OBJECT_LIST;
|
|
856
856
|
// false for MEDIA_STRING, MEDIA_OBJECT
|
|
857
857
|
function test064_isList() {
|
|
858
|
-
assert(MediaUrn.fromString(
|
|
859
|
-
assert(MediaUrn.fromString(
|
|
860
|
-
assert(MediaUrn.fromString(
|
|
858
|
+
assert(MediaUrn.fromString(MEDIA_STRING_LIST).isList(), 'MEDIA_STRING_LIST should be list');
|
|
859
|
+
assert(MediaUrn.fromString(MEDIA_INTEGER_LIST).isList(), 'MEDIA_INTEGER_LIST should be list');
|
|
860
|
+
assert(MediaUrn.fromString(MEDIA_OBJECT_LIST).isList(), 'MEDIA_OBJECT_LIST should be list');
|
|
861
861
|
assert(!MediaUrn.fromString(MEDIA_STRING).isList(), 'MEDIA_STRING should not be list');
|
|
862
862
|
assert(!MediaUrn.fromString(MEDIA_OBJECT).isList(), 'MEDIA_OBJECT should not be list');
|
|
863
863
|
}
|
|
@@ -865,7 +865,7 @@ function test064_isList() {
|
|
|
865
865
|
// TEST065: is_opaque returns true if NO record marker (opaque is default structure)
|
|
866
866
|
function test065_isOpaque() {
|
|
867
867
|
assert(MediaUrn.fromString(MEDIA_STRING).isOpaque(), 'MEDIA_STRING should be opaque');
|
|
868
|
-
assert(MediaUrn.fromString(
|
|
868
|
+
assert(MediaUrn.fromString(MEDIA_STRING_LIST).isOpaque(), 'MEDIA_STRING_LIST (list but no record) should be opaque');
|
|
869
869
|
assert(MediaUrn.fromString(MEDIA_PDF).isOpaque(), 'MEDIA_PDF should be opaque');
|
|
870
870
|
assert(MediaUrn.fromString('media:textable').isOpaque(), 'plain textable should be opaque');
|
|
871
871
|
// With record marker, is_opaque is false
|
|
@@ -912,8 +912,8 @@ function test071_toStringRoundtrip() {
|
|
|
912
912
|
function test072_constantsParse() {
|
|
913
913
|
const constants = [
|
|
914
914
|
MEDIA_STRING, MEDIA_INTEGER, MEDIA_NUMBER, MEDIA_BOOLEAN,
|
|
915
|
-
MEDIA_OBJECT,
|
|
916
|
-
|
|
915
|
+
MEDIA_OBJECT, MEDIA_STRING_LIST, MEDIA_INTEGER_LIST,
|
|
916
|
+
MEDIA_NUMBER_LIST, MEDIA_BOOLEAN_LIST, MEDIA_OBJECT_LIST,
|
|
917
917
|
MEDIA_IDENTITY, MEDIA_VOID, MEDIA_PNG, MEDIA_PDF, MEDIA_EPUB,
|
|
918
918
|
MEDIA_MD, MEDIA_TXT, MEDIA_RST, MEDIA_LOG, MEDIA_HTML, MEDIA_XML,
|
|
919
919
|
MEDIA_JSON, MEDIA_YAML, MEDIA_JSON_SCHEMA, MEDIA_AUDIO, MEDIA_VIDEO,
|
|
@@ -1052,8 +1052,8 @@ function test101_resolvedIsScalar() {
|
|
|
1052
1052
|
|
|
1053
1053
|
// TEST102: MediaSpec with list -> isList() true
|
|
1054
1054
|
function test102_resolvedIsList() {
|
|
1055
|
-
const spec = new MediaSpec('text/plain', null, null, 'String
|
|
1056
|
-
assert(spec.isList(), 'Resolved
|
|
1055
|
+
const spec = new MediaSpec('text/plain', null, null, 'String List', null, MEDIA_STRING_LIST);
|
|
1056
|
+
assert(spec.isList(), 'Resolved string_list spec should be list');
|
|
1057
1057
|
}
|
|
1058
1058
|
|
|
1059
1059
|
// TEST103: MediaSpec with json tag -> isJSON() true
|
|
@@ -1706,37 +1706,37 @@ function test309_modelAvailabilityAndPathAreDistinct() {
|
|
|
1706
1706
|
assert(avail.toString() !== path.toString(), 'availability and path must be distinct');
|
|
1707
1707
|
}
|
|
1708
1708
|
|
|
1709
|
-
// TEST310:
|
|
1710
|
-
function
|
|
1711
|
-
const urn =
|
|
1712
|
-
assert(urn.
|
|
1713
|
-
assert(urn.
|
|
1714
|
-
assert(urn.
|
|
1709
|
+
// TEST310: llm_generate_text_urn has correct op and ml-model tags
|
|
1710
|
+
function test310_llmGenerateTextUrn() {
|
|
1711
|
+
const urn = llmGenerateTextUrn();
|
|
1712
|
+
assert(urn.hasTag('op', 'generate_text'), 'Must have op=generate_text');
|
|
1713
|
+
assert(urn.getTag('llm') !== undefined, 'Must have llm tag');
|
|
1714
|
+
assert(urn.getTag('ml-model') !== undefined, 'Must have ml-model tag');
|
|
1715
1715
|
}
|
|
1716
1716
|
|
|
1717
|
-
// TEST311:
|
|
1718
|
-
function
|
|
1719
|
-
const urn =
|
|
1717
|
+
// TEST311: llm_generate_text_urn in/out specs match MEDIA_STRING
|
|
1718
|
+
function test311_llmGenerateTextUrnSpecs() {
|
|
1719
|
+
const urn = llmGenerateTextUrn();
|
|
1720
1720
|
const inSpec = TaggedUrn.fromString(urn.getInSpec());
|
|
1721
1721
|
const expectedIn = TaggedUrn.fromString(MEDIA_STRING);
|
|
1722
1722
|
assert(inSpec.conformsTo(expectedIn), 'in_spec must conform to MEDIA_STRING');
|
|
1723
1723
|
const outSpec = TaggedUrn.fromString(urn.getOutSpec());
|
|
1724
|
-
const expectedOut = TaggedUrn.fromString(
|
|
1725
|
-
assert(outSpec.conformsTo(expectedOut), 'out_spec must conform to
|
|
1724
|
+
const expectedOut = TaggedUrn.fromString(MEDIA_STRING);
|
|
1725
|
+
assert(outSpec.conformsTo(expectedOut), 'out_spec must conform to MEDIA_STRING');
|
|
1726
1726
|
}
|
|
1727
1727
|
|
|
1728
1728
|
// TEST312: All URN builders produce parseable cap URNs
|
|
1729
1729
|
function test312_allUrnBuildersProduceValidUrns() {
|
|
1730
1730
|
const avail = modelAvailabilityUrn();
|
|
1731
1731
|
const path = modelPathUrn();
|
|
1732
|
-
const
|
|
1732
|
+
const llmGen = llmGenerateTextUrn();
|
|
1733
1733
|
|
|
1734
1734
|
const parsedAvail = CapUrn.fromString(avail.toString());
|
|
1735
1735
|
assert(parsedAvail !== null, 'modelAvailabilityUrn must be parseable');
|
|
1736
1736
|
const parsedPath = CapUrn.fromString(path.toString());
|
|
1737
1737
|
assert(parsedPath !== null, 'modelPathUrn must be parseable');
|
|
1738
|
-
const
|
|
1739
|
-
assert(
|
|
1738
|
+
const parsedLlmGen = CapUrn.fromString(llmGen.toString());
|
|
1739
|
+
assert(parsedLlmGen !== null, 'llmGenerateTextUrn must be parseable');
|
|
1740
1740
|
}
|
|
1741
1741
|
|
|
1742
1742
|
// ============================================================================
|
|
@@ -2013,7 +2013,7 @@ function testJS_mediaSpecConstruction() {
|
|
|
2013
2013
|
|
|
2014
2014
|
// Sample registry for testing
|
|
2015
2015
|
const sampleRegistry = {
|
|
2016
|
-
schemaVersion: '
|
|
2016
|
+
schemaVersion: '4.0',
|
|
2017
2017
|
lastUpdated: '2026-02-07T16:48:28Z',
|
|
2018
2018
|
cartridges: {
|
|
2019
2019
|
pdfcartridge: {
|
|
@@ -2043,12 +2043,14 @@ const sampleRegistry = {
|
|
|
2043
2043
|
releaseDate: '2026-02-07T16:40:28Z',
|
|
2044
2044
|
changelog: ['Initial release'],
|
|
2045
2045
|
minAppVersion: '1.0.0',
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2046
|
+
builds: [{
|
|
2047
|
+
platform: 'darwin-arm64',
|
|
2048
|
+
package: {
|
|
2049
|
+
name: 'pdfcartridge-0.81.5325.pkg',
|
|
2050
|
+
sha256: '9b68724eb9220ecf01e8ed4f5f80c594fbac2239bc5bf675005ec882ecc5eba0',
|
|
2051
|
+
size: 5187485
|
|
2052
|
+
}
|
|
2053
|
+
}]
|
|
2052
2054
|
}
|
|
2053
2055
|
}
|
|
2054
2056
|
},
|
|
@@ -2074,12 +2076,14 @@ const sampleRegistry = {
|
|
|
2074
2076
|
releaseDate: '2026-02-07T17:44:00Z',
|
|
2075
2077
|
changelog: ['First version'],
|
|
2076
2078
|
minAppVersion: '1.0.0',
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2079
|
+
builds: [{
|
|
2080
|
+
platform: 'darwin-arm64',
|
|
2081
|
+
package: {
|
|
2082
|
+
name: 'txtcartridge-0.54.6408.pkg',
|
|
2083
|
+
sha256: 'abc123',
|
|
2084
|
+
size: 821000
|
|
2085
|
+
}
|
|
2086
|
+
}]
|
|
2083
2087
|
}
|
|
2084
2088
|
}
|
|
2085
2089
|
}
|
|
@@ -2095,9 +2099,16 @@ function test320_cartridgeInfoConstruction() {
|
|
|
2095
2099
|
description: 'A test',
|
|
2096
2100
|
teamId: 'TEAM123',
|
|
2097
2101
|
signedAt: '2026-01-01',
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2102
|
+
caps: [{urn: 'cap:in="media:void";op=test;out="media:void"', title: 'Test', description: ''}],
|
|
2103
|
+
versions: {
|
|
2104
|
+
'1.0.0': {
|
|
2105
|
+
releaseDate: '2026-01-01',
|
|
2106
|
+
changelog: ['Initial'],
|
|
2107
|
+
minAppVersion: '1.0.0',
|
|
2108
|
+
builds: [{platform: 'darwin-arm64', package: {name: 'test-1.0.0.pkg', sha256: 'abc123', size: 100}}]
|
|
2109
|
+
}
|
|
2110
|
+
},
|
|
2111
|
+
availableVersions: ['1.0.0']
|
|
2101
2112
|
};
|
|
2102
2113
|
const cartridge = new CartridgeInfo(data);
|
|
2103
2114
|
assert(cartridge.id === 'testcartridge', 'ID should match');
|
|
@@ -2118,28 +2129,50 @@ function test321_cartridgeInfoIsSigned() {
|
|
|
2118
2129
|
assert(unsigned2.isSigned() === false, 'Cartridge without signedAt should not be signed');
|
|
2119
2130
|
}
|
|
2120
2131
|
|
|
2121
|
-
// TEST322: Cartridge info
|
|
2122
|
-
function
|
|
2123
|
-
const
|
|
2124
|
-
|
|
2132
|
+
// TEST322: Cartridge info build for platform and available platforms
|
|
2133
|
+
function test322_cartridgeInfoBuildForPlatform() {
|
|
2134
|
+
const withBuilds = new CartridgeInfo({
|
|
2135
|
+
id: 'test', version: '1.0.0', caps: [],
|
|
2136
|
+
versions: {
|
|
2137
|
+
'1.0.0': {
|
|
2138
|
+
builds: [
|
|
2139
|
+
{platform: 'darwin-arm64', package: {name: 'test-darwin.pkg', sha256: 'abc', size: 100}},
|
|
2140
|
+
{platform: 'linux-x86_64', package: {name: 'test-linux.pkg', sha256: 'def', size: 200}}
|
|
2141
|
+
]
|
|
2142
|
+
}
|
|
2143
|
+
},
|
|
2144
|
+
availableVersions: ['1.0.0']
|
|
2145
|
+
});
|
|
2146
|
+
const darwinBuild = withBuilds.buildForPlatform('darwin-arm64');
|
|
2147
|
+
assert(darwinBuild !== null, 'Should find darwin-arm64 build');
|
|
2148
|
+
assert(darwinBuild.package.name === 'test-darwin.pkg', 'Should have correct package name');
|
|
2149
|
+
|
|
2150
|
+
const linuxBuild = withBuilds.buildForPlatform('linux-x86_64');
|
|
2151
|
+
assert(linuxBuild !== null, 'Should find linux-x86_64 build');
|
|
2152
|
+
|
|
2153
|
+
const missingBuild = withBuilds.buildForPlatform('windows-x86_64');
|
|
2154
|
+
assert(missingBuild === null, 'Should return null for missing platform');
|
|
2125
2155
|
|
|
2126
|
-
const
|
|
2127
|
-
assert(
|
|
2156
|
+
const platforms = withBuilds.availablePlatforms();
|
|
2157
|
+
assert(platforms.length === 2, 'Should have 2 platforms');
|
|
2158
|
+
assert(platforms.includes('darwin-arm64'), 'Should include darwin-arm64');
|
|
2159
|
+
assert(platforms.includes('linux-x86_64'), 'Should include linux-x86_64');
|
|
2128
2160
|
|
|
2129
|
-
const
|
|
2130
|
-
assert(
|
|
2161
|
+
const noBuilds = new CartridgeInfo({id: 'test', version: '1.0.0', caps: [], versions: {}, availableVersions: []});
|
|
2162
|
+
assert(noBuilds.buildForPlatform('darwin-arm64') === null, 'Should return null when no versions');
|
|
2163
|
+
assert(noBuilds.availablePlatforms().length === 0, 'Should have no platforms');
|
|
2131
2164
|
}
|
|
2132
2165
|
|
|
2133
2166
|
// TEST323: CartridgeRepoServer validate registry
|
|
2134
2167
|
function test323_cartridgeRepoServerValidateRegistry() {
|
|
2135
2168
|
// Valid registry
|
|
2136
2169
|
const server = new CartridgeRepoServer(sampleRegistry);
|
|
2137
|
-
assert(server.registry.schemaVersion === '
|
|
2170
|
+
assert(server.registry.schemaVersion === '4.0', 'Should accept valid registry');
|
|
2138
2171
|
|
|
2139
2172
|
// Invalid schema version
|
|
2140
2173
|
let threw = false;
|
|
2141
2174
|
try {
|
|
2142
|
-
new CartridgeRepoServer({schemaVersion: '
|
|
2175
|
+
new CartridgeRepoServer({schemaVersion: '3.0', cartridges: {}});
|
|
2143
2176
|
} catch (e) {
|
|
2144
2177
|
threw = true;
|
|
2145
2178
|
assert(e.message.includes('schema version'), 'Should reject wrong schema version');
|
|
@@ -2149,7 +2182,7 @@ function test323_cartridgeRepoServerValidateRegistry() {
|
|
|
2149
2182
|
// Missing cartridges
|
|
2150
2183
|
threw = false;
|
|
2151
2184
|
try {
|
|
2152
|
-
new CartridgeRepoServer({schemaVersion: '
|
|
2185
|
+
new CartridgeRepoServer({schemaVersion: '4.0'});
|
|
2153
2186
|
} catch (e) {
|
|
2154
2187
|
threw = true;
|
|
2155
2188
|
assert(e.message.includes('cartridges'), 'Should reject missing cartridges');
|
|
@@ -2170,8 +2203,14 @@ function test324_cartridgeRepoServerTransformToArray() {
|
|
|
2170
2203
|
assert(pdf.version === '0.81.5325', 'Should have latest version');
|
|
2171
2204
|
assert(pdf.teamId === 'P336JK947M', 'Should have teamId');
|
|
2172
2205
|
assert(pdf.signedAt === '2026-02-07T16:40:28Z', 'Should have signedAt from releaseDate');
|
|
2173
|
-
assert(pdf.
|
|
2174
|
-
assert(pdf.
|
|
2206
|
+
assert(pdf.versions !== undefined, 'Should have versions');
|
|
2207
|
+
assert(pdf.versions['0.81.5325'] !== undefined, 'Should have version data');
|
|
2208
|
+
assert(pdf.versions['0.81.5325'].builds.length === 1, 'Should have 1 build');
|
|
2209
|
+
assert(pdf.versions['0.81.5325'].builds[0].platform === 'darwin-arm64', 'Should have correct platform');
|
|
2210
|
+
assert(pdf.versions['0.81.5325'].builds[0].package.name === 'pdfcartridge-0.81.5325.pkg', 'Should have package name');
|
|
2211
|
+
assert(pdf.versions['0.81.5325'].builds[0].package.sha256 === '9b68724eb9220ecf01e8ed4f5f80c594fbac2239bc5bf675005ec882ecc5eba0', 'Should have package SHA256');
|
|
2212
|
+
assert(Array.isArray(pdf.availableVersions), 'Should have availableVersions array');
|
|
2213
|
+
assert(pdf.availableVersions.includes('0.81.5325'), 'Should include latest version');
|
|
2175
2214
|
assert(Array.isArray(pdf.caps), 'Should have caps array');
|
|
2176
2215
|
assert(pdf.caps.length === 2, 'Should have 2 caps');
|
|
2177
2216
|
}
|
|
@@ -2338,7 +2377,7 @@ function test335_cartridgeRepoServerClientIntegration() {
|
|
|
2338
2377
|
const cartridge = client.getCartridge('pdfcartridge');
|
|
2339
2378
|
assert(cartridge !== null, 'Client should find cartridge from server data');
|
|
2340
2379
|
assert(cartridge.isSigned(), 'Cartridge should be signed');
|
|
2341
|
-
assert(cartridge.
|
|
2380
|
+
assert(cartridge.buildForPlatform('darwin-arm64') !== null, 'Cartridge should have darwin-arm64 build');
|
|
2342
2381
|
|
|
2343
2382
|
// Client can get suggestions
|
|
2344
2383
|
const capUrn = 'cap:in="media:pdf";op=disbind;out="media:disbound-page;textable;list"';
|
|
@@ -2359,7 +2398,7 @@ function test335_cartridgeRepoServerClientIntegration() {
|
|
|
2359
2398
|
// TEST546: isImage returns true only when image marker tag is present
|
|
2360
2399
|
function test546_isImage() {
|
|
2361
2400
|
assert(MediaUrn.fromString(MEDIA_PNG).isImage(), 'MEDIA_PNG should be image');
|
|
2362
|
-
assert(MediaUrn.fromString(
|
|
2401
|
+
assert(MediaUrn.fromString('media:image;png;thumbnail').isImage(), 'media:image;png;thumbnail should be image');
|
|
2363
2402
|
assert(MediaUrn.fromString('media:image;jpg').isImage(), 'media:image;jpg should be image');
|
|
2364
2403
|
// Non-image types
|
|
2365
2404
|
assert(!MediaUrn.fromString(MEDIA_PDF).isImage(), 'MEDIA_PDF should not be image');
|
|
@@ -2393,8 +2432,8 @@ function test548_isVideo() {
|
|
|
2393
2432
|
function test549_isNumeric() {
|
|
2394
2433
|
assert(MediaUrn.fromString(MEDIA_INTEGER).isNumeric(), 'MEDIA_INTEGER should be numeric');
|
|
2395
2434
|
assert(MediaUrn.fromString(MEDIA_NUMBER).isNumeric(), 'MEDIA_NUMBER should be numeric');
|
|
2396
|
-
assert(MediaUrn.fromString(
|
|
2397
|
-
assert(MediaUrn.fromString(
|
|
2435
|
+
assert(MediaUrn.fromString(MEDIA_INTEGER_LIST).isNumeric(), 'MEDIA_INTEGER_LIST should be numeric');
|
|
2436
|
+
assert(MediaUrn.fromString(MEDIA_NUMBER_LIST).isNumeric(), 'MEDIA_NUMBER_LIST should be numeric');
|
|
2398
2437
|
// Non-numeric types
|
|
2399
2438
|
assert(!MediaUrn.fromString(MEDIA_STRING).isNumeric(), 'MEDIA_STRING should not be numeric');
|
|
2400
2439
|
assert(!MediaUrn.fromString(MEDIA_BOOLEAN).isNumeric(), 'MEDIA_BOOLEAN should not be numeric');
|
|
@@ -2404,9 +2443,9 @@ function test549_isNumeric() {
|
|
|
2404
2443
|
// TEST550: isBool returns true only when bool marker tag is present
|
|
2405
2444
|
function test550_isBool() {
|
|
2406
2445
|
assert(MediaUrn.fromString(MEDIA_BOOLEAN).isBool(), 'MEDIA_BOOLEAN should be bool');
|
|
2407
|
-
assert(MediaUrn.fromString(
|
|
2408
|
-
|
|
2409
|
-
assert(MediaUrn.fromString(
|
|
2446
|
+
assert(MediaUrn.fromString(MEDIA_BOOLEAN_LIST).isBool(), 'MEDIA_BOOLEAN_LIST should be bool');
|
|
2447
|
+
// MEDIA_DECISION is now a JSON record type (not bool)
|
|
2448
|
+
assert(!MediaUrn.fromString(MEDIA_DECISION).isBool(), 'MEDIA_DECISION should not be bool (it is a JSON record now)');
|
|
2410
2449
|
// Non-bool types
|
|
2411
2450
|
assert(!MediaUrn.fromString(MEDIA_STRING).isBool(), 'MEDIA_STRING should not be bool');
|
|
2412
2451
|
assert(!MediaUrn.fromString(MEDIA_INTEGER).isBool(), 'MEDIA_INTEGER should not be bool');
|
|
@@ -2429,7 +2468,7 @@ function test552_isFilePathArray() {
|
|
|
2429
2468
|
// Scalar file-path is NOT isFilePathArray
|
|
2430
2469
|
assert(!MediaUrn.fromString(MEDIA_FILE_PATH).isFilePathArray(), 'MEDIA_FILE_PATH should not be isFilePathArray');
|
|
2431
2470
|
// Non-file-path types
|
|
2432
|
-
assert(!MediaUrn.fromString(
|
|
2471
|
+
assert(!MediaUrn.fromString(MEDIA_STRING_LIST).isFilePathArray(), 'MEDIA_STRING_LIST should not be file-path-array');
|
|
2433
2472
|
}
|
|
2434
2473
|
|
|
2435
2474
|
// TEST553: isAnyFilePath returns true for both scalar and array file-path
|
|
@@ -2438,7 +2477,7 @@ function test553_isAnyFilePath() {
|
|
|
2438
2477
|
assert(MediaUrn.fromString(MEDIA_FILE_PATH_ARRAY).isAnyFilePath(), 'MEDIA_FILE_PATH_ARRAY should be any-file-path');
|
|
2439
2478
|
// Non-file-path types
|
|
2440
2479
|
assert(!MediaUrn.fromString(MEDIA_STRING).isAnyFilePath(), 'MEDIA_STRING should not be any-file-path');
|
|
2441
|
-
assert(!MediaUrn.fromString(
|
|
2480
|
+
assert(!MediaUrn.fromString(MEDIA_STRING_LIST).isAnyFilePath(), 'MEDIA_STRING_LIST should not be any-file-path');
|
|
2442
2481
|
}
|
|
2443
2482
|
|
|
2444
2483
|
// TEST554: isCollection returns true when collection marker tag is present
|
|
@@ -5369,8 +5408,8 @@ async function runTests() {
|
|
|
5369
5408
|
runTest('TEST307: model_availability_urn', test307_modelAvailabilityUrn);
|
|
5370
5409
|
runTest('TEST308: model_path_urn', test308_modelPathUrn);
|
|
5371
5410
|
runTest('TEST309: model_availability_and_path_are_distinct', test309_modelAvailabilityAndPathAreDistinct);
|
|
5372
|
-
runTest('TEST310:
|
|
5373
|
-
runTest('TEST311:
|
|
5411
|
+
runTest('TEST310: llm_generate_text_urn', test310_llmGenerateTextUrn);
|
|
5412
|
+
runTest('TEST311: llm_generate_text_urn_specs', test311_llmGenerateTextUrnSpecs);
|
|
5374
5413
|
runTest('TEST312: all_urn_builders_produce_valid_urns', test312_allUrnBuildersProduceValidUrns);
|
|
5375
5414
|
|
|
5376
5415
|
// JS-specific tests (no Rust number)
|
|
@@ -5395,7 +5434,7 @@ async function runTests() {
|
|
|
5395
5434
|
console.log('\n--- cartridge_repo ---');
|
|
5396
5435
|
runTest('TEST320: cartridge_info_construction', test320_cartridgeInfoConstruction);
|
|
5397
5436
|
runTest('TEST321: cartridge_info_is_signed', test321_cartridgeInfoIsSigned);
|
|
5398
|
-
runTest('TEST322:
|
|
5437
|
+
runTest('TEST322: cartridge_info_build_for_platform', test322_cartridgeInfoBuildForPlatform);
|
|
5399
5438
|
runTest('TEST323: cartridge_repo_server_validate_registry', test323_cartridgeRepoServerValidateRegistry);
|
|
5400
5439
|
runTest('TEST324: cartridge_repo_server_transform_to_array', test324_cartridgeRepoServerTransformToArray);
|
|
5401
5440
|
runTest('TEST325: cartridge_repo_server_get_cartridges', test325_cartridgeRepoServerGetCartridges);
|
package/package.json
CHANGED