@camstack/server 1.0.8 → 1.1.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/dist/api/addon-upload.js +73 -8
- package/dist/api/deploy-stage-registry.js +60 -0
- package/dist/api/model-distributor.js +128 -0
- package/dist/api/trpc/generated-cap-mounts.js +20 -1
- package/dist/api/trpc/generated-cap-routers.js +1068 -1025
- package/dist/api/trpc/trpc.router.js +1 -0
- package/dist/core/addon/addon-package.service.js +5 -3
- package/dist/core/addon/addon-registry.service.js +7 -3
- package/dist/core/addon/addon-settings-provider.js +7 -3
- package/dist/core/agent/agent-registry.service.js +9 -1
- package/dist/core/auth/auth.service.js +0 -3
- package/dist/core/config/config.service.js +0 -3
- package/dist/core/feature/feature.service.js +0 -3
- package/dist/core/moleculer/moleculer.service.js +5 -5
- package/dist/main.js +16 -8
- package/package.json +1 -1
package/dist/api/addon-upload.js
CHANGED
|
@@ -33,6 +33,9 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.getDeployStageRegistry = getDeployStageRegistry;
|
|
37
|
+
exports.registerDeployBundleRoute = registerDeployBundleRoute;
|
|
38
|
+
exports.buildHubHttpSource = buildHubHttpSource;
|
|
36
39
|
exports.registerAddonUploadRoute = registerAddonUploadRoute;
|
|
37
40
|
const fs = __importStar(require("node:fs"));
|
|
38
41
|
const path = __importStar(require("node:path"));
|
|
@@ -40,6 +43,40 @@ const os = __importStar(require("node:os"));
|
|
|
40
43
|
const node_child_process_1 = require("node:child_process");
|
|
41
44
|
const scope_access_js_1 = require("./trpc/scope-access.js");
|
|
42
45
|
const addon_package_service_js_1 = require("../core/addon/addon-package.service.js");
|
|
46
|
+
const deploy_stage_registry_js_1 = require("./deploy-stage-registry.js");
|
|
47
|
+
const deployStageRegistry = new deploy_stage_registry_js_1.DeployStageRegistry();
|
|
48
|
+
function getDeployStageRegistry() {
|
|
49
|
+
return deployStageRegistry;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* One-time-token route agents pull a staged addon tgz from. Bearer token is
|
|
53
|
+
* the per-deploy token minted in `DeployStageRegistry.stage()` and carried to
|
|
54
|
+
* the agent in the `$agent.deploy` `hub-http` source descriptor.
|
|
55
|
+
*/
|
|
56
|
+
function registerDeployBundleRoute(fastify) {
|
|
57
|
+
// `compress: false` — never gzip/brotli this route. The body is a large
|
|
58
|
+
// binary tarball (addon bundle / model artifact) the agent pulls with undici
|
|
59
|
+
// + sha256 verification. With global @fastify/compress on, the 1–2MB response
|
|
60
|
+
// was Brotli-encoded and undici's BrotliDecompress aborted the stream
|
|
61
|
+
// ("TypeError: terminated"), silently breaking EVERY agent-pull (addon-deploy
|
|
62
|
+
// AND model distribution). Identity bytes also keep the byte-length check valid.
|
|
63
|
+
fastify.get('/api/addons/deploy-bundle/:id', { compress: false }, (request, reply) => {
|
|
64
|
+
const auth = request.headers.authorization ?? '';
|
|
65
|
+
const token = auth.startsWith('Bearer ') ? auth.slice('Bearer '.length) : '';
|
|
66
|
+
if (!token) {
|
|
67
|
+
reply.status(401).send({ error: 'missing bearer token' });
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const buffer = deployStageRegistry.consume(request.params.id, token);
|
|
71
|
+
if (!buffer) {
|
|
72
|
+
reply
|
|
73
|
+
.status(deployStageRegistry.peekExists(request.params.id) ? 401 : 404)
|
|
74
|
+
.send({ error: 'bundle not available' });
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
reply.header('content-type', 'application/octet-stream').send(buffer);
|
|
78
|
+
});
|
|
79
|
+
}
|
|
43
80
|
/**
|
|
44
81
|
* Validate a `cst_*` scoped token via the `user-management` cap singleton.
|
|
45
82
|
*
|
|
@@ -76,6 +113,20 @@ function isUploadAllowed(scoped) {
|
|
|
76
113
|
const TARBALL_EXTENSIONS = ['.tgz', '.tar.gz'];
|
|
77
114
|
const MAX_UPLOAD_BYTES = 50 * 1024 * 1024;
|
|
78
115
|
const AGENT_DEPLOY_TIMEOUT_MS = 60_000;
|
|
116
|
+
const AGENT_DEPLOY_CONTROL_TIMEOUT_MS = 30_000;
|
|
117
|
+
function hubBundleBaseUrl() {
|
|
118
|
+
return process.env['CAMSTACK_HUB_PUBLIC_URL'] ?? 'https://127.0.0.1:4443';
|
|
119
|
+
}
|
|
120
|
+
function buildHubHttpSource(buffer, hubBaseUrl) {
|
|
121
|
+
const staged = getDeployStageRegistry().stage(buffer);
|
|
122
|
+
return {
|
|
123
|
+
kind: 'hub-http',
|
|
124
|
+
url: `${hubBaseUrl.replace(/\/$/, '')}/api/addons/deploy-bundle/${staged.id}`,
|
|
125
|
+
token: staged.token,
|
|
126
|
+
sha256: staged.sha256,
|
|
127
|
+
bytes: staged.bytes,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
79
130
|
function isTarball(filename) {
|
|
80
131
|
return TARBALL_EXTENSIONS.some((ext) => filename.endsWith(ext));
|
|
81
132
|
}
|
|
@@ -119,6 +170,7 @@ async function registerAddonUploadRoute(fastify, addonBridge, authService, molec
|
|
|
119
170
|
await fastify.register(Promise.resolve().then(() => __importStar(require('@fastify/multipart'))), {
|
|
120
171
|
limits: { fileSize: MAX_UPLOAD_BYTES },
|
|
121
172
|
});
|
|
173
|
+
registerDeployBundleRoute(fastify);
|
|
122
174
|
fastify.post('/api/addons/upload', async (request, reply) => {
|
|
123
175
|
const authHeader = request.headers.authorization;
|
|
124
176
|
if (!authHeader) {
|
|
@@ -206,11 +258,12 @@ async function registerAddonUploadRoute(fastify, addonBridge, authService, molec
|
|
|
206
258
|
// the hub AND broadcasts to every connected agent that can run any of
|
|
207
259
|
// the package's addons (i.e. anything not marked hub-only). Any other
|
|
208
260
|
// explicit `nodeId` value routes only to that agent via `$agent.deploy`.
|
|
261
|
+
const baseUrl = hubBundleBaseUrl();
|
|
209
262
|
if (!nodeId || nodeId === 'hub') {
|
|
210
|
-
return installToHub(reply, addonBridge, addonRegistry, addonPackageService, moleculer, logger, data.filename, buffer);
|
|
263
|
+
return installToHub(reply, addonBridge, addonRegistry, addonPackageService, moleculer, logger, data.filename, buffer, baseUrl);
|
|
211
264
|
}
|
|
212
265
|
const agentAddonId = addonIdHint ?? manifest.name;
|
|
213
|
-
return deployToAgent(reply, moleculer, nodeId, agentAddonId, buffer);
|
|
266
|
+
return deployToAgent(reply, moleculer, nodeId, agentAddonId, buffer, baseUrl);
|
|
214
267
|
});
|
|
215
268
|
}
|
|
216
269
|
/**
|
|
@@ -248,7 +301,7 @@ function packageHasAgentDeployable(addonsDir, packageName) {
|
|
|
248
301
|
* without an agent restart. Agents that fail are reported per-node — one
|
|
249
302
|
* unreachable agent must not block the others or the hub install.
|
|
250
303
|
*/
|
|
251
|
-
async function propagateToAgents(moleculer, logger, packageName, buffer) {
|
|
304
|
+
async function propagateToAgents(moleculer, logger, packageName, buffer, hubBaseUrl) {
|
|
252
305
|
const broker = moleculer.broker;
|
|
253
306
|
const nodes = broker.registry?.getNodeList?.({ onlyAvailable: true }) ?? [];
|
|
254
307
|
// Moleculer reports both top-level nodes (`hub`, `dev-agent-0`, …) AND
|
|
@@ -270,7 +323,13 @@ async function propagateToAgents(moleculer, logger, packageName, buffer) {
|
|
|
270
323
|
const results = [];
|
|
271
324
|
for (const nodeId of agentNodeIds) {
|
|
272
325
|
try {
|
|
273
|
-
|
|
326
|
+
// The hub currently only ever sends the `hub-http` source (agent streams
|
|
327
|
+
// the tgz directly from this hub). The `{kind:'npm'}` source is a reserved
|
|
328
|
+
// follow-up (published-addon optimization) and is intentionally not produced
|
|
329
|
+
// yet. Wiring it later must also make the agent's npm path evict the addon's
|
|
330
|
+
// declaration ids before `$agent.reload`, otherwise an npm update pins the
|
|
331
|
+
// stale version.
|
|
332
|
+
const deployRaw = await broker.call('$agent.deploy', { addonId: packageName, source: buildHubHttpSource(buffer, hubBaseUrl) }, { nodeID: nodeId, timeout: AGENT_DEPLOY_CONTROL_TIMEOUT_MS });
|
|
274
333
|
if (!isAgentDeployResponse(deployRaw)) {
|
|
275
334
|
results.push({ nodeId, success: false, error: 'malformed deploy response' });
|
|
276
335
|
continue;
|
|
@@ -302,7 +361,7 @@ async function propagateToAgents(moleculer, logger, packageName, buffer) {
|
|
|
302
361
|
* Without this the CLI push was write-to-disk-only and required a server
|
|
303
362
|
* restart to actually run the new code.
|
|
304
363
|
*/
|
|
305
|
-
async function installToHub(reply, addonBridge, addonRegistry, addonPackageService, moleculer, logger, filename, buffer) {
|
|
364
|
+
async function installToHub(reply, addonBridge, addonRegistry, addonPackageService, moleculer, logger, filename, buffer, hubBaseUrl) {
|
|
306
365
|
const tmpDir = path.join(os.tmpdir(), `camstack-addon-upload-${Date.now()}`);
|
|
307
366
|
fs.mkdirSync(tmpDir, { recursive: true });
|
|
308
367
|
const tgzPath = path.join(tmpDir, filename);
|
|
@@ -401,7 +460,7 @@ async function installToHub(reply, addonBridge, addonRegistry, addonPackageServi
|
|
|
401
460
|
});
|
|
402
461
|
}
|
|
403
462
|
if (propagatable) {
|
|
404
|
-
void propagateToAgents(moleculer, logger, result.name, buffer).then((agentResults) => {
|
|
463
|
+
void propagateToAgents(moleculer, logger, result.name, buffer, hubBaseUrl).then((agentResults) => {
|
|
405
464
|
logger.info('propagation done', {
|
|
406
465
|
meta: { packageName: result.name, agents: agentResults },
|
|
407
466
|
});
|
|
@@ -434,10 +493,16 @@ function isAgentDeployResponse(value) {
|
|
|
434
493
|
return false;
|
|
435
494
|
return true;
|
|
436
495
|
}
|
|
437
|
-
async function deployToAgent(reply, moleculer, nodeId, addonId, buffer) {
|
|
496
|
+
async function deployToAgent(reply, moleculer, nodeId, addonId, buffer, hubBaseUrl) {
|
|
438
497
|
try {
|
|
439
498
|
const broker = moleculer.broker;
|
|
440
|
-
|
|
499
|
+
// The hub currently only ever sends the `hub-http` source (agent streams
|
|
500
|
+
// the tgz directly from this hub). The `{kind:'npm'}` source is a reserved
|
|
501
|
+
// follow-up (published-addon optimization) and is intentionally not produced
|
|
502
|
+
// yet. Wiring it later must also make the agent's npm path evict the addon's
|
|
503
|
+
// declaration ids before `$agent.reload`, otherwise an npm update pins the
|
|
504
|
+
// stale version.
|
|
505
|
+
const raw = await broker.call('$agent.deploy', { addonId, source: buildHubHttpSource(buffer, hubBaseUrl) }, { nodeID: nodeId, timeout: AGENT_DEPLOY_CONTROL_TIMEOUT_MS });
|
|
441
506
|
if (!isAgentDeployResponse(raw)) {
|
|
442
507
|
return reply.status(502).send({ error: 'Agent deploy returned malformed response' });
|
|
443
508
|
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DeployStageRegistry = void 0;
|
|
4
|
+
const node_crypto_1 = require("node:crypto");
|
|
5
|
+
/**
|
|
6
|
+
* In-memory, single-use, TTL-bounded store of staged addon tarballs the hub
|
|
7
|
+
* serves to agents over HTTP. One entry per deploy target; the token is
|
|
8
|
+
* consumed on the first successful read so a bundle URL is never reusable.
|
|
9
|
+
*/
|
|
10
|
+
class DeployStageRegistry {
|
|
11
|
+
entries = new Map();
|
|
12
|
+
ttlMs;
|
|
13
|
+
now;
|
|
14
|
+
constructor(opts) {
|
|
15
|
+
this.ttlMs = opts?.ttlMs ?? 300_000;
|
|
16
|
+
this.now = opts?.now ?? Date.now;
|
|
17
|
+
}
|
|
18
|
+
stage(buffer) {
|
|
19
|
+
// Reclaim abandoned (never-consumed) entries before inserting a new one.
|
|
20
|
+
this.sweepExpired();
|
|
21
|
+
const id = (0, node_crypto_1.randomUUID)();
|
|
22
|
+
const token = (0, node_crypto_1.randomUUID)();
|
|
23
|
+
const sha256 = (0, node_crypto_1.createHash)('sha256').update(buffer).digest('hex');
|
|
24
|
+
this.entries.set(id, { token, buffer, sha256, expiresAt: this.now() + this.ttlMs });
|
|
25
|
+
return { id, token, sha256, bytes: buffer.length };
|
|
26
|
+
}
|
|
27
|
+
consume(id, token) {
|
|
28
|
+
const entry = this.entries.get(id);
|
|
29
|
+
if (!entry)
|
|
30
|
+
return null;
|
|
31
|
+
if (this.now() > entry.expiresAt) {
|
|
32
|
+
this.entries.delete(id);
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
// Constant-time comparison: different byte lengths → not equal (no timing oracle).
|
|
36
|
+
const entryBuf = Buffer.from(entry.token);
|
|
37
|
+
const tokenBuf = Buffer.from(token);
|
|
38
|
+
if (entryBuf.length !== tokenBuf.length || !(0, node_crypto_1.timingSafeEqual)(entryBuf, tokenBuf))
|
|
39
|
+
return null;
|
|
40
|
+
this.entries.delete(id); // single-use
|
|
41
|
+
return entry.buffer;
|
|
42
|
+
}
|
|
43
|
+
peekExists(id) {
|
|
44
|
+
const e = this.entries.get(id);
|
|
45
|
+
if (!e)
|
|
46
|
+
return false;
|
|
47
|
+
if (this.now() > e.expiresAt) {
|
|
48
|
+
this.entries.delete(id);
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
sweepExpired() {
|
|
54
|
+
const t = this.now();
|
|
55
|
+
for (const [id, e] of this.entries)
|
|
56
|
+
if (t > e.expiresAt)
|
|
57
|
+
this.entries.delete(id);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
exports.DeployStageRegistry = DeployStageRegistry;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.tarModelFiles = tarModelFiles;
|
|
37
|
+
exports.distributeModelWith = distributeModelWith;
|
|
38
|
+
exports.buildModelDistributorProvider = buildModelDistributorProvider;
|
|
39
|
+
const fs = __importStar(require("node:fs"));
|
|
40
|
+
const path = __importStar(require("node:path"));
|
|
41
|
+
const node_child_process_1 = require("node:child_process");
|
|
42
|
+
const system_1 = require("@camstack/system");
|
|
43
|
+
const addon_upload_js_1 = require("./addon-upload.js");
|
|
44
|
+
const AGENT_DISTRIBUTE_TIMEOUT_MS = 60_000;
|
|
45
|
+
function hubModelsDir() {
|
|
46
|
+
return path.join(process.env['CAMSTACK_DATA'] ?? 'camstack-data', 'models');
|
|
47
|
+
}
|
|
48
|
+
function hubBundleBaseUrl() {
|
|
49
|
+
return process.env['CAMSTACK_HUB_PUBLIC_URL'] ?? 'https://127.0.0.1:4443';
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* `tar -czf -` the given files relative to `cwd`, buffering the gzip stream.
|
|
53
|
+
* Async (spawn, not execFileSync) so a large model doesn't block the event
|
|
54
|
+
* loop. The agent untars this exact buffer into its own modelsDir.
|
|
55
|
+
*/
|
|
56
|
+
function tarModelFiles(cwd, files) {
|
|
57
|
+
return new Promise((resolve, reject) => {
|
|
58
|
+
const child = (0, node_child_process_1.spawn)('tar', ['-czf', '-', '-C', cwd, ...files]);
|
|
59
|
+
const chunks = [];
|
|
60
|
+
const errChunks = [];
|
|
61
|
+
child.stdout.on('data', (c) => chunks.push(c));
|
|
62
|
+
child.stderr.on('data', (c) => errChunks.push(c));
|
|
63
|
+
child.on('error', reject);
|
|
64
|
+
child.on('close', (code) => {
|
|
65
|
+
if (code === 0)
|
|
66
|
+
resolve(Buffer.concat(chunks));
|
|
67
|
+
else
|
|
68
|
+
reject(new Error(`tar exited ${code}: ${Buffer.concat(errChunks).toString().slice(0, 300)}`));
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Core distribution logic (pure orchestration over the seam):
|
|
74
|
+
* precondition-check → collect → tar → stage → trigger the agent pull.
|
|
75
|
+
* `nodeId === 'hub'` is a no-op (the artifact is already resident).
|
|
76
|
+
*/
|
|
77
|
+
async function distributeModelWith(seam, input) {
|
|
78
|
+
const { nodeId, modelId, format, entry } = input;
|
|
79
|
+
if (nodeId === 'hub') {
|
|
80
|
+
return { ok: true, sha256: '', bytes: 0, path: seam.modelsDir };
|
|
81
|
+
}
|
|
82
|
+
if (!seam.isPresent(entry, format)) {
|
|
83
|
+
throw new Error(`model ${modelId}/${format} not present on hub — download or convert it first`);
|
|
84
|
+
}
|
|
85
|
+
const files = (0, system_1.collectModelFiles)(entry, format).filter((f) => seam.fileExists(f));
|
|
86
|
+
if (files.length === 0) {
|
|
87
|
+
throw new Error(`model ${modelId}/${format} resolved no files to distribute`);
|
|
88
|
+
}
|
|
89
|
+
const tgz = await seam.tar(files);
|
|
90
|
+
const source = seam.stage(tgz);
|
|
91
|
+
const res = await seam.callAgent(nodeId, { modelId, format, source });
|
|
92
|
+
return { ok: true, sha256: source.sha256, bytes: source.bytes, path: res.path ?? '' };
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Build the hub-resident `model-distributor` singleton provider. Mounted as a
|
|
96
|
+
* service-backed cap override in `trpc.router.ts` because it needs the
|
|
97
|
+
* Moleculer broker + the deploy-stage registry, which a forked addon can't
|
|
98
|
+
* reach.
|
|
99
|
+
*/
|
|
100
|
+
function buildModelDistributorProvider(moleculer, loggingService) {
|
|
101
|
+
const logger = loggingService.createLogger('model-distributor');
|
|
102
|
+
const modelsDir = hubModelsDir();
|
|
103
|
+
const seam = {
|
|
104
|
+
modelsDir,
|
|
105
|
+
isPresent: (entry, format) => (0, system_1.isModelDownloaded)(modelsDir, entry, format),
|
|
106
|
+
fileExists: (rel) => fs.existsSync(path.join(modelsDir, rel)),
|
|
107
|
+
tar: (files) => tarModelFiles(modelsDir, files),
|
|
108
|
+
stage: (buffer) => (0, addon_upload_js_1.buildHubHttpSource)(buffer, hubBundleBaseUrl()),
|
|
109
|
+
callAgent: async (nodeId, payload) => {
|
|
110
|
+
const broker = moleculer?.broker;
|
|
111
|
+
if (!broker)
|
|
112
|
+
throw new Error('model-distributor: Moleculer broker unavailable');
|
|
113
|
+
const raw = await broker.call('$agent.distributeModel', payload, {
|
|
114
|
+
nodeID: nodeId,
|
|
115
|
+
timeout: AGENT_DISTRIBUTE_TIMEOUT_MS,
|
|
116
|
+
});
|
|
117
|
+
return (raw ?? {});
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
return {
|
|
121
|
+
distributeModel: async (input) => {
|
|
122
|
+
logger.info('Distributing model to node', {
|
|
123
|
+
meta: { modelId: input.modelId, format: input.format, nodeId: input.nodeId },
|
|
124
|
+
});
|
|
125
|
+
return distributeModelWith(seam, input);
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// AUTO-GENERATED by scripts/generate-cap-mounts.ts — DO NOT EDIT
|
|
3
3
|
// Re-run: npx tsx scripts/generate-cap-mounts.ts
|
|
4
4
|
//
|
|
5
|
-
// Mounted:
|
|
5
|
+
// Mounted: 130 Skipped (legacy): 6
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
7
|
exports.LEGACY_SHAPE_SKIP = void 0;
|
|
8
8
|
exports.mountAllCaps = mountAllCaps;
|
|
@@ -121,6 +121,22 @@ function mountAllCaps(services) {
|
|
|
121
121
|
contact: (0, generated_cap_routers_js_1.createCapRouter_contact)((_ctx) => (0, cap_mount_helpers_js_1.requireDeviceScoped)(reg, 'contact'), remoteCapProxy),
|
|
122
122
|
control: (0, generated_cap_routers_js_1.createCapRouter_control)((_ctx) => (0, cap_mount_helpers_js_1.requireDeviceScoped)(reg, 'control'), remoteCapProxy),
|
|
123
123
|
cover: (0, generated_cap_routers_js_1.createCapRouter_cover)((_ctx) => (0, cap_mount_helpers_js_1.requireDeviceScoped)(reg, 'cover'), remoteCapProxy),
|
|
124
|
+
customModelRegistry: (0, generated_cap_routers_js_1.createCapRouter_customModelRegistry)((_ctx, addonId) => {
|
|
125
|
+
if (!reg)
|
|
126
|
+
return null;
|
|
127
|
+
if (addonId !== undefined) {
|
|
128
|
+
return reg.getProviderByAddonId('custom-model-registry', addonId);
|
|
129
|
+
}
|
|
130
|
+
const entries = reg.getCollectionEntries('custom-model-registry');
|
|
131
|
+
if (entries.length === 0)
|
|
132
|
+
return null;
|
|
133
|
+
const providers = entries.map(([, p]) => p);
|
|
134
|
+
const first = providers[0];
|
|
135
|
+
return {
|
|
136
|
+
...first,
|
|
137
|
+
listModels: (0, cap_mount_helpers_js_1.concatCollection)(providers, 'listModels'),
|
|
138
|
+
};
|
|
139
|
+
}, remoteCapProxy),
|
|
124
140
|
decoder: (0, generated_cap_routers_js_1.createCapRouter_decoder)((_ctx) => reg?.getSingleton('decoder') ?? null, remoteCapProxy),
|
|
125
141
|
detectionPipeline: (0, generated_cap_routers_js_1.createCapRouter_detectionPipeline)((_ctx) => reg?.getSingleton('detection-pipeline') ?? null, remoteCapProxy),
|
|
126
142
|
deviceAdoption: (0, generated_cap_routers_js_1.createCapRouter_deviceAdoption)((_ctx) => reg?.getSingleton('device-adoption') ??
|
|
@@ -204,6 +220,9 @@ function mountAllCaps(services) {
|
|
|
204
220
|
}, remoteCapProxy),
|
|
205
221
|
metricsProvider: (0, generated_cap_routers_js_1.createCapRouter_metricsProvider)((_ctx) => reg?.getSingleton('metrics-provider') ??
|
|
206
222
|
null, remoteCapProxy),
|
|
223
|
+
modelConvert: (0, generated_cap_routers_js_1.createCapRouter_modelConvert)((_ctx) => reg?.getSingleton('model-convert') ?? null, remoteCapProxy),
|
|
224
|
+
modelDistributor: (0, generated_cap_routers_js_1.createCapRouter_modelDistributor)((_ctx) => reg?.getSingleton('model-distributor') ??
|
|
225
|
+
null, remoteCapProxy),
|
|
207
226
|
motion: (0, generated_cap_routers_js_1.createCapRouter_motion)((_ctx) => reg?.getSingleton('motion') ?? null, remoteCapProxy),
|
|
208
227
|
motionDetection: (0, generated_cap_routers_js_1.createCapRouter_motionDetection)((_ctx) => reg?.getSingleton('motion-detection') ??
|
|
209
228
|
null, remoteCapProxy),
|