capdag 0.153.347 → 0.158.370
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/build-browser.js +15 -15
- package/{cap-graph-renderer.js → cap-fab-renderer.js} +78 -78
- package/capdag.js +455 -23
- package/capdag.test.js +26 -26
- package/package.json +2 -2
package/capdag.js
CHANGED
|
@@ -843,10 +843,44 @@ const MEDIA_OBJECT_LIST = 'media:list;record';
|
|
|
843
843
|
// Semantic media types for specialized content
|
|
844
844
|
// Media URN for PNG image data
|
|
845
845
|
const MEDIA_PNG = 'media:image;png';
|
|
846
|
+
// Media URN for JPEG image data
|
|
847
|
+
const MEDIA_JPEG = 'media:jpeg;image';
|
|
848
|
+
// Media URN for GIF image data
|
|
849
|
+
const MEDIA_GIF = 'media:gif;image';
|
|
850
|
+
// Media URN for BMP image data
|
|
851
|
+
const MEDIA_BMP = 'media:bmp;image';
|
|
852
|
+
// Media URN for TIFF image data
|
|
853
|
+
const MEDIA_TIFF = 'media:tiff;image';
|
|
854
|
+
// Media URN for WebP image data
|
|
855
|
+
const MEDIA_WEBP = 'media:webp;image';
|
|
846
856
|
// Media URN for audio data (wav, mp3, flac, etc.)
|
|
847
857
|
const MEDIA_AUDIO = 'media:wav;audio';
|
|
858
|
+
// Media URN for MP3 audio data
|
|
859
|
+
const MEDIA_MP3 = 'media:mp3;audio';
|
|
860
|
+
// Media URN for WAV audio data
|
|
861
|
+
const MEDIA_WAV = 'media:wav;audio';
|
|
862
|
+
// Media URN for FLAC audio data
|
|
863
|
+
const MEDIA_FLAC = 'media:flac;audio';
|
|
864
|
+
// Media URN for OGG audio data
|
|
865
|
+
const MEDIA_OGG = 'media:ogg;audio';
|
|
866
|
+
// Media URN for AAC audio data
|
|
867
|
+
const MEDIA_AAC = 'media:aac;audio';
|
|
868
|
+
// Media URN for M4A audio data
|
|
869
|
+
const MEDIA_M4A = 'media:m4a;audio';
|
|
870
|
+
// Media URN for AIFF audio data
|
|
871
|
+
const MEDIA_AIFF = 'media:aiff;audio';
|
|
872
|
+
// Media URN for Opus audio data
|
|
873
|
+
const MEDIA_OPUS = 'media:opus;audio';
|
|
848
874
|
// Media URN for video data (mp4, webm, mov, etc.)
|
|
849
875
|
const MEDIA_VIDEO = 'media:video';
|
|
876
|
+
// Media URN for MP4 video data
|
|
877
|
+
const MEDIA_MP4 = 'media:mp4;video';
|
|
878
|
+
// Media URN for MOV video data
|
|
879
|
+
const MEDIA_MOV = 'media:mov;video';
|
|
880
|
+
// Media URN for WebM video data
|
|
881
|
+
const MEDIA_WEBM = 'media:webm;video';
|
|
882
|
+
// Media URN for MKV video data
|
|
883
|
+
const MEDIA_MKV = 'media:mkv;video';
|
|
850
884
|
|
|
851
885
|
// Semantic AI input types - distinguished by their purpose/context
|
|
852
886
|
// Media URN for audio input containing speech for transcription (Whisper)
|
|
@@ -930,6 +964,16 @@ const MEDIA_TRANSCRIPTION_OUTPUT = 'media:record;textable;transcription';
|
|
|
930
964
|
const MEDIA_DECISION = 'media:decision;json;record;textable';
|
|
931
965
|
// Media URN for textable page output
|
|
932
966
|
const MEDIA_TEXTABLE_PAGE = 'media:textable;page';
|
|
967
|
+
// Media URN for Hugging Face API token (secret, textable)
|
|
968
|
+
const MEDIA_HF_TOKEN = 'media:hf-token;secret;textable';
|
|
969
|
+
// Media URN for a list of model architectures — JSON record
|
|
970
|
+
const MEDIA_MODEL_ARCH_LIST = 'media:model-arch-list;json;record;textable';
|
|
971
|
+
// Media URN for a model search request — JSON record
|
|
972
|
+
const MEDIA_MODEL_SEARCH_REQUEST = 'media:model-search-request;json;record;textable';
|
|
973
|
+
// Media URN for a model search response — JSON record
|
|
974
|
+
const MEDIA_MODEL_SEARCH_RESPONSE = 'media:model-search-response;json;record;textable';
|
|
975
|
+
// Media URN for model filter resolution — JSON record
|
|
976
|
+
const MEDIA_MODEL_FILTER_RESOLUTION = 'media:model-filter-resolution;json;record;textable';
|
|
933
977
|
// Collection types
|
|
934
978
|
const MEDIA_COLLECTION = 'media:collection;record';
|
|
935
979
|
const MEDIA_COLLECTION_LIST = 'media:collection;list;record';
|
|
@@ -2031,6 +2075,8 @@ class Cap {
|
|
|
2031
2075
|
this.output = null;
|
|
2032
2076
|
this.metadata_json = metadataJson;
|
|
2033
2077
|
this.registered_by = null; // Registration attribution
|
|
2078
|
+
this.supported_model_types = []; // Model types this cap supports (omitted when empty)
|
|
2079
|
+
this.default_model_spec = null; // Default model spec string (omitted when null)
|
|
2034
2080
|
}
|
|
2035
2081
|
|
|
2036
2082
|
/**
|
|
@@ -2247,7 +2293,9 @@ class Cap {
|
|
|
2247
2293
|
JSON.stringify(this.args.map(a => a.toJSON())) === JSON.stringify(other.args.map(a => a.toJSON())) &&
|
|
2248
2294
|
JSON.stringify(this.output) === JSON.stringify(other.output) &&
|
|
2249
2295
|
JSON.stringify(this.metadata_json) === JSON.stringify(other.metadata_json) &&
|
|
2250
|
-
JSON.stringify(this.registered_by) === JSON.stringify(other.registered_by)
|
|
2296
|
+
JSON.stringify(this.registered_by) === JSON.stringify(other.registered_by) &&
|
|
2297
|
+
JSON.stringify(this.supported_model_types) === JSON.stringify(other.supported_model_types) &&
|
|
2298
|
+
this.default_model_spec === other.default_model_spec;
|
|
2251
2299
|
}
|
|
2252
2300
|
|
|
2253
2301
|
/**
|
|
@@ -2276,6 +2324,16 @@ class Cap {
|
|
|
2276
2324
|
result.metadata_json = this.metadata_json;
|
|
2277
2325
|
}
|
|
2278
2326
|
|
|
2327
|
+
// supported_model_types: omit when empty, matching Rust skip_serializing_if = is_empty
|
|
2328
|
+
if (Array.isArray(this.supported_model_types) && this.supported_model_types.length > 0) {
|
|
2329
|
+
result.supported_model_types = this.supported_model_types;
|
|
2330
|
+
}
|
|
2331
|
+
|
|
2332
|
+
// default_model_spec: omit when null, matching Rust skip_serializing_if = is_none
|
|
2333
|
+
if (this.default_model_spec !== null && this.default_model_spec !== undefined) {
|
|
2334
|
+
result.default_model_spec = this.default_model_spec;
|
|
2335
|
+
}
|
|
2336
|
+
|
|
2279
2337
|
return result;
|
|
2280
2338
|
}
|
|
2281
2339
|
|
|
@@ -2304,6 +2362,8 @@ class Cap {
|
|
|
2304
2362
|
}
|
|
2305
2363
|
cap.output = json.output;
|
|
2306
2364
|
cap.registered_by = json.registered_by ? RegisteredBy.fromJSON(json.registered_by) : null;
|
|
2365
|
+
cap.supported_model_types = Array.isArray(json.supported_model_types) ? json.supported_model_types : [];
|
|
2366
|
+
cap.default_model_spec = (typeof json.default_model_spec === 'string') ? json.default_model_spec : null;
|
|
2307
2367
|
return cap;
|
|
2308
2368
|
}
|
|
2309
2369
|
|
|
@@ -2331,6 +2391,183 @@ class Cap {
|
|
|
2331
2391
|
}
|
|
2332
2392
|
}
|
|
2333
2393
|
|
|
2394
|
+
/**
|
|
2395
|
+
* A cap group bundles caps and adapter URNs as an atomic registration unit.
|
|
2396
|
+
*
|
|
2397
|
+
* If any adapter in the group creates ambiguity with an already-registered
|
|
2398
|
+
* adapter, the entire group is rejected — none of its caps or adapters get
|
|
2399
|
+
* registered. Mirrors CSCapGroup / capdag::cap_group::CapGroup.
|
|
2400
|
+
*/
|
|
2401
|
+
class CapGroup {
|
|
2402
|
+
/**
|
|
2403
|
+
* @param {string} name - Group name (for diagnostics and error messages)
|
|
2404
|
+
* @param {Cap[]} caps - Caps in this group
|
|
2405
|
+
* @param {string[]} adapterUrns - Media URNs this group's adapter handles
|
|
2406
|
+
*/
|
|
2407
|
+
constructor(name, caps = [], adapterUrns = []) {
|
|
2408
|
+
if (!name || typeof name !== 'string') {
|
|
2409
|
+
throw new Error('CapGroup name is required and must be a string');
|
|
2410
|
+
}
|
|
2411
|
+
this.name = name;
|
|
2412
|
+
this.caps = caps;
|
|
2413
|
+
this.adapter_urns = adapterUrns;
|
|
2414
|
+
}
|
|
2415
|
+
|
|
2416
|
+
/**
|
|
2417
|
+
* Create a CapGroup from JSON representation
|
|
2418
|
+
* @param {Object} json - The JSON data
|
|
2419
|
+
* @returns {CapGroup} The CapGroup instance
|
|
2420
|
+
*/
|
|
2421
|
+
static fromJSON(json) {
|
|
2422
|
+
if (!json.name) {
|
|
2423
|
+
throw new Error('CapGroup missing required field: name');
|
|
2424
|
+
}
|
|
2425
|
+
const caps = Array.isArray(json.caps) ? json.caps.map(c => Cap.fromJSON(c)) : [];
|
|
2426
|
+
const adapterUrns = Array.isArray(json.adapter_urns) ? json.adapter_urns : [];
|
|
2427
|
+
return new CapGroup(json.name, caps, adapterUrns);
|
|
2428
|
+
}
|
|
2429
|
+
|
|
2430
|
+
/**
|
|
2431
|
+
* Convert to JSON representation
|
|
2432
|
+
* @returns {Object} The JSON representation
|
|
2433
|
+
*/
|
|
2434
|
+
toJSON() {
|
|
2435
|
+
return {
|
|
2436
|
+
name: this.name,
|
|
2437
|
+
caps: this.caps.map(c => c.toJSON()),
|
|
2438
|
+
adapter_urns: this.adapter_urns
|
|
2439
|
+
};
|
|
2440
|
+
}
|
|
2441
|
+
}
|
|
2442
|
+
|
|
2443
|
+
/**
|
|
2444
|
+
* Unified cap-based manifest for components (providers and cartridges).
|
|
2445
|
+
*
|
|
2446
|
+
* `(registry_url, channel, name, version)` is the cartridge's full
|
|
2447
|
+
* identity — each (registry, channel) pair is an independent namespace.
|
|
2448
|
+
* Mirrors CSCapManifest / capdag::cap_manifest::CapManifest.
|
|
2449
|
+
*/
|
|
2450
|
+
class CapManifest {
|
|
2451
|
+
/**
|
|
2452
|
+
* @param {string} name - Component name
|
|
2453
|
+
* @param {string} version - Semver version string
|
|
2454
|
+
* @param {string} channel - Distribution channel: 'release' or 'nightly'
|
|
2455
|
+
* @param {string|null} registryUrl - Verbatim registry URL, or null for dev builds
|
|
2456
|
+
* @param {string} description - Short plain-text description
|
|
2457
|
+
* @param {CapGroup[]} capGroups - Cap groups (all caps must be in a group)
|
|
2458
|
+
*/
|
|
2459
|
+
constructor(name, version, channel, registryUrl, description, capGroups = []) {
|
|
2460
|
+
if (!name || typeof name !== 'string') {
|
|
2461
|
+
throw new Error('CapManifest name is required and must be a string');
|
|
2462
|
+
}
|
|
2463
|
+
if (!version || typeof version !== 'string') {
|
|
2464
|
+
throw new Error('CapManifest version is required and must be a string');
|
|
2465
|
+
}
|
|
2466
|
+
if (channel !== 'release' && channel !== 'nightly') {
|
|
2467
|
+
throw new Error(`CapManifest channel must be 'release' or 'nightly', got: '${channel}'`);
|
|
2468
|
+
}
|
|
2469
|
+
if (registryUrl !== null && registryUrl !== undefined && typeof registryUrl !== 'string') {
|
|
2470
|
+
throw new Error("CapManifest registry_url must be null (dev build) or a string");
|
|
2471
|
+
}
|
|
2472
|
+
if (!description || typeof description !== 'string') {
|
|
2473
|
+
throw new Error('CapManifest description is required and must be a string');
|
|
2474
|
+
}
|
|
2475
|
+
|
|
2476
|
+
this.name = name;
|
|
2477
|
+
this.version = version;
|
|
2478
|
+
this.channel = channel;
|
|
2479
|
+
this.registry_url = registryUrl !== undefined ? registryUrl : null;
|
|
2480
|
+
this.description = description;
|
|
2481
|
+
this.cap_groups = capGroups;
|
|
2482
|
+
this.author = null;
|
|
2483
|
+
this.page_url = null;
|
|
2484
|
+
}
|
|
2485
|
+
|
|
2486
|
+
/**
|
|
2487
|
+
* Returns all caps flattened across all cap groups.
|
|
2488
|
+
* @returns {Cap[]}
|
|
2489
|
+
*/
|
|
2490
|
+
allCaps() {
|
|
2491
|
+
return this.cap_groups.flatMap(g => g.caps);
|
|
2492
|
+
}
|
|
2493
|
+
|
|
2494
|
+
/**
|
|
2495
|
+
* Create a CapManifest from JSON / dictionary representation.
|
|
2496
|
+
*
|
|
2497
|
+
* `registry_url` must be present as a key; it may be `null` (dev build)
|
|
2498
|
+
* or a non-empty string (registry build). A missing key is a hard parse
|
|
2499
|
+
* error so old-schema payloads never silently pass.
|
|
2500
|
+
*
|
|
2501
|
+
* @param {Object} json - The JSON data
|
|
2502
|
+
* @returns {CapManifest} The CapManifest instance
|
|
2503
|
+
* @throws {Error} If required fields are missing or invalid
|
|
2504
|
+
*/
|
|
2505
|
+
static fromJSON(json) {
|
|
2506
|
+
if (!json.name) throw new Error('CapManifest missing required field: name');
|
|
2507
|
+
if (!json.version) throw new Error('CapManifest missing required field: version');
|
|
2508
|
+
if (!json.channel) throw new Error('CapManifest missing required field: channel');
|
|
2509
|
+
if (!json.description) throw new Error('CapManifest missing required field: description');
|
|
2510
|
+
if (!Array.isArray(json.cap_groups)) throw new Error('CapManifest missing required field: cap_groups');
|
|
2511
|
+
|
|
2512
|
+
// registry_url must be present as a key (may be null for dev builds)
|
|
2513
|
+
if (!Object.prototype.hasOwnProperty.call(json, 'registry_url')) {
|
|
2514
|
+
throw new Error(
|
|
2515
|
+
'CapManifest missing required field: registry_url. ' +
|
|
2516
|
+
'It must be present with value null for dev builds or a URL string for registry builds.'
|
|
2517
|
+
);
|
|
2518
|
+
}
|
|
2519
|
+
|
|
2520
|
+
if (json.channel !== 'release' && json.channel !== 'nightly') {
|
|
2521
|
+
throw new Error(`CapManifest channel must be 'release' or 'nightly', got: '${json.channel}'`);
|
|
2522
|
+
}
|
|
2523
|
+
|
|
2524
|
+
const registryUrl = (json.registry_url !== null && json.registry_url !== undefined)
|
|
2525
|
+
? json.registry_url
|
|
2526
|
+
: null;
|
|
2527
|
+
|
|
2528
|
+
if (registryUrl !== null && typeof registryUrl !== 'string') {
|
|
2529
|
+
throw new Error("CapManifest registry_url must be null or a string");
|
|
2530
|
+
}
|
|
2531
|
+
|
|
2532
|
+
const capGroups = json.cap_groups.map(g => CapGroup.fromJSON(g));
|
|
2533
|
+
const manifest = new CapManifest(
|
|
2534
|
+
json.name,
|
|
2535
|
+
json.version,
|
|
2536
|
+
json.channel,
|
|
2537
|
+
registryUrl,
|
|
2538
|
+
json.description,
|
|
2539
|
+
capGroups
|
|
2540
|
+
);
|
|
2541
|
+
|
|
2542
|
+
if (json.author && typeof json.author === 'string') {
|
|
2543
|
+
manifest.author = json.author;
|
|
2544
|
+
}
|
|
2545
|
+
if (json.page_url && typeof json.page_url === 'string') {
|
|
2546
|
+
manifest.page_url = json.page_url;
|
|
2547
|
+
}
|
|
2548
|
+
|
|
2549
|
+
return manifest;
|
|
2550
|
+
}
|
|
2551
|
+
|
|
2552
|
+
/**
|
|
2553
|
+
* Convert to JSON representation
|
|
2554
|
+
* @returns {Object} The JSON representation
|
|
2555
|
+
*/
|
|
2556
|
+
toJSON() {
|
|
2557
|
+
const result = {
|
|
2558
|
+
name: this.name,
|
|
2559
|
+
version: this.version,
|
|
2560
|
+
channel: this.channel,
|
|
2561
|
+
registry_url: this.registry_url,
|
|
2562
|
+
description: this.description,
|
|
2563
|
+
cap_groups: this.cap_groups.map(g => g.toJSON())
|
|
2564
|
+
};
|
|
2565
|
+
if (this.author) result.author = this.author;
|
|
2566
|
+
if (this.page_url) result.page_url = this.page_url;
|
|
2567
|
+
return result;
|
|
2568
|
+
}
|
|
2569
|
+
}
|
|
2570
|
+
|
|
2334
2571
|
/**
|
|
2335
2572
|
* Helper functions for creating capabilities
|
|
2336
2573
|
*/
|
|
@@ -3142,13 +3379,13 @@ class CapArgumentValue {
|
|
|
3142
3379
|
|
|
3143
3380
|
|
|
3144
3381
|
// ============================================================================
|
|
3145
|
-
//
|
|
3382
|
+
// CAPFAB - Directed graph of capability conversions
|
|
3146
3383
|
// ============================================================================
|
|
3147
3384
|
|
|
3148
3385
|
/**
|
|
3149
3386
|
* An edge in the capability graph representing a conversion from one media URN to another.
|
|
3150
3387
|
*/
|
|
3151
|
-
class
|
|
3388
|
+
class CapFabEdge {
|
|
3152
3389
|
/**
|
|
3153
3390
|
* @param {string} fromUrn - The input media URN
|
|
3154
3391
|
* @param {string} toUrn - The output media URN
|
|
@@ -3168,7 +3405,7 @@ class CapGraphEdge {
|
|
|
3168
3405
|
/**
|
|
3169
3406
|
* Statistics about a capability graph.
|
|
3170
3407
|
*/
|
|
3171
|
-
class
|
|
3408
|
+
class CapFabStats {
|
|
3172
3409
|
/**
|
|
3173
3410
|
* @param {number} nodeCount - Number of unique media URN nodes
|
|
3174
3411
|
* @param {number} edgeCount - Number of edges (capabilities)
|
|
@@ -3187,7 +3424,7 @@ class CapGraphStats {
|
|
|
3187
3424
|
* A directed graph where nodes are media URNs and edges are capabilities.
|
|
3188
3425
|
* This graph enables discovering conversion paths between different media formats.
|
|
3189
3426
|
*/
|
|
3190
|
-
class
|
|
3427
|
+
class CapFab {
|
|
3191
3428
|
constructor() {
|
|
3192
3429
|
this.edges = [];
|
|
3193
3430
|
this.outgoing = new Map(); // fromUrn -> edge indices
|
|
@@ -3211,7 +3448,7 @@ class CapGraph {
|
|
|
3211
3448
|
|
|
3212
3449
|
// Create edge
|
|
3213
3450
|
const edgeIndex = this.edges.length;
|
|
3214
|
-
const edge = new
|
|
3451
|
+
const edge = new CapFabEdge(fromUrn, toUrn, cap, registryName, specificity);
|
|
3215
3452
|
this.edges.push(edge);
|
|
3216
3453
|
|
|
3217
3454
|
// Update outgoing index
|
|
@@ -3237,7 +3474,7 @@ class CapGraph {
|
|
|
3237
3474
|
|
|
3238
3475
|
/**
|
|
3239
3476
|
* Get all edges in the graph.
|
|
3240
|
-
* @returns {
|
|
3477
|
+
* @returns {CapFabEdge[]}
|
|
3241
3478
|
*/
|
|
3242
3479
|
getEdges() {
|
|
3243
3480
|
return [...this.edges];
|
|
@@ -3247,7 +3484,7 @@ class CapGraph {
|
|
|
3247
3484
|
* Get all edges where the provided URN satisfies the edge's input requirement.
|
|
3248
3485
|
* Uses conformsTo-based matching instead of exact string matching.
|
|
3249
3486
|
* @param {string} urn - The media URN
|
|
3250
|
-
* @returns {
|
|
3487
|
+
* @returns {CapFabEdge[]}
|
|
3251
3488
|
*/
|
|
3252
3489
|
getOutgoing(urn) {
|
|
3253
3490
|
// Use TaggedUrn matching: find all edges where the provided URN (instance)
|
|
@@ -3268,7 +3505,7 @@ class CapGraph {
|
|
|
3268
3505
|
/**
|
|
3269
3506
|
* Get all edges targeting a media URN.
|
|
3270
3507
|
* @param {string} urn - The media URN
|
|
3271
|
-
* @returns {
|
|
3508
|
+
* @returns {CapFabEdge[]}
|
|
3272
3509
|
*/
|
|
3273
3510
|
getIncoming(urn) {
|
|
3274
3511
|
const indices = this.incoming.get(urn) || [];
|
|
@@ -3289,7 +3526,7 @@ class CapGraph {
|
|
|
3289
3526
|
* Get all direct edges from one URN to another, sorted by specificity (highest first).
|
|
3290
3527
|
* @param {string} fromUrn - The source media URN
|
|
3291
3528
|
* @param {string} toUrn - The target media URN
|
|
3292
|
-
* @returns {
|
|
3529
|
+
* @returns {CapFabEdge[]}
|
|
3293
3530
|
*/
|
|
3294
3531
|
getDirectEdges(fromUrn, toUrn) {
|
|
3295
3532
|
const edges = this.getOutgoing(fromUrn).filter(edge => edge.toUrn === toUrn);
|
|
@@ -3338,7 +3575,7 @@ class CapGraph {
|
|
|
3338
3575
|
* Find the shortest conversion path from one URN to another.
|
|
3339
3576
|
* @param {string} fromUrn - The source media URN
|
|
3340
3577
|
* @param {string} toUrn - The target media URN
|
|
3341
|
-
* @returns {
|
|
3578
|
+
* @returns {CapFabEdge[]|null} Array of edges representing the path, or null if no path exists
|
|
3342
3579
|
*/
|
|
3343
3580
|
findPath(fromUrn, toUrn) {
|
|
3344
3581
|
if (fromUrn === toUrn) {
|
|
@@ -3393,7 +3630,7 @@ class CapGraph {
|
|
|
3393
3630
|
* @param {string} fromUrn - The source media URN
|
|
3394
3631
|
* @param {string} toUrn - The target media URN
|
|
3395
3632
|
* @param {number} maxDepth - Maximum path length to search
|
|
3396
|
-
* @returns {
|
|
3633
|
+
* @returns {CapFabEdge[][]} Array of paths (each path is an array of edges)
|
|
3397
3634
|
*/
|
|
3398
3635
|
findAllPaths(fromUrn, toUrn, maxDepth) {
|
|
3399
3636
|
if (!this.nodes.has(fromUrn) || !this.nodes.has(toUrn)) {
|
|
@@ -3447,7 +3684,7 @@ class CapGraph {
|
|
|
3447
3684
|
* @param {string} fromUrn - The source media URN
|
|
3448
3685
|
* @param {string} toUrn - The target media URN
|
|
3449
3686
|
* @param {number} maxDepth - Maximum path length to search
|
|
3450
|
-
* @returns {
|
|
3687
|
+
* @returns {CapFabEdge[]|null} Array of edges representing the best path, or null if no path exists
|
|
3451
3688
|
*/
|
|
3452
3689
|
findBestPath(fromUrn, toUrn, maxDepth) {
|
|
3453
3690
|
const allPaths = this.findAllPaths(fromUrn, toUrn, maxDepth);
|
|
@@ -3488,10 +3725,10 @@ class CapGraph {
|
|
|
3488
3725
|
|
|
3489
3726
|
/**
|
|
3490
3727
|
* Get statistics about the graph.
|
|
3491
|
-
* @returns {
|
|
3728
|
+
* @returns {CapFabStats}
|
|
3492
3729
|
*/
|
|
3493
3730
|
stats() {
|
|
3494
|
-
return new
|
|
3731
|
+
return new CapFabStats(
|
|
3495
3732
|
this.nodes.size,
|
|
3496
3733
|
this.edges.length,
|
|
3497
3734
|
this.outgoing.size,
|
|
@@ -4283,6 +4520,171 @@ class CartridgeRepoServer {
|
|
|
4283
4520
|
}
|
|
4284
4521
|
}
|
|
4285
4522
|
|
|
4523
|
+
// ============================================================================
|
|
4524
|
+
// Bifaci — cartridge attachment & runtime identity types
|
|
4525
|
+
// ============================================================================
|
|
4526
|
+
|
|
4527
|
+
/**
|
|
4528
|
+
* Reasons why a cartridge attachment attempt failed.
|
|
4529
|
+
* Mirrors Rust CartridgeAttachmentErrorKind.
|
|
4530
|
+
*/
|
|
4531
|
+
const CartridgeAttachmentErrorKind = Object.freeze({
|
|
4532
|
+
INCOMPATIBLE: 'incompatible',
|
|
4533
|
+
MANIFEST_INVALID: 'manifest_invalid',
|
|
4534
|
+
HANDSHAKE_FAILED: 'handshake_failed',
|
|
4535
|
+
IDENTITY_REJECTED: 'identity_rejected',
|
|
4536
|
+
ENTRY_POINT_MISSING: 'entry_point_missing',
|
|
4537
|
+
QUARANTINED: 'quarantined',
|
|
4538
|
+
});
|
|
4539
|
+
|
|
4540
|
+
/**
|
|
4541
|
+
* Describes a failed cartridge attachment attempt.
|
|
4542
|
+
* Mirrors Rust CartridgeAttachmentError.
|
|
4543
|
+
*/
|
|
4544
|
+
class CartridgeAttachmentError {
|
|
4545
|
+
/**
|
|
4546
|
+
* @param {string} kind - CartridgeAttachmentErrorKind value
|
|
4547
|
+
* @param {string} message - Human-readable error message
|
|
4548
|
+
* @param {number|null} detectedAtUnixSeconds - Unix timestamp of detection
|
|
4549
|
+
*/
|
|
4550
|
+
constructor(kind, message, detectedAtUnixSeconds) {
|
|
4551
|
+
this.kind = kind;
|
|
4552
|
+
this.message = message;
|
|
4553
|
+
this.detected_at_unix_seconds = detectedAtUnixSeconds;
|
|
4554
|
+
}
|
|
4555
|
+
|
|
4556
|
+
toJSON() {
|
|
4557
|
+
return {
|
|
4558
|
+
kind: this.kind,
|
|
4559
|
+
message: this.message,
|
|
4560
|
+
detected_at_unix_seconds: this.detected_at_unix_seconds,
|
|
4561
|
+
};
|
|
4562
|
+
}
|
|
4563
|
+
|
|
4564
|
+
static fromJSON(d) {
|
|
4565
|
+
return new CartridgeAttachmentError(d.kind, d.message, d.detected_at_unix_seconds);
|
|
4566
|
+
}
|
|
4567
|
+
}
|
|
4568
|
+
|
|
4569
|
+
/**
|
|
4570
|
+
* Runtime statistics for a running (or stopped) cartridge process.
|
|
4571
|
+
* Mirrors Rust CartridgeRuntimeStats.
|
|
4572
|
+
*/
|
|
4573
|
+
class CartridgeRuntimeStats {
|
|
4574
|
+
/**
|
|
4575
|
+
* @param {Object} opts
|
|
4576
|
+
* @param {boolean} [opts.running=false]
|
|
4577
|
+
* @param {number|null} [opts.pid=null]
|
|
4578
|
+
* @param {number} [opts.activeRequestCount=0]
|
|
4579
|
+
* @param {number} [opts.peerRequestCount=0]
|
|
4580
|
+
* @param {number} [opts.memoryFootprintMb=0]
|
|
4581
|
+
* @param {number} [opts.memoryRssMb=0]
|
|
4582
|
+
* @param {number|null} [opts.lastHeartbeatUnixSeconds=null]
|
|
4583
|
+
* @param {number} [opts.restartCount=0]
|
|
4584
|
+
*/
|
|
4585
|
+
constructor({
|
|
4586
|
+
running = false,
|
|
4587
|
+
pid = null,
|
|
4588
|
+
activeRequestCount = 0,
|
|
4589
|
+
peerRequestCount = 0,
|
|
4590
|
+
memoryFootprintMb = 0,
|
|
4591
|
+
memoryRssMb = 0,
|
|
4592
|
+
lastHeartbeatUnixSeconds = null,
|
|
4593
|
+
restartCount = 0,
|
|
4594
|
+
} = {}) {
|
|
4595
|
+
this.running = running;
|
|
4596
|
+
this.pid = pid;
|
|
4597
|
+
this.active_request_count = activeRequestCount;
|
|
4598
|
+
this.peer_request_count = peerRequestCount;
|
|
4599
|
+
this.memory_footprint_mb = memoryFootprintMb;
|
|
4600
|
+
this.memory_rss_mb = memoryRssMb;
|
|
4601
|
+
this.last_heartbeat_unix_seconds = lastHeartbeatUnixSeconds;
|
|
4602
|
+
this.restart_count = restartCount;
|
|
4603
|
+
}
|
|
4604
|
+
|
|
4605
|
+
/** Convenience constructor for a cartridge that is not running. */
|
|
4606
|
+
static notRunning() {
|
|
4607
|
+
return new CartridgeRuntimeStats();
|
|
4608
|
+
}
|
|
4609
|
+
|
|
4610
|
+
toJSON() {
|
|
4611
|
+
const obj = { running: this.running };
|
|
4612
|
+
if (this.pid !== null) obj.pid = this.pid;
|
|
4613
|
+
if (this.active_request_count) obj.active_request_count = this.active_request_count;
|
|
4614
|
+
if (this.peer_request_count) obj.peer_request_count = this.peer_request_count;
|
|
4615
|
+
if (this.memory_footprint_mb) obj.memory_footprint_mb = this.memory_footprint_mb;
|
|
4616
|
+
if (this.memory_rss_mb) obj.memory_rss_mb = this.memory_rss_mb;
|
|
4617
|
+
if (this.last_heartbeat_unix_seconds !== null) obj.last_heartbeat_unix_seconds = this.last_heartbeat_unix_seconds;
|
|
4618
|
+
if (this.restart_count) obj.restart_count = this.restart_count;
|
|
4619
|
+
return obj;
|
|
4620
|
+
}
|
|
4621
|
+
|
|
4622
|
+
static fromJSON(d) {
|
|
4623
|
+
return new CartridgeRuntimeStats({
|
|
4624
|
+
running: d.running || false,
|
|
4625
|
+
pid: d.pid !== undefined ? d.pid : null,
|
|
4626
|
+
activeRequestCount: d.active_request_count || 0,
|
|
4627
|
+
peerRequestCount: d.peer_request_count || 0,
|
|
4628
|
+
memoryFootprintMb: d.memory_footprint_mb || 0,
|
|
4629
|
+
memoryRssMb: d.memory_rss_mb || 0,
|
|
4630
|
+
lastHeartbeatUnixSeconds: d.last_heartbeat_unix_seconds !== undefined ? d.last_heartbeat_unix_seconds : null,
|
|
4631
|
+
restartCount: d.restart_count || 0,
|
|
4632
|
+
});
|
|
4633
|
+
}
|
|
4634
|
+
}
|
|
4635
|
+
|
|
4636
|
+
/**
|
|
4637
|
+
* Full identity of an installed cartridge, including optional attachment error
|
|
4638
|
+
* and runtime statistics.
|
|
4639
|
+
* Mirrors Rust InstalledCartridgeIdentity.
|
|
4640
|
+
*/
|
|
4641
|
+
class InstalledCartridgeIdentity {
|
|
4642
|
+
/**
|
|
4643
|
+
* @param {Object} opts
|
|
4644
|
+
* @param {string|null} [opts.registryUrl=null]
|
|
4645
|
+
* @param {string} opts.channel
|
|
4646
|
+
* @param {string} opts.id
|
|
4647
|
+
* @param {string} opts.version
|
|
4648
|
+
* @param {string} opts.sha256
|
|
4649
|
+
* @param {CartridgeAttachmentError|null} [opts.attachmentError=null]
|
|
4650
|
+
* @param {CartridgeRuntimeStats|null} [opts.runtimeStats=null]
|
|
4651
|
+
*/
|
|
4652
|
+
constructor({ registryUrl = null, channel, id, version, sha256, attachmentError = null, runtimeStats = null }) {
|
|
4653
|
+
this.registry_url = registryUrl;
|
|
4654
|
+
this.channel = channel;
|
|
4655
|
+
this.id = id;
|
|
4656
|
+
this.version = version;
|
|
4657
|
+
this.sha256 = sha256;
|
|
4658
|
+
this.attachment_error = attachmentError;
|
|
4659
|
+
this.runtime_stats = runtimeStats;
|
|
4660
|
+
}
|
|
4661
|
+
|
|
4662
|
+
toJSON() {
|
|
4663
|
+
const obj = {
|
|
4664
|
+
channel: this.channel,
|
|
4665
|
+
id: this.id,
|
|
4666
|
+
version: this.version,
|
|
4667
|
+
sha256: this.sha256,
|
|
4668
|
+
};
|
|
4669
|
+
if (this.registry_url !== null) obj.registry_url = this.registry_url;
|
|
4670
|
+
if (this.attachment_error !== null) obj.attachment_error = this.attachment_error.toJSON();
|
|
4671
|
+
if (this.runtime_stats !== null) obj.runtime_stats = this.runtime_stats.toJSON();
|
|
4672
|
+
return obj;
|
|
4673
|
+
}
|
|
4674
|
+
|
|
4675
|
+
static fromJSON(d) {
|
|
4676
|
+
return new InstalledCartridgeIdentity({
|
|
4677
|
+
registryUrl: d.registry_url !== undefined ? d.registry_url : null,
|
|
4678
|
+
channel: d.channel,
|
|
4679
|
+
id: d.id,
|
|
4680
|
+
version: d.version,
|
|
4681
|
+
sha256: d.sha256,
|
|
4682
|
+
attachmentError: d.attachment_error ? CartridgeAttachmentError.fromJSON(d.attachment_error) : null,
|
|
4683
|
+
runtimeStats: d.runtime_stats ? CartridgeRuntimeStats.fromJSON(d.runtime_stats) : null,
|
|
4684
|
+
});
|
|
4685
|
+
}
|
|
4686
|
+
}
|
|
4687
|
+
|
|
4286
4688
|
// ============================================================================
|
|
4287
4689
|
// Machine Notation — compact, round-trippable DAG path identifiers
|
|
4288
4690
|
//
|
|
@@ -5183,16 +5585,16 @@ class MachineBuilder {
|
|
|
5183
5585
|
}
|
|
5184
5586
|
|
|
5185
5587
|
/**
|
|
5186
|
-
* Add a linear chain of edges from
|
|
5588
|
+
* Add a linear chain of edges from CapFabEdge[] (from CapFab.findAllPaths).
|
|
5187
5589
|
*
|
|
5188
|
-
* Each
|
|
5590
|
+
* Each CapFabEdge has fromUrn, toUrn, and cap (with cap.urn).
|
|
5189
5591
|
* This converts the path into a series of MachineEdges.
|
|
5190
5592
|
*
|
|
5191
|
-
* @param {
|
|
5593
|
+
* @param {CapFabEdge[]} capFabEdges - Array of CapFabEdge from pathfinding
|
|
5192
5594
|
* @returns {MachineBuilder} this (for chaining)
|
|
5193
5595
|
*/
|
|
5194
|
-
|
|
5195
|
-
for (const edge of
|
|
5596
|
+
addCapFabPath(capFabEdges) {
|
|
5597
|
+
for (const edge of capFabEdges) {
|
|
5196
5598
|
const source = MediaUrn.fromString(edge.fromUrn);
|
|
5197
5599
|
const target = MediaUrn.fromString(edge.toUrn);
|
|
5198
5600
|
this._edges.push(new MachineEdge([source], edge.cap.urn, target, false));
|
|
@@ -5393,6 +5795,8 @@ module.exports = {
|
|
|
5393
5795
|
MediaUrnError,
|
|
5394
5796
|
MediaUrnErrorCodes,
|
|
5395
5797
|
Cap,
|
|
5798
|
+
CapGroup,
|
|
5799
|
+
CapManifest,
|
|
5396
5800
|
CapArg,
|
|
5397
5801
|
ArgSource,
|
|
5398
5802
|
RegisteredBy,
|
|
@@ -5437,8 +5841,25 @@ module.exports = {
|
|
|
5437
5841
|
MEDIA_IDENTITY,
|
|
5438
5842
|
MEDIA_VOID,
|
|
5439
5843
|
MEDIA_PNG,
|
|
5844
|
+
MEDIA_JPEG,
|
|
5845
|
+
MEDIA_GIF,
|
|
5846
|
+
MEDIA_BMP,
|
|
5847
|
+
MEDIA_TIFF,
|
|
5848
|
+
MEDIA_WEBP,
|
|
5440
5849
|
MEDIA_AUDIO,
|
|
5850
|
+
MEDIA_MP3,
|
|
5851
|
+
MEDIA_WAV,
|
|
5852
|
+
MEDIA_FLAC,
|
|
5853
|
+
MEDIA_OGG,
|
|
5854
|
+
MEDIA_AAC,
|
|
5855
|
+
MEDIA_M4A,
|
|
5856
|
+
MEDIA_AIFF,
|
|
5857
|
+
MEDIA_OPUS,
|
|
5441
5858
|
MEDIA_VIDEO,
|
|
5859
|
+
MEDIA_MP4,
|
|
5860
|
+
MEDIA_MOV,
|
|
5861
|
+
MEDIA_WEBM,
|
|
5862
|
+
MEDIA_MKV,
|
|
5442
5863
|
MEDIA_AUDIO_SPEECH,
|
|
5443
5864
|
// Document types (PRIMARY naming)
|
|
5444
5865
|
MEDIA_PDF,
|
|
@@ -5484,6 +5905,12 @@ module.exports = {
|
|
|
5484
5905
|
// File path type — single URN; cardinality lives on is_sequence.
|
|
5485
5906
|
MEDIA_FILE_PATH,
|
|
5486
5907
|
MEDIA_MLX_MODEL_PATH,
|
|
5908
|
+
// HF token and model search types
|
|
5909
|
+
MEDIA_HF_TOKEN,
|
|
5910
|
+
MEDIA_MODEL_ARCH_LIST,
|
|
5911
|
+
MEDIA_MODEL_SEARCH_REQUEST,
|
|
5912
|
+
MEDIA_MODEL_SEARCH_RESPONSE,
|
|
5913
|
+
MEDIA_MODEL_FILTER_RESOLUTION,
|
|
5487
5914
|
// Collection types
|
|
5488
5915
|
MEDIA_COLLECTION,
|
|
5489
5916
|
MEDIA_COLLECTION_LIST,
|
|
@@ -5501,9 +5928,9 @@ module.exports = {
|
|
|
5501
5928
|
mediaUrnForType,
|
|
5502
5929
|
modelAvailabilityUrn,
|
|
5503
5930
|
modelPathUrn,
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5931
|
+
CapFabEdge,
|
|
5932
|
+
CapFabStats,
|
|
5933
|
+
CapFab,
|
|
5507
5934
|
StdinSource,
|
|
5508
5935
|
StdinSourceKind,
|
|
5509
5936
|
// Cartridge Repository
|
|
@@ -5514,6 +5941,11 @@ module.exports = {
|
|
|
5514
5941
|
CartridgeRepoClient,
|
|
5515
5942
|
CartridgeRepoServer,
|
|
5516
5943
|
CartridgeChannel,
|
|
5944
|
+
// Bifaci — cartridge attachment & runtime identity
|
|
5945
|
+
CartridgeAttachmentErrorKind,
|
|
5946
|
+
CartridgeAttachmentError,
|
|
5947
|
+
CartridgeRuntimeStats,
|
|
5948
|
+
InstalledCartridgeIdentity,
|
|
5517
5949
|
// Registry slug
|
|
5518
5950
|
DEV_SLUG,
|
|
5519
5951
|
SLUG_HEX_LEN,
|