@camstack/server 1.2.476 → 1.2.478
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/agent/agent-service.js +27 -71
- package/dist/agent/agent-undeploy.js +255 -0
- package/dist/agent/main.js +13 -0
- package/dist/api/core/cap-providers.js +91 -3
- package/dist/api/trpc/generated-cap-routers.js +8 -199
- package/dist/api/trpc/trpc.router.js +1 -1
- package/dist/core/addon/addon-uninstall.js +259 -0
- package/dist/core/addon-bridge/addon-bridge.service.js +5 -5
- package/dist/core/agent/agent-addon-backfill.js +20 -18
- package/dist/core/agent/agent-registry.service.js +35 -7
- package/dist/server-root/index.js +0 -1
- package/dist/single-copy-cleanup.js +0 -1
- package/package.json +13 -13
- package/dist/api/bridge-addons.router.js +0 -109
|
@@ -569,6 +569,14 @@ function createCapRouter_addons(getProvider, _createRemoteProxy) {
|
|
|
569
569
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
570
570
|
return p.uninstallPackage(input);
|
|
571
571
|
}),
|
|
572
|
+
listInstallations: trpc_middleware_js_1.adminProcedure
|
|
573
|
+
.input(types_10.addonsCapability.methods.listInstallations.input.loose())
|
|
574
|
+
.output(types_10.addonsCapability.methods.listInstallations.output)
|
|
575
|
+
.query(async ({ input, ctx }) => {
|
|
576
|
+
const p = requireCapProvider('addons', () => getProvider(ctx));
|
|
577
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
578
|
+
return p.listInstallations(input);
|
|
579
|
+
}),
|
|
572
580
|
reloadPackages: trpc_middleware_js_1.adminProcedure
|
|
573
581
|
.input(zod_1.z.object({ nodeId: zod_1.z.string().optional() }).optional())
|
|
574
582
|
.output(types_10.addonsCapability.methods.reloadPackages.output)
|
|
@@ -2434,15 +2442,6 @@ function createCapRouter_dayNight(getProvider, createRemoteProxy) {
|
|
|
2434
2442
|
}
|
|
2435
2443
|
function createCapRouter_decoder(getProvider, createRemoteProxy) {
|
|
2436
2444
|
return (0, trpc_middleware_js_1.trpcRouter)({
|
|
2437
|
-
supportsCodec: trpc_middleware_js_1.protectedProcedure
|
|
2438
|
-
.input(types_42.decoderCapability.methods.supportsCodec.input.loose())
|
|
2439
|
-
.output(types_42.decoderCapability.methods.supportsCodec.output)
|
|
2440
|
-
.query(async ({ input, ctx }) => {
|
|
2441
|
-
const { nodeId, ...methodInput } = input;
|
|
2442
|
-
const p = resolveProvider('decoder', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
2443
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
2444
|
-
return p.supportsCodec(methodInput);
|
|
2445
|
-
}),
|
|
2446
2445
|
getInfo: trpc_middleware_js_1.protectedProcedure
|
|
2447
2446
|
.input(zod_1.z.object({ nodeId: zod_1.z.string().optional() }).optional())
|
|
2448
2447
|
.output(types_42.decoderCapability.methods.getInfo.output)
|
|
@@ -2450,103 +2449,6 @@ function createCapRouter_decoder(getProvider, createRemoteProxy) {
|
|
|
2450
2449
|
const p = resolveProvider('decoder', input?.nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
2451
2450
|
return p.getInfo();
|
|
2452
2451
|
}),
|
|
2453
|
-
createSession: trpc_middleware_js_1.protectedProcedure
|
|
2454
|
-
.input(types_42.decoderCapability.methods.createSession.input.loose())
|
|
2455
|
-
.output(types_42.decoderCapability.methods.createSession.output)
|
|
2456
|
-
.query(async ({ input, ctx }) => {
|
|
2457
|
-
const { nodeId, ...methodInput } = input;
|
|
2458
|
-
const p = resolveProvider('decoder', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
2459
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
2460
|
-
return p.createSession(methodInput);
|
|
2461
|
-
}),
|
|
2462
|
-
destroySession: trpc_middleware_js_1.protectedProcedure
|
|
2463
|
-
.input(types_42.decoderCapability.methods.destroySession.input.loose())
|
|
2464
|
-
.output(types_42.decoderCapability.methods.destroySession.output)
|
|
2465
|
-
.query(async ({ input, ctx }) => {
|
|
2466
|
-
const { nodeId, ...methodInput } = input;
|
|
2467
|
-
const p = resolveProvider('decoder', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
2468
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
2469
|
-
return p.destroySession(methodInput);
|
|
2470
|
-
}),
|
|
2471
|
-
pushPacket: trpc_middleware_js_1.protectedProcedure
|
|
2472
|
-
.input(types_42.decoderCapability.methods.pushPacket.input.loose())
|
|
2473
|
-
.output(types_42.decoderCapability.methods.pushPacket.output)
|
|
2474
|
-
.query(async ({ input, ctx }) => {
|
|
2475
|
-
const { nodeId, ...methodInput } = input;
|
|
2476
|
-
const p = resolveProvider('decoder', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
2477
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
2478
|
-
return p.pushPacket(methodInput);
|
|
2479
|
-
}),
|
|
2480
|
-
openStream: trpc_middleware_js_1.protectedProcedure
|
|
2481
|
-
.input(types_42.decoderCapability.methods.openStream.input.loose())
|
|
2482
|
-
.output(types_42.decoderCapability.methods.openStream.output)
|
|
2483
|
-
.query(async ({ input, ctx }) => {
|
|
2484
|
-
const { nodeId, ...methodInput } = input;
|
|
2485
|
-
const p = resolveProvider('decoder', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
2486
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
2487
|
-
return p.openStream(methodInput);
|
|
2488
|
-
}),
|
|
2489
|
-
pullFrames: trpc_middleware_js_1.protectedProcedure
|
|
2490
|
-
.input(types_42.decoderCapability.methods.pullFrames.input.loose())
|
|
2491
|
-
.output(types_42.decoderCapability.methods.pullFrames.output)
|
|
2492
|
-
.query(async ({ input, ctx }) => {
|
|
2493
|
-
const { nodeId, ...methodInput } = input;
|
|
2494
|
-
const p = resolveProvider('decoder', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
2495
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
2496
|
-
return p.pullFrames(methodInput);
|
|
2497
|
-
}),
|
|
2498
|
-
pullHandles: trpc_middleware_js_1.protectedProcedure
|
|
2499
|
-
.input(types_42.decoderCapability.methods.pullHandles.input.loose())
|
|
2500
|
-
.output(types_42.decoderCapability.methods.pullHandles.output)
|
|
2501
|
-
.query(async ({ input, ctx }) => {
|
|
2502
|
-
const { nodeId, ...methodInput } = input;
|
|
2503
|
-
const p = resolveProvider('decoder', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
2504
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
2505
|
-
return p.pullHandles(methodInput);
|
|
2506
|
-
}),
|
|
2507
|
-
getFrame: trpc_middleware_js_1.protectedProcedure
|
|
2508
|
-
.input(types_42.decoderCapability.methods.getFrame.input.loose())
|
|
2509
|
-
.output(types_42.decoderCapability.methods.getFrame.output)
|
|
2510
|
-
.query(async ({ input, ctx }) => {
|
|
2511
|
-
const { nodeId, ...methodInput } = input;
|
|
2512
|
-
const p = resolveProvider('decoder', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
2513
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
2514
|
-
return p.getFrame(methodInput);
|
|
2515
|
-
}),
|
|
2516
|
-
getShmStats: trpc_middleware_js_1.protectedProcedure
|
|
2517
|
-
.input(types_42.decoderCapability.methods.getShmStats.input.loose())
|
|
2518
|
-
.output(types_42.decoderCapability.methods.getShmStats.output)
|
|
2519
|
-
.query(async ({ input, ctx }) => {
|
|
2520
|
-
const { nodeId, ...methodInput } = input;
|
|
2521
|
-
const p = resolveProvider('decoder', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
2522
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
2523
|
-
return p.getShmStats(methodInput);
|
|
2524
|
-
}),
|
|
2525
|
-
updateConfig: trpc_middleware_js_1.protectedProcedure
|
|
2526
|
-
.input(types_42.decoderCapability.methods.updateConfig.input.loose())
|
|
2527
|
-
.output(types_42.decoderCapability.methods.updateConfig.output)
|
|
2528
|
-
.query(async ({ input, ctx }) => {
|
|
2529
|
-
const { nodeId, ...methodInput } = input;
|
|
2530
|
-
const p = resolveProvider('decoder', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
2531
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
2532
|
-
return p.updateConfig(methodInput);
|
|
2533
|
-
}),
|
|
2534
|
-
getStats: trpc_middleware_js_1.protectedProcedure
|
|
2535
|
-
.input(types_42.decoderCapability.methods.getStats.input.loose())
|
|
2536
|
-
.output(types_42.decoderCapability.methods.getStats.output)
|
|
2537
|
-
.query(async ({ input, ctx }) => {
|
|
2538
|
-
const { nodeId, ...methodInput } = input;
|
|
2539
|
-
const p = resolveProvider('decoder', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
2540
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
2541
|
-
return p.getStats(methodInput);
|
|
2542
|
-
}),
|
|
2543
|
-
listActiveSessions: trpc_middleware_js_1.protectedProcedure
|
|
2544
|
-
.input(zod_1.z.object({ nodeId: zod_1.z.string().optional() }).optional())
|
|
2545
|
-
.output(types_42.decoderCapability.methods.listActiveSessions.output)
|
|
2546
|
-
.query(async ({ input, ctx }) => {
|
|
2547
|
-
const p = resolveProvider('decoder', input?.nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
2548
|
-
return p.listActiveSessions();
|
|
2549
|
-
}),
|
|
2550
2452
|
reprobeHwaccel: trpc_middleware_js_1.adminProcedure
|
|
2551
2453
|
.input(zod_1.z.object({ nodeId: zod_1.z.string().optional() }).optional())
|
|
2552
2454
|
.output(types_42.decoderCapability.methods.reprobeHwaccel.output)
|
|
@@ -7383,40 +7285,6 @@ function createCapRouter_pipelineExecutor(getProvider, createRemoteProxy) {
|
|
|
7383
7285
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
7384
7286
|
return p.validatePipeline(methodInput);
|
|
7385
7287
|
}),
|
|
7386
|
-
listTemplates: trpc_middleware_js_1.protectedProcedure
|
|
7387
|
-
.input(zod_1.z.object({ nodeId: zod_1.z.string().optional() }).optional())
|
|
7388
|
-
.output(types_93.pipelineExecutorCapability.methods.listTemplates.output)
|
|
7389
|
-
.query(async ({ input, ctx }) => {
|
|
7390
|
-
const p = resolveProvider('pipeline-executor', input?.nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
7391
|
-
return p.listTemplates();
|
|
7392
|
-
}),
|
|
7393
|
-
saveTemplate: trpc_middleware_js_1.protectedProcedure
|
|
7394
|
-
.input(types_93.pipelineExecutorCapability.methods.saveTemplate.input.loose())
|
|
7395
|
-
.output(types_93.pipelineExecutorCapability.methods.saveTemplate.output)
|
|
7396
|
-
.mutation(async ({ input, ctx }) => {
|
|
7397
|
-
const { nodeId, ...methodInput } = input;
|
|
7398
|
-
const p = resolveProvider('pipeline-executor', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
7399
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
7400
|
-
return p.saveTemplate(methodInput);
|
|
7401
|
-
}),
|
|
7402
|
-
updateTemplate: trpc_middleware_js_1.protectedProcedure
|
|
7403
|
-
.input(types_93.pipelineExecutorCapability.methods.updateTemplate.input.loose())
|
|
7404
|
-
.output(types_93.pipelineExecutorCapability.methods.updateTemplate.output)
|
|
7405
|
-
.mutation(async ({ input, ctx }) => {
|
|
7406
|
-
const { nodeId, ...methodInput } = input;
|
|
7407
|
-
const p = resolveProvider('pipeline-executor', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
7408
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
7409
|
-
return p.updateTemplate(methodInput);
|
|
7410
|
-
}),
|
|
7411
|
-
deleteTemplate: trpc_middleware_js_1.protectedProcedure
|
|
7412
|
-
.input(types_93.pipelineExecutorCapability.methods.deleteTemplate.input.loose())
|
|
7413
|
-
.output(types_93.pipelineExecutorCapability.methods.deleteTemplate.output)
|
|
7414
|
-
.mutation(async ({ input, ctx }) => {
|
|
7415
|
-
const { nodeId, ...methodInput } = input;
|
|
7416
|
-
const p = resolveProvider('pipeline-executor', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
7417
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
7418
|
-
return p.deleteTemplate(methodInput);
|
|
7419
|
-
}),
|
|
7420
7288
|
getCapabilities: trpc_middleware_js_1.protectedProcedure
|
|
7421
7289
|
.input(zod_1.z.object({ nodeId: zod_1.z.string().optional() }).optional())
|
|
7422
7290
|
.output(types_93.pipelineExecutorCapability.methods.getCapabilities.output)
|
|
@@ -8037,38 +7905,6 @@ function createCapRouter_pipelineOrchestrator(getProvider, _createRemoteProxy) {
|
|
|
8037
7905
|
const p = requireCapProvider('pipeline-orchestrator', () => getProvider(ctx));
|
|
8038
7906
|
return p.getReconcileFromDiskStatus();
|
|
8039
7907
|
}),
|
|
8040
|
-
listTemplates: trpc_middleware_js_1.protectedProcedure
|
|
8041
|
-
.input(zod_1.z.object({ nodeId: zod_1.z.string().optional() }).optional())
|
|
8042
|
-
.output(types_94.pipelineOrchestratorCapability.methods.listTemplates.output)
|
|
8043
|
-
.query(async ({ input, ctx }) => {
|
|
8044
|
-
void input;
|
|
8045
|
-
const p = requireCapProvider('pipeline-orchestrator', () => getProvider(ctx));
|
|
8046
|
-
return p.listTemplates();
|
|
8047
|
-
}),
|
|
8048
|
-
saveTemplate: trpc_middleware_js_1.adminProcedure
|
|
8049
|
-
.input(types_94.pipelineOrchestratorCapability.methods.saveTemplate.input.loose())
|
|
8050
|
-
.output(types_94.pipelineOrchestratorCapability.methods.saveTemplate.output)
|
|
8051
|
-
.mutation(async ({ input, ctx }) => {
|
|
8052
|
-
const p = requireCapProvider('pipeline-orchestrator', () => getProvider(ctx));
|
|
8053
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
8054
|
-
return p.saveTemplate(input);
|
|
8055
|
-
}),
|
|
8056
|
-
updateTemplate: trpc_middleware_js_1.adminProcedure
|
|
8057
|
-
.input(types_94.pipelineOrchestratorCapability.methods.updateTemplate.input.loose())
|
|
8058
|
-
.output(types_94.pipelineOrchestratorCapability.methods.updateTemplate.output)
|
|
8059
|
-
.mutation(async ({ input, ctx }) => {
|
|
8060
|
-
const p = requireCapProvider('pipeline-orchestrator', () => getProvider(ctx));
|
|
8061
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
8062
|
-
return p.updateTemplate(input);
|
|
8063
|
-
}),
|
|
8064
|
-
deleteTemplate: trpc_middleware_js_1.adminProcedure
|
|
8065
|
-
.input(types_94.pipelineOrchestratorCapability.methods.deleteTemplate.input.loose())
|
|
8066
|
-
.output(types_94.pipelineOrchestratorCapability.methods.deleteTemplate.output)
|
|
8067
|
-
.mutation(async ({ input, ctx }) => {
|
|
8068
|
-
const p = requireCapProvider('pipeline-orchestrator', () => getProvider(ctx));
|
|
8069
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
8070
|
-
return p.deleteTemplate(input);
|
|
8071
|
-
}),
|
|
8072
7908
|
});
|
|
8073
7909
|
}
|
|
8074
7910
|
function createCapRouter_pipelineRunner(getProvider, createRemoteProxy) {
|
|
@@ -10321,33 +10157,6 @@ function createCapRouter_streamBroker(getProvider, createRemoteProxy) {
|
|
|
10321
10157
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
10322
10158
|
return p.unsubscribeAudioChunks(methodInput);
|
|
10323
10159
|
}),
|
|
10324
|
-
subscribeFrames: trpc_middleware_js_1.protectedProcedure
|
|
10325
|
-
.input(types_118.streamBrokerCapability.methods.subscribeFrames.input.loose())
|
|
10326
|
-
.output(types_118.streamBrokerCapability.methods.subscribeFrames.output)
|
|
10327
|
-
.mutation(async ({ input, ctx }) => {
|
|
10328
|
-
const { nodeId, ...methodInput } = input;
|
|
10329
|
-
const p = resolveProvider('stream-broker', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
10330
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
10331
|
-
return p.subscribeFrames(methodInput);
|
|
10332
|
-
}),
|
|
10333
|
-
pullFrameHandles: trpc_middleware_js_1.protectedProcedure
|
|
10334
|
-
.input(types_118.streamBrokerCapability.methods.pullFrameHandles.input.loose())
|
|
10335
|
-
.output(types_118.streamBrokerCapability.methods.pullFrameHandles.output)
|
|
10336
|
-
.query(async ({ input, ctx }) => {
|
|
10337
|
-
const { nodeId, ...methodInput } = input;
|
|
10338
|
-
const p = resolveProvider('stream-broker', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
10339
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
10340
|
-
return p.pullFrameHandles(methodInput);
|
|
10341
|
-
}),
|
|
10342
|
-
unsubscribeFrames: trpc_middleware_js_1.protectedProcedure
|
|
10343
|
-
.input(types_118.streamBrokerCapability.methods.unsubscribeFrames.input.loose())
|
|
10344
|
-
.output(types_118.streamBrokerCapability.methods.unsubscribeFrames.output)
|
|
10345
|
-
.mutation(async ({ input, ctx }) => {
|
|
10346
|
-
const { nodeId, ...methodInput } = input;
|
|
10347
|
-
const p = resolveProvider('stream-broker', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
10348
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
10349
|
-
return p.unsubscribeFrames(methodInput);
|
|
10350
|
-
}),
|
|
10351
10160
|
setPreBufferDuration: trpc_middleware_js_1.adminProcedure
|
|
10352
10161
|
.input(types_118.streamBrokerCapability.methods.setPreBufferDuration.input.loose())
|
|
10353
10162
|
.output(types_118.streamBrokerCapability.methods.setPreBufferDuration.output)
|
|
@@ -170,7 +170,7 @@ function buildServerProviders(services) {
|
|
|
170
170
|
nodes: wrap(() => (0, cap_providers_js_1.buildNodesProvider)(services.agentRegistry, services.moleculer, services.addonRegistry, (0, cap_providers_js_1.createNodeRootPackageLookup)(services.moleculer, services.serverUpdateService), services.loggingService.createLogger('nodes'))),
|
|
171
171
|
// server-management — runtime-updatable root package (phase 1: hub only).
|
|
172
172
|
'server-management': wrap(() => (0, server_management_provider_js_1.buildServerManagementProvider)(services.serverUpdateService)),
|
|
173
|
-
addons: wrap((ctx) => (0, cap_providers_js_1.buildAddonsProvider)(services.addonRegistry, services.addonPackageService, services.loggingService, services.moleculer, services.configService, ctx)),
|
|
173
|
+
addons: wrap((ctx) => (0, cap_providers_js_1.buildAddonsProvider)(services.addonRegistry, services.addonPackageService, services.loggingService, services.moleculer, services.configService, ctx, services.agentRegistry)),
|
|
174
174
|
// webrtc-session — the resolved broker singleton, wrapped per request to
|
|
175
175
|
// inject the server-read User-Agent into signaling attribution (the broker
|
|
176
176
|
// is a forked addon that can't see the HTTP request). Cross-node (agent-
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PackageProtectedError = void 0;
|
|
4
|
+
exports.isHubNodeId = isHubNodeId;
|
|
5
|
+
exports.uninstallPackage = uninstallPackage;
|
|
6
|
+
exports.listInstallations = listInstallations;
|
|
7
|
+
/** The hub refuses before touching any node. Mapped to FORBIDDEN by the router. */
|
|
8
|
+
class PackageProtectedError extends Error {
|
|
9
|
+
constructor(packageName) {
|
|
10
|
+
super(`Package ${packageName} is required and cannot be uninstalled`);
|
|
11
|
+
this.name = 'PackageProtectedError';
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
exports.PackageProtectedError = PackageProtectedError;
|
|
15
|
+
/** A node id that refers to the hub itself (top-level or a hub group runner). */
|
|
16
|
+
function isHubNodeId(nodeId) {
|
|
17
|
+
return nodeId === 'hub' || nodeId.startsWith('hub/');
|
|
18
|
+
}
|
|
19
|
+
const OK_OUTCOMES = new Set([
|
|
20
|
+
'uninstalled',
|
|
21
|
+
'not-installed',
|
|
22
|
+
'pending-reconnect',
|
|
23
|
+
]);
|
|
24
|
+
function errMessage(err) {
|
|
25
|
+
return err instanceof Error ? err.message : String(err);
|
|
26
|
+
}
|
|
27
|
+
function nodeResult(nodeId, outcome, extra = {}) {
|
|
28
|
+
return {
|
|
29
|
+
nodeId,
|
|
30
|
+
outcome,
|
|
31
|
+
addonIds: extra.addonIds ?? [],
|
|
32
|
+
runnersStopped: extra.runnersStopped ?? [],
|
|
33
|
+
warnings: extra.warnings ?? [],
|
|
34
|
+
...(extra.error !== undefined ? { error: extra.error } : {}),
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function stringArray(value) {
|
|
38
|
+
return Array.isArray(value) ? value.filter((v) => typeof v === 'string') : [];
|
|
39
|
+
}
|
|
40
|
+
function isRecord(value) {
|
|
41
|
+
return typeof value === 'object' && value !== null;
|
|
42
|
+
}
|
|
43
|
+
function parseUndeployReply(reply) {
|
|
44
|
+
if (!isRecord(reply)) {
|
|
45
|
+
return {
|
|
46
|
+
packageScoped: false,
|
|
47
|
+
outcome: null,
|
|
48
|
+
reason: undefined,
|
|
49
|
+
addonIds: [],
|
|
50
|
+
runnersStopped: null,
|
|
51
|
+
warnings: [],
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const outcome = reply['outcome'];
|
|
55
|
+
return {
|
|
56
|
+
packageScoped: reply['scope'] === 'package',
|
|
57
|
+
outcome: outcome === 'uninstalled' || outcome === 'not-installed' || outcome === 'refused'
|
|
58
|
+
? outcome
|
|
59
|
+
: null,
|
|
60
|
+
reason: typeof reply['reason'] === 'string' ? reply['reason'] : undefined,
|
|
61
|
+
addonIds: stringArray(reply['addonIds']),
|
|
62
|
+
runnersStopped: Array.isArray(reply['runnersStopped'])
|
|
63
|
+
? stringArray(reply['runnersStopped'])
|
|
64
|
+
: null,
|
|
65
|
+
warnings: stringArray(reply['warnings']),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* An agent built before package mode answers `{ success, addonId }` and removes
|
|
70
|
+
* ONE addon — its folder, not its runner. Its packages are still removed (one
|
|
71
|
+
* call per addon id), and the result says the runner outlives it.
|
|
72
|
+
*/
|
|
73
|
+
const LEGACY_AGENT_WARNING = 'this agent predates runner teardown: the addon is removed from disk but its runner keeps running until the agent restarts — update the agent';
|
|
74
|
+
async function uninstallFromOnlineAgent(seam, nodeId, packageName) {
|
|
75
|
+
let listed;
|
|
76
|
+
try {
|
|
77
|
+
listed = (await seam.agentAddons(nodeId))
|
|
78
|
+
.filter((a) => a.packageName === packageName)
|
|
79
|
+
.map((a) => a.id);
|
|
80
|
+
}
|
|
81
|
+
catch (err) {
|
|
82
|
+
return nodeResult(nodeId, 'failed', { error: `status read failed: ${errMessage(err)}` });
|
|
83
|
+
}
|
|
84
|
+
try {
|
|
85
|
+
// `addonId` is for an agent that predates package mode: it reads only that
|
|
86
|
+
// field. With no listed addon, the package name doubles as the dir to drop.
|
|
87
|
+
const first = parseUndeployReply(await seam.agentUndeploy(nodeId, { packageName, addonId: listed[0] ?? packageName }));
|
|
88
|
+
if (first.packageScoped && first.outcome !== null) {
|
|
89
|
+
if (first.outcome === 'refused') {
|
|
90
|
+
return nodeResult(nodeId, 'refused', { error: first.reason, addonIds: first.addonIds });
|
|
91
|
+
}
|
|
92
|
+
await seam.notePackageUndeployed(nodeId, packageName, first.addonIds);
|
|
93
|
+
return nodeResult(nodeId, first.outcome, {
|
|
94
|
+
addonIds: first.addonIds,
|
|
95
|
+
runnersStopped: first.runnersStopped ?? [],
|
|
96
|
+
warnings: first.warnings,
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
for (const addonId of listed.slice(1)) {
|
|
100
|
+
await seam.agentUndeploy(nodeId, { packageName, addonId });
|
|
101
|
+
}
|
|
102
|
+
await seam.notePackageUndeployed(nodeId, packageName, listed);
|
|
103
|
+
return nodeResult(nodeId, listed.length > 0 ? 'uninstalled' : 'not-installed', {
|
|
104
|
+
addonIds: listed,
|
|
105
|
+
warnings: listed.length > 0 ? [LEGACY_AGENT_WARNING] : [],
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
return nodeResult(nodeId, 'failed', { addonIds: listed, error: errMessage(err) });
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
async function uninstallFromAgent(seam, node, packageName) {
|
|
113
|
+
if (node.isOnline)
|
|
114
|
+
return uninstallFromOnlineAgent(seam, node.id, packageName);
|
|
115
|
+
// Offline: nothing can be called. Pruning the record is what keeps the
|
|
116
|
+
// back-fill from returning it; the copy itself is the stale sweep's on rejoin.
|
|
117
|
+
try {
|
|
118
|
+
await seam.notePackageUndeployed(node.id, packageName, []);
|
|
119
|
+
return nodeResult(node.id, 'pending-reconnect', {
|
|
120
|
+
warnings: [
|
|
121
|
+
'node is offline — its copy is removed when it reconnects, provided the hub no longer installs the package',
|
|
122
|
+
],
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
catch (err) {
|
|
126
|
+
return nodeResult(node.id, 'failed', { error: errMessage(err) });
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
async function uninstallFromHub(seam, packageName) {
|
|
130
|
+
const row = seam.listHubPackages().find((p) => p.name === packageName);
|
|
131
|
+
if (!row)
|
|
132
|
+
return nodeResult('hub', 'not-installed');
|
|
133
|
+
try {
|
|
134
|
+
await seam.uninstallOnHub(packageName);
|
|
135
|
+
return nodeResult('hub', 'uninstalled', { addonIds: row.addonIds });
|
|
136
|
+
}
|
|
137
|
+
catch (err) {
|
|
138
|
+
return nodeResult('hub', 'failed', { addonIds: row.addonIds, error: errMessage(err) });
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
function summarize(packageName, results) {
|
|
142
|
+
return { success: results.every((r) => OK_OUTCOMES.has(r.outcome)), packageName, results };
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* Agents that have (or are recorded to have) the package. An online agent
|
|
146
|
+
* whose status read fails is still targeted — the undeploy leg reports the
|
|
147
|
+
* failure for that node rather than silently skipping it.
|
|
148
|
+
*/
|
|
149
|
+
async function agentsHolding(seam, packageName) {
|
|
150
|
+
const agents = await seam.listAgents();
|
|
151
|
+
const holding = await Promise.all(agents.map(async (node) => {
|
|
152
|
+
if (node.isOnline) {
|
|
153
|
+
try {
|
|
154
|
+
const addons = await seam.agentAddons(node.id);
|
|
155
|
+
if (addons.some((a) => a.packageName === packageName))
|
|
156
|
+
return node;
|
|
157
|
+
}
|
|
158
|
+
catch {
|
|
159
|
+
return node;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
const recorded = await seam.recordedPackages(node.id).catch(() => []);
|
|
163
|
+
return recorded.some((p) => p.name === packageName) ? node : null;
|
|
164
|
+
}));
|
|
165
|
+
return holding.filter((n) => n !== null);
|
|
166
|
+
}
|
|
167
|
+
async function uninstallPackage(seam, request) {
|
|
168
|
+
const { packageName } = request;
|
|
169
|
+
if (seam.isProtected(packageName))
|
|
170
|
+
throw new PackageProtectedError(packageName);
|
|
171
|
+
if (request.everywhere === true) {
|
|
172
|
+
const agents = await agentsHolding(seam, packageName);
|
|
173
|
+
// Agents first, hub last — the hub routes every call to the agents, and a
|
|
174
|
+
// hub uninstall that fails must not leave the agents' records already
|
|
175
|
+
// pruned for nothing: each leg records only its own success.
|
|
176
|
+
const results = [];
|
|
177
|
+
for (const node of agents)
|
|
178
|
+
results.push(await uninstallFromAgent(seam, node, packageName));
|
|
179
|
+
results.push(await uninstallFromHub(seam, packageName));
|
|
180
|
+
return summarize(packageName, results);
|
|
181
|
+
}
|
|
182
|
+
const nodeId = request.nodeId;
|
|
183
|
+
if (nodeId === undefined || isHubNodeId(nodeId)) {
|
|
184
|
+
// The original contract: hub-only, and a failure THROWS.
|
|
185
|
+
await seam.uninstallOnHub(packageName);
|
|
186
|
+
const row = seam.listHubPackages().find((p) => p.name === packageName);
|
|
187
|
+
return summarize(packageName, [
|
|
188
|
+
nodeResult('hub', 'uninstalled', { addonIds: row?.addonIds ?? [] }),
|
|
189
|
+
]);
|
|
190
|
+
}
|
|
191
|
+
const node = (await seam.listAgents()).find((n) => n.id === nodeId);
|
|
192
|
+
if (!node) {
|
|
193
|
+
return summarize(packageName, [
|
|
194
|
+
nodeResult(nodeId, 'failed', { error: `unknown node ${nodeId}` }),
|
|
195
|
+
]);
|
|
196
|
+
}
|
|
197
|
+
return summarize(packageName, [await uninstallFromAgent(seam, node, packageName)]);
|
|
198
|
+
}
|
|
199
|
+
async function listInstallations(seam, packageName) {
|
|
200
|
+
const wanted = (name) => packageName === undefined || name === packageName;
|
|
201
|
+
const hubPackages = seam.listHubPackages();
|
|
202
|
+
const onHub = new Set(hubPackages.map((p) => p.name));
|
|
203
|
+
const installations = hubPackages
|
|
204
|
+
.filter((p) => wanted(p.name))
|
|
205
|
+
.map((p) => ({
|
|
206
|
+
packageName: p.name,
|
|
207
|
+
nodeId: 'hub',
|
|
208
|
+
nodeName: 'Hub',
|
|
209
|
+
isHub: true,
|
|
210
|
+
isOnline: true,
|
|
211
|
+
version: p.version,
|
|
212
|
+
addonIds: p.addonIds,
|
|
213
|
+
source: 'live',
|
|
214
|
+
onHub: true,
|
|
215
|
+
}));
|
|
216
|
+
const unreachable = [];
|
|
217
|
+
const perAgent = await Promise.all((await seam.listAgents()).map(async (node) => {
|
|
218
|
+
const base = { nodeId: node.id, nodeName: node.name, isHub: false, isOnline: node.isOnline };
|
|
219
|
+
if (node.isOnline) {
|
|
220
|
+
try {
|
|
221
|
+
const byPackage = new Map();
|
|
222
|
+
for (const addon of await seam.agentAddons(node.id)) {
|
|
223
|
+
if (addon.packageName === undefined || !wanted(addon.packageName))
|
|
224
|
+
continue;
|
|
225
|
+
const row = byPackage.get(addon.packageName) ?? {
|
|
226
|
+
version: addon.version ?? null,
|
|
227
|
+
addonIds: [],
|
|
228
|
+
};
|
|
229
|
+
row.addonIds.push(addon.id);
|
|
230
|
+
byPackage.set(addon.packageName, row);
|
|
231
|
+
}
|
|
232
|
+
return [...byPackage].map(([name, row]) => ({
|
|
233
|
+
...base,
|
|
234
|
+
packageName: name,
|
|
235
|
+
version: row.version,
|
|
236
|
+
addonIds: row.addonIds,
|
|
237
|
+
source: 'live',
|
|
238
|
+
onHub: onHub.has(name),
|
|
239
|
+
}));
|
|
240
|
+
}
|
|
241
|
+
catch (err) {
|
|
242
|
+
unreachable.push({ nodeId: node.id, error: errMessage(err) });
|
|
243
|
+
return [];
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
const recorded = await seam.recordedPackages(node.id).catch(() => []);
|
|
247
|
+
return recorded
|
|
248
|
+
.filter((p) => wanted(p.name))
|
|
249
|
+
.map((p) => ({
|
|
250
|
+
...base,
|
|
251
|
+
packageName: p.name,
|
|
252
|
+
version: p.version,
|
|
253
|
+
addonIds: [],
|
|
254
|
+
source: 'recorded',
|
|
255
|
+
onHub: onHub.has(p.name),
|
|
256
|
+
}));
|
|
257
|
+
}));
|
|
258
|
+
return { installations: [...installations, ...perAgent.flat()], unreachable };
|
|
259
|
+
}
|
|
@@ -50,11 +50,11 @@ const types_1 = require("@camstack/types");
|
|
|
50
50
|
*
|
|
51
51
|
* What's left is ONLY the addon package management surface: the installer
|
|
52
52
|
* + the loader + `reloadPackages` + `listAvailableAddons`. These are still
|
|
53
|
-
* used by
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
53
|
+
* used by `server/backend/src/api/addon-upload.ts` (multipart upload →
|
|
54
|
+
* install → loader reload). The `bridge-addons.router.ts` that also used it
|
|
55
|
+
* was never mounted and was deleted on 2026-10-08 — its hard-coded REQUIRED
|
|
56
|
+
* set read like the uninstall guard and was not; uninstall is the `addons`
|
|
57
|
+
* cap's (`core/addon/addon-uninstall.ts`).
|
|
58
58
|
*/
|
|
59
59
|
class AddonBridgeService {
|
|
60
60
|
loggingService;
|
|
@@ -24,13 +24,20 @@
|
|
|
24
24
|
* both mutations are attributable actions rather than heuristics.
|
|
25
25
|
*
|
|
26
26
|
* THE INTENDED VERSION
|
|
27
|
-
* The hub's own installed version
|
|
28
|
-
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
*
|
|
33
|
-
*
|
|
27
|
+
* The hub's own installed version, and only that. Never `latest` — resolving
|
|
28
|
+
* `latest` per node is exactly the silent per-node drift the bootstrap
|
|
29
|
+
* installer already suffers from (`addon-installer.ts` installs unpinned and
|
|
30
|
+
* `ensureRequiredPackages` never re-installs, so whatever `latest` meant at a
|
|
31
|
+
* node's first boot is what that node runs forever).
|
|
32
|
+
*
|
|
33
|
+
* A package the hub does NOT install is never delivered, whatever the node last
|
|
34
|
+
* reported. It used to fall back to the node's last-reported version, which
|
|
35
|
+
* made the back-fill the one path that could put an UNINSTALLED addon back: an
|
|
36
|
+
* operator removes `@camstack/addon-decoder-ffmpeg` from the hub and moves the
|
|
37
|
+
* agent's copy aside, and the next reconnect reinstalled it from npm at the
|
|
38
|
+
* version the node last ran — while the same pass's stale sweep exists to
|
|
39
|
+
* remove exactly such an addon. The two directions disagreed; now the hub's
|
|
40
|
+
* install set bounds both. Such packages are reported as `notOnHub`.
|
|
34
41
|
*
|
|
35
42
|
* BOUNDING
|
|
36
43
|
* Delivery failures are counted per `(node, package, version)` and the pair is
|
|
@@ -66,7 +73,7 @@ function computeBackfillPlan(input) {
|
|
|
66
73
|
const liveNames = new Set(input.live.map((p) => p.name));
|
|
67
74
|
const targets = [];
|
|
68
75
|
const retired = [];
|
|
69
|
-
const
|
|
76
|
+
const notOnHub = [];
|
|
70
77
|
for (const entry of input.recorded) {
|
|
71
78
|
if (liveNames.has(entry.name))
|
|
72
79
|
continue;
|
|
@@ -79,18 +86,13 @@ function computeBackfillPlan(input) {
|
|
|
79
86
|
continue;
|
|
80
87
|
}
|
|
81
88
|
const hubVersion = input.hubVersions.get(entry.name);
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
unresolvable.push(entry.name);
|
|
89
|
+
if (hubVersion === undefined) {
|
|
90
|
+
notOnHub.push(entry.name);
|
|
85
91
|
continue;
|
|
86
92
|
}
|
|
87
|
-
targets.push({
|
|
88
|
-
name: entry.name,
|
|
89
|
-
version,
|
|
90
|
-
versionSource: hubVersion === undefined ? 'node-last-reported' : 'hub-installed',
|
|
91
|
-
});
|
|
93
|
+
targets.push({ name: entry.name, version: hubVersion, versionSource: 'hub-installed' });
|
|
92
94
|
}
|
|
93
|
-
return { targets, retired,
|
|
95
|
+
return { targets, retired, notOnHub, liveCount: liveNames.size };
|
|
94
96
|
}
|
|
95
97
|
function confirmBackfillPlan(plan, secondRead) {
|
|
96
98
|
const present = new Set(secondRead.map((p) => p.name));
|
|
@@ -187,5 +189,5 @@ async function executeBackfill(seam, nodeId, plan, tracker) {
|
|
|
187
189
|
if (delivered.length > 0) {
|
|
188
190
|
await seam.reload(nodeId);
|
|
189
191
|
}
|
|
190
|
-
return { delivered, failed, skipped: plan.retired,
|
|
192
|
+
return { delivered, failed, skipped: plan.retired, notOnHub: plan.notOnHub };
|
|
191
193
|
}
|