@camstack/server 0.2.2 → 1.0.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/{src/agent-status-page.ts → dist/agent-status-page.js} +30 -45
- package/dist/api/addon-upload.js +441 -0
- package/dist/api/addons-custom.router.js +91 -0
- package/dist/api/auth-whoami.js +55 -0
- package/dist/api/bridge-addons.router.js +109 -0
- package/dist/api/capabilities.router.js +229 -0
- package/dist/api/core/addon-settings.router.js +117 -0
- package/dist/api/core/agents.router.js +73 -0
- package/dist/api/core/auth.router.js +286 -0
- package/dist/api/core/bulk-update-coordinator.js +229 -0
- package/dist/api/core/cap-providers.js +1124 -0
- package/dist/api/core/capabilities.router.js +138 -0
- package/dist/api/core/collection-preference.js +17 -0
- package/dist/api/core/event-bus-proxy.router.js +45 -0
- package/dist/api/core/hwaccel.router.js +91 -0
- package/dist/api/core/live-events.router.js +61 -0
- package/dist/api/core/logs.router.js +172 -0
- package/dist/api/core/notifications.router.js +67 -0
- package/dist/api/core/repl.router.js +35 -0
- package/dist/api/core/settings-backend.router.js +121 -0
- package/dist/api/core/stream-probe.router.js +58 -0
- package/dist/api/core/system-events.router.js +100 -0
- package/dist/api/health/health.routes.js +68 -0
- package/{src/api/oauth2/consent-page.ts → dist/api/oauth2/consent-page.js} +11 -20
- package/dist/api/oauth2/oauth2-routes.js +219 -0
- package/dist/api/trpc/cap-mount-helpers.js +194 -0
- package/dist/api/trpc/cap-route-error-formatter.js +133 -0
- package/dist/api/trpc/client-ip.js +147 -0
- package/dist/api/trpc/core-cap-bridge.js +115 -0
- package/dist/api/trpc/generated-cap-mounts.js +388 -0
- package/dist/api/trpc/generated-cap-routers.js +7635 -0
- package/dist/api/trpc/scope-access.js +93 -0
- package/dist/api/trpc/trpc.context.js +184 -0
- package/dist/api/trpc/trpc.middleware.js +139 -0
- package/dist/api/trpc/trpc.router.js +188 -0
- package/dist/auth/session-cookie.js +47 -0
- package/dist/boot/boot-config.js +241 -0
- package/dist/boot/integration-id-backfill.js +76 -0
- package/dist/boot/post-boot.service.js +85 -0
- package/dist/core/addon/addon-call-gateway.js +99 -0
- package/dist/core/addon/addon-package.service.js +1560 -0
- package/dist/core/addon/addon-registry.service.js +2739 -0
- package/{src/core/addon/addon-row-manifest.ts → dist/core/addon/addon-row-manifest.js} +5 -5
- package/dist/core/addon/addon-search.service.js +62 -0
- package/dist/core/addon/addon-settings-provider.js +102 -0
- package/dist/core/addon/addon.tokens.js +5 -0
- package/dist/core/addon-bridge/addon-bridge.service.js +145 -0
- package/dist/core/addon-pages/addon-pages.service.js +107 -0
- package/dist/core/addon-widgets/addon-widgets.service.js +120 -0
- package/dist/core/agent/agent-registry.service.js +477 -0
- package/dist/core/auth/auth.service.js +10 -0
- package/dist/core/capability/capability.service.js +58 -0
- package/dist/core/config/config.schema.js +7 -0
- package/dist/core/config/config.service.js +10 -0
- package/dist/core/events/event-bus.service.js +83 -0
- package/dist/core/feature/feature.service.js +10 -0
- package/dist/core/lifecycle/lifecycle-state-machine.js +6 -0
- package/dist/core/logging/log-ring-buffer.js +6 -0
- package/dist/core/logging/logging.service.js +130 -0
- package/dist/core/logging/scoped-logger.js +6 -0
- package/dist/core/moleculer/cap-call-fn.js +50 -0
- package/dist/core/moleculer/cap-route-authority.js +122 -0
- package/dist/core/moleculer/moleculer.service.js +898 -0
- package/dist/core/network/network-quality.service.js +7 -0
- package/dist/core/notification/notification-wrapper.service.js +33 -0
- package/dist/core/notification/toast-wrapper.service.js +25 -0
- package/dist/core/provider/provider.tokens.js +4 -0
- package/dist/core/repl/repl-engine.service.js +140 -0
- package/dist/core/storage/fs-storage-backend.js +6 -0
- package/dist/core/storage/storage-location-manager.js +6 -0
- package/dist/core/storage/storage.service.js +7 -0
- package/dist/core/streaming/stream-probe.service.js +209 -0
- package/dist/core/topology/topology-emitter.service.js +106 -0
- package/dist/launcher.js +325 -0
- package/dist/main.js +1098 -0
- package/dist/manual-boot.js +227 -0
- package/package.json +5 -1
- package/src/__tests__/addon-install-e2e.test.ts +0 -74
- package/src/__tests__/addon-pages-e2e.test.ts +0 -200
- package/src/__tests__/addon-route-session.test.ts +0 -17
- package/src/__tests__/addon-settings-router.spec.ts +0 -67
- package/src/__tests__/addon-upload.spec.ts +0 -475
- package/src/__tests__/agent-registry.spec.ts +0 -179
- package/src/__tests__/agent-status-page.spec.ts +0 -82
- package/src/__tests__/auth-session-cookie.test.ts +0 -48
- package/src/__tests__/bulk-update-coordinator.spec.ts +0 -303
- package/src/__tests__/cap-ownership-authority.spec.ts +0 -431
- package/src/__tests__/cap-providers/cap-providers-location-import.spec.ts +0 -206
- package/src/__tests__/cap-providers/cap-usage-graph.spec.ts +0 -37
- package/src/__tests__/cap-providers/compute-topology-categories.spec.ts +0 -110
- package/src/__tests__/cap-providers/integrations-delete-cascade.spec.ts +0 -292
- package/src/__tests__/cap-providers-bulk-update.spec.ts +0 -408
- package/src/__tests__/cap-route-adapter.spec.ts +0 -302
- package/src/__tests__/cap-routers/_meta.spec.ts +0 -199
- package/src/__tests__/cap-routers/addon-settings.router.spec.ts +0 -115
- package/src/__tests__/cap-routers/broker-routing.router.spec.ts +0 -177
- package/src/__tests__/cap-routers/cap-route-error-formatter.spec.ts +0 -125
- package/src/__tests__/cap-routers/capabilities-node.spec.ts +0 -68
- package/src/__tests__/cap-routers/device-link-overlay.spec.ts +0 -137
- package/src/__tests__/cap-routers/device-manager-aggregate.router.spec.ts +0 -194
- package/src/__tests__/cap-routers/harness.ts +0 -163
- package/src/__tests__/cap-routers/metrics-provider.router.spec.ts +0 -133
- package/src/__tests__/cap-routers/null-provider-guard.spec.ts +0 -64
- package/src/__tests__/cap-routers/pipeline-executor.router.spec.ts +0 -159
- package/src/__tests__/cap-routers/settings-store.router.spec.ts +0 -291
- package/src/__tests__/capability-e2e.test.ts +0 -384
- package/src/__tests__/cli-e2e.test.ts +0 -150
- package/src/__tests__/core-cap-bridge.spec.ts +0 -91
- package/src/__tests__/dev-bootstrap-shm-ring.spec.ts +0 -40
- package/src/__tests__/device-settings-contribution-dispatch.spec.ts +0 -280
- package/src/__tests__/embedded-deps-e2e.test.ts +0 -125
- package/src/__tests__/event-bus-proxy-router.spec.ts +0 -75
- package/src/__tests__/fixtures/mock-analysis-addon-a.ts +0 -37
- package/src/__tests__/fixtures/mock-analysis-addon-b.ts +0 -37
- package/src/__tests__/fixtures/mock-log-addon.ts +0 -37
- package/src/__tests__/fixtures/mock-storage-addon.ts +0 -40
- package/src/__tests__/framework-allowlist.spec.ts +0 -96
- package/src/__tests__/framework-installer-defer-restart.spec.ts +0 -165
- package/src/__tests__/https-e2e.test.ts +0 -124
- package/src/__tests__/lifecycle-e2e.test.ts +0 -189
- package/src/__tests__/live-events-subscription.spec.ts +0 -149
- package/src/__tests__/moleculer/uds-readiness.spec.ts +0 -150
- package/src/__tests__/moleculer/uds-topology.spec.ts +0 -418
- package/src/__tests__/moleculer/uds-unowned-call.spec.ts +0 -383
- package/src/__tests__/moleculer-register-node-idempotency.spec.ts +0 -273
- package/src/__tests__/native-cap-route.spec.ts +0 -427
- package/src/__tests__/oauth2-account-linking.spec.ts +0 -867
- package/src/__tests__/post-boot-restart.spec.ts +0 -161
- package/src/__tests__/singleton-contention.test.ts +0 -499
- package/src/__tests__/streaming-diagnostic.test.ts +0 -615
- package/src/__tests__/streaming-scale.test.ts +0 -314
- package/src/__tests__/uds-addon-call-wiring.spec.ts +0 -242
- package/src/__tests__/uds-log-ingest.spec.ts +0 -183
- package/src/api/__tests__/addons-custom.spec.ts +0 -148
- package/src/api/__tests__/capabilities.router.test.ts +0 -56
- package/src/api/addon-upload.ts +0 -529
- package/src/api/addons-custom.router.ts +0 -101
- package/src/api/auth-whoami.ts +0 -101
- package/src/api/bridge-addons.router.ts +0 -122
- package/src/api/capabilities.router.ts +0 -265
- package/src/api/core/__tests__/auth-router-totp.spec.ts +0 -297
- package/src/api/core/__tests__/integration-markers.spec.ts +0 -10
- package/src/api/core/addon-settings.router.ts +0 -127
- package/src/api/core/agents.router.ts +0 -86
- package/src/api/core/auth.router.ts +0 -322
- package/src/api/core/bulk-update-coordinator.ts +0 -305
- package/src/api/core/cap-providers.ts +0 -1339
- package/src/api/core/capabilities.router.ts +0 -149
- package/src/api/core/collection-preference.ts +0 -40
- package/src/api/core/event-bus-proxy.router.ts +0 -45
- package/src/api/core/hwaccel.router.ts +0 -108
- package/src/api/core/live-events.router.ts +0 -67
- package/src/api/core/logs.router.ts +0 -195
- package/src/api/core/notifications.router.ts +0 -66
- package/src/api/core/repl.router.ts +0 -39
- package/src/api/core/settings-backend.router.ts +0 -140
- package/src/api/core/stream-probe.router.ts +0 -57
- package/src/api/core/system-events.router.ts +0 -125
- package/src/api/health/health.routes.ts +0 -117
- package/src/api/oauth2/__tests__/oauth2-routes.spec.ts +0 -62
- package/src/api/oauth2/oauth2-routes.ts +0 -281
- package/src/api/trpc/__tests__/client-ip.spec.ts +0 -146
- package/src/api/trpc/__tests__/scope-access-device.spec.ts +0 -268
- package/src/api/trpc/__tests__/scope-access.spec.ts +0 -102
- package/src/api/trpc/__tests__/webrtc-session-ua-enrich.spec.ts +0 -136
- package/src/api/trpc/cap-mount-helpers.ts +0 -245
- package/src/api/trpc/cap-route-error-formatter.ts +0 -171
- package/src/api/trpc/client-ip.ts +0 -147
- package/src/api/trpc/core-cap-bridge.ts +0 -154
- package/src/api/trpc/generated-cap-mounts.ts +0 -1240
- package/src/api/trpc/generated-cap-routers.ts +0 -11523
- package/src/api/trpc/scope-access.ts +0 -110
- package/src/api/trpc/trpc.context.ts +0 -258
- package/src/api/trpc/trpc.middleware.ts +0 -146
- package/src/api/trpc/trpc.router.ts +0 -389
- package/src/auth/session-cookie.ts +0 -54
- package/src/boot/__tests__/integration-id-backfill.spec.ts +0 -131
- package/src/boot/boot-config.ts +0 -259
- package/src/boot/integration-id-backfill.ts +0 -109
- package/src/boot/post-boot.service.ts +0 -105
- package/src/core/addon/__tests__/addon-registry-capability.test.ts +0 -62
- package/src/core/addon/__tests__/addon-row-manifest.spec.ts +0 -62
- package/src/core/addon/addon-call-gateway.ts +0 -171
- package/src/core/addon/addon-package.service.ts +0 -1787
- package/src/core/addon/addon-registry.service.ts +0 -3130
- package/src/core/addon/addon-search.service.ts +0 -91
- package/src/core/addon/addon-settings-provider.ts +0 -220
- package/src/core/addon/addon.tokens.ts +0 -2
- package/src/core/addon-bridge/addon-bridge.service.ts +0 -130
- package/src/core/addon-pages/addon-pages.service.spec.ts +0 -117
- package/src/core/addon-pages/addon-pages.service.ts +0 -82
- package/src/core/addon-widgets/addon-widgets.service.ts +0 -95
- package/src/core/agent/agent-registry.service.ts +0 -529
- package/src/core/auth/auth.service.spec.ts +0 -86
- package/src/core/auth/auth.service.ts +0 -8
- package/src/core/capability/capability.service.ts +0 -66
- package/src/core/config/config.schema.ts +0 -3
- package/src/core/config/config.service.spec.ts +0 -175
- package/src/core/config/config.service.ts +0 -7
- package/src/core/events/event-bus.service.spec.ts +0 -235
- package/src/core/events/event-bus.service.ts +0 -89
- package/src/core/feature/feature.service.spec.ts +0 -99
- package/src/core/feature/feature.service.ts +0 -8
- package/src/core/lifecycle/lifecycle-state-machine.spec.ts +0 -166
- package/src/core/lifecycle/lifecycle-state-machine.ts +0 -3
- package/src/core/logging/log-ring-buffer.ts +0 -3
- package/src/core/logging/logging.service.spec.ts +0 -287
- package/src/core/logging/logging.service.ts +0 -143
- package/src/core/logging/scoped-logger.ts +0 -3
- package/src/core/moleculer/cap-call-fn.spec.ts +0 -173
- package/src/core/moleculer/cap-call-fn.ts +0 -107
- package/src/core/moleculer/cap-route-authority.ts +0 -194
- package/src/core/moleculer/moleculer.service.ts +0 -1072
- package/src/core/network/network-quality.service.spec.ts +0 -53
- package/src/core/network/network-quality.service.ts +0 -5
- package/src/core/notification/notification-wrapper.service.ts +0 -34
- package/src/core/notification/toast-wrapper.service.ts +0 -27
- package/src/core/provider/provider.tokens.ts +0 -1
- package/src/core/repl/repl-engine.service.spec.ts +0 -444
- package/src/core/repl/repl-engine.service.ts +0 -155
- package/src/core/storage/fs-storage-backend.spec.ts +0 -70
- package/src/core/storage/fs-storage-backend.ts +0 -3
- package/src/core/storage/storage-location-manager.spec.ts +0 -130
- package/src/core/storage/storage-location-manager.ts +0 -3
- package/src/core/storage/storage.service.spec.ts +0 -73
- package/src/core/storage/storage.service.ts +0 -3
- package/src/core/streaming/stream-probe.service.ts +0 -221
- package/src/core/topology/topology-emitter.service.ts +0 -105
- package/src/launcher.ts +0 -314
- package/src/main.ts +0 -1245
- package/src/manual-boot.ts +0 -301
- package/tsconfig.build.json +0 -8
- package/tsconfig.json +0 -33
- package/vitest.config.ts +0 -26
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LoggingService = void 0;
|
|
4
|
+
const core_1 = require("@camstack/core");
|
|
5
|
+
const types_1 = require("@camstack/types");
|
|
6
|
+
/** Baseline tags every hub-local logger gets, so console output never
|
|
7
|
+
* falls back to `?` for the agent slot. Hub-local addons already
|
|
8
|
+
* re-tag with their addonId; this ensures non-addon scopes (core
|
|
9
|
+
* services like `AddonRegistry`, `StreamProbeService`, `moleculer`)
|
|
10
|
+
* render as `hub` in the agent column.
|
|
11
|
+
*
|
|
12
|
+
* `pid` is the hub renderer's own OS pid. Entries forwarded from
|
|
13
|
+
* forked workers via `$hub.log` carry their own `tags.pid` which
|
|
14
|
+
* wins over this baseline (right-biased merge in `writeFromWorker`). */
|
|
15
|
+
const HUB_BASELINE_TAGS = { agentId: 'hub', nodeId: 'hub', pid: process.pid };
|
|
16
|
+
class LoggingService extends core_1.LogManager {
|
|
17
|
+
/**
|
|
18
|
+
* Device-name cache consulted by the formatter when a log line
|
|
19
|
+
* carries `tags.deviceId` but no explicit `deviceName`. Populated
|
|
20
|
+
* by `setDeviceNames` whenever device-manager emits registered /
|
|
21
|
+
* updated / removed events. Missing entries fall back to `#<id>`.
|
|
22
|
+
*/
|
|
23
|
+
deviceNames = new Map();
|
|
24
|
+
constructor(configService) {
|
|
25
|
+
// The log buffer is now partitioned per addonId (see PartitionedLogBuffer):
|
|
26
|
+
// this value caps EACH addon's bucket, not the total. A chatty addon evicts
|
|
27
|
+
// only its own lines, so quiet addons (e.g. a HomeAssistant `image` entity)
|
|
28
|
+
// keep their sparse history. `eventBus.ringBufferSize` still sizes the
|
|
29
|
+
// separate system-event ring; logs get their own per-addon cap.
|
|
30
|
+
const perAddonCapacity = configService.get('eventBus.perAddonLogBufferSize') ?? 5000;
|
|
31
|
+
super(perAddonCapacity);
|
|
32
|
+
// Enriches every emitted LogEntry with `tags.deviceName` before
|
|
33
|
+
// destinations / subscribers see it — works across bundled copies
|
|
34
|
+
// of `@camstack/core` (addon packages) because the mutation
|
|
35
|
+
// happens upstream of their formatters.
|
|
36
|
+
this.setDeviceNameLookup((id) => this.deviceNames.get(id) ?? null);
|
|
37
|
+
}
|
|
38
|
+
/** Bulk-refresh from device-manager snapshot. Replaces every entry. */
|
|
39
|
+
setDeviceNames(entries) {
|
|
40
|
+
this.deviceNames.clear();
|
|
41
|
+
for (const { id, name } of entries) {
|
|
42
|
+
if (typeof id === 'number' &&
|
|
43
|
+
Number.isFinite(id) &&
|
|
44
|
+
typeof name === 'string' &&
|
|
45
|
+
name.length > 0) {
|
|
46
|
+
this.deviceNames.set(id, name);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** Incremental update — called from DeviceRegistered / DeviceUpdated. */
|
|
51
|
+
upsertDeviceName(id, name) {
|
|
52
|
+
if (!Number.isFinite(id))
|
|
53
|
+
return;
|
|
54
|
+
if (typeof name === 'string' && name.length > 0)
|
|
55
|
+
this.deviceNames.set(id, name);
|
|
56
|
+
else
|
|
57
|
+
this.deviceNames.delete(id);
|
|
58
|
+
}
|
|
59
|
+
/** Drop on DeviceRemoved. */
|
|
60
|
+
removeDeviceName(id) {
|
|
61
|
+
this.deviceNames.delete(id);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Subscribe to `device.*` events so the cache stays live: every
|
|
65
|
+
* DeviceRegistered / updated emit carries `{deviceId, name}` — we
|
|
66
|
+
* upsert, and DeviceUnregistered clears. Call once at boot.
|
|
67
|
+
*/
|
|
68
|
+
attachDeviceNameStream(eventBus) {
|
|
69
|
+
const selfLogger = this.createLogger('logging').withTags({ addonId: 'logging' });
|
|
70
|
+
eventBus.subscribe({ category: types_1.EventCategory.DeviceRegistered }, (event) => {
|
|
71
|
+
const data = event.data;
|
|
72
|
+
if (data && typeof data.deviceId === 'number') {
|
|
73
|
+
this.upsertDeviceName(data.deviceId, data.name);
|
|
74
|
+
selfLogger.info('device-name cache upserted', {
|
|
75
|
+
meta: {
|
|
76
|
+
deviceId: data.deviceId,
|
|
77
|
+
name: data.name ?? null,
|
|
78
|
+
cacheSize: this.deviceNames.size,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
eventBus.subscribe({ category: types_1.EventCategory.DeviceUnregistered }, (event) => {
|
|
84
|
+
const data = event.data;
|
|
85
|
+
if (data && typeof data.deviceId === 'number')
|
|
86
|
+
this.removeDeviceName(data.deviceId);
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Override — every logger created hub-side comes pre-tagged with
|
|
91
|
+
* `agentId: 'hub'`. Callers that need per-addon identity chain
|
|
92
|
+
* `.withTags({ addonId })` on top (the merge is right-biased, so
|
|
93
|
+
* the explicit tag wins).
|
|
94
|
+
*/
|
|
95
|
+
createLogger(scope) {
|
|
96
|
+
return super.createLogger(scope).withTags(HUB_BASELINE_TAGS);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Write a log entry received from a forked worker / remote agent.
|
|
100
|
+
* Called by the `$hub.log` action and `log-receiver.ingest` action.
|
|
101
|
+
*
|
|
102
|
+
* Tags propagated by the worker (including `deviceId`, `deviceName`,
|
|
103
|
+
* `integrationId`, `streamId`, etc.) are preserved via `withTags`;
|
|
104
|
+
* baseline tags (`addonId`, `nodeId`, `agentId`) are always ensured
|
|
105
|
+
* even if the worker didn't set them explicitly.
|
|
106
|
+
*/
|
|
107
|
+
writeFromWorker(entry) {
|
|
108
|
+
// Scope is a single optional sub-component label; addon/node identity
|
|
109
|
+
// lives in tags, not in scope. Pass through whatever the worker set.
|
|
110
|
+
let logger = this.createLogger(entry.scope);
|
|
111
|
+
const nodeId = entry.nodeId;
|
|
112
|
+
const agentId = nodeId?.includes('/') ? nodeId.split('/')[0] : nodeId;
|
|
113
|
+
const mergedTags = {
|
|
114
|
+
...entry.tags,
|
|
115
|
+
addonId: entry.addonId,
|
|
116
|
+
...(nodeId !== undefined ? { nodeId } : {}),
|
|
117
|
+
...(agentId !== undefined ? { agentId } : {}),
|
|
118
|
+
};
|
|
119
|
+
logger = logger.withTags(mergedTags);
|
|
120
|
+
const level = entry.level;
|
|
121
|
+
const extras = entry.meta !== undefined ? { meta: entry.meta } : undefined;
|
|
122
|
+
if (typeof logger[level] === 'function') {
|
|
123
|
+
logger[level](entry.message, extras);
|
|
124
|
+
}
|
|
125
|
+
else {
|
|
126
|
+
logger.info(entry.message, extras);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
exports.LoggingService = LoggingService;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ScopedLogger = void 0;
|
|
4
|
+
// Re-export from @camstack/core
|
|
5
|
+
var core_1 = require("@camstack/core");
|
|
6
|
+
Object.defineProperty(exports, "ScopedLogger", { enumerable: true, get: function () { return core_1.ScopedLogger; } });
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.buildCapCallFn = buildCapCallFn;
|
|
4
|
+
/** Extract a numeric `deviceId` routing hint out of arbitrary method args. */
|
|
5
|
+
function extractDeviceId(params) {
|
|
6
|
+
if (params === null || typeof params !== 'object')
|
|
7
|
+
return undefined;
|
|
8
|
+
const raw = Reflect.get(params, 'deviceId');
|
|
9
|
+
return typeof raw === 'number' ? raw : undefined;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Build the `CallFn` (`(method, params, targetNodeId?) => Promise`) for one
|
|
13
|
+
* registered provider. See module docs for the routing rationale.
|
|
14
|
+
*/
|
|
15
|
+
function buildCapCallFn(deps) {
|
|
16
|
+
return async (method, params, targetNodeId) => {
|
|
17
|
+
const deviceId = extractDeviceId(params);
|
|
18
|
+
// ── Hub-local child: route per-child over UDS (collection-safe — keyed by
|
|
19
|
+
// the specific child, not by capName which would collapse a collection
|
|
20
|
+
// cap onto the first child). NEVER fall back to Moleculer: a hub-local
|
|
21
|
+
// child is not a Moleculer service, so a broker call would wait the full
|
|
22
|
+
// discovery timeout for a node that never appears. If the child isn't
|
|
23
|
+
// currently providing the cap, fail fast.
|
|
24
|
+
if (deps.udsChildId !== null && targetNodeId === undefined) {
|
|
25
|
+
const registry = deps.getLocalChildRegistry();
|
|
26
|
+
if (registry !== null && registry.childProvides(deps.udsChildId, deps.capName, deviceId)) {
|
|
27
|
+
deps.onUdsRoute?.(deps.capName);
|
|
28
|
+
const input = {
|
|
29
|
+
capName: deps.capName,
|
|
30
|
+
method,
|
|
31
|
+
args: params,
|
|
32
|
+
...(deviceId !== undefined ? { deviceId } : {}),
|
|
33
|
+
};
|
|
34
|
+
return registry.callCapOnChild(deps.udsChildId, input);
|
|
35
|
+
}
|
|
36
|
+
throw new Error(`hub-local child "${deps.udsChildId}" does not currently provide cap "${deps.capName}"`);
|
|
37
|
+
}
|
|
38
|
+
// ── Agent-hosted or explicit remote: delegate to the unified resolver.
|
|
39
|
+
const resolver = deps.getResolver();
|
|
40
|
+
if (resolver !== null) {
|
|
41
|
+
const route = resolver.resolveCapRoute(deps.capName, {
|
|
42
|
+
nodeId: targetNodeId ?? deps.nodeId,
|
|
43
|
+
...(deviceId !== undefined ? { deviceId } : {}),
|
|
44
|
+
});
|
|
45
|
+
return resolver.dispatch(route, method, params);
|
|
46
|
+
}
|
|
47
|
+
// ── Pre-init window (resolver not yet constructed): legacy Moleculer call.
|
|
48
|
+
return deps.legacyBrokerCall(method, params, targetNodeId ?? deps.nodeId);
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Adapter factories that bridge the server-side registry/service state into the
|
|
4
|
+
* narrow interfaces that CapRouteResolver (in @camstack/kernel) requires.
|
|
5
|
+
*
|
|
6
|
+
* Layer note: this file is in server/backend and may import server-side types.
|
|
7
|
+
* The resolver itself (in @camstack/kernel) must NOT import from here —
|
|
8
|
+
* it depends only on the narrow interfaces (NodeCapAuthority, InProcessProviderLookup)
|
|
9
|
+
* defined in the kernel. These factories are the wiring adapters.
|
|
10
|
+
*
|
|
11
|
+
* nodeIsAgent format rule (from agent-registry.service.ts:74-77):
|
|
12
|
+
* hub → 'hub' → not an agent
|
|
13
|
+
* hub child → 'hub/<runnerId>' → not an agent
|
|
14
|
+
* agent → bare id with no '/' → agent
|
|
15
|
+
*/
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.createNodeCapAuthority = createNodeCapAuthority;
|
|
18
|
+
exports.createInProcessProviderLookup = createInProcessProviderLookup;
|
|
19
|
+
/**
|
|
20
|
+
* Build a NodeCapAuthority backed by a HubNodeRegistry.
|
|
21
|
+
*
|
|
22
|
+
* nodeIsAgent rule: a node is an agent when its id is not 'hub' and does not
|
|
23
|
+
* contain '/' (hub children are `hub/<runnerId>`; agents are bare ids).
|
|
24
|
+
*
|
|
25
|
+
* nodeOnline: uses registry membership — per CLAUDE.md "the registry is the
|
|
26
|
+
* union of registerNode manifests minus disconnected nodes" because
|
|
27
|
+
* `removeNode` is called on `$node.disconnected`. Registry membership is the
|
|
28
|
+
* cleanest, cast-free liveness check.
|
|
29
|
+
*
|
|
30
|
+
* getAgentChildId: always returns null — the hub cannot resolve which forked
|
|
31
|
+
* child under an agent provides a cap (the agent flattens its subtree into
|
|
32
|
+
* one merged manifest). The agent resolves its own child locally (Task 6).
|
|
33
|
+
*/
|
|
34
|
+
function createNodeCapAuthority(nodeRegistry, resolver) {
|
|
35
|
+
return {
|
|
36
|
+
nodeKnowsCap(nodeId, capName) {
|
|
37
|
+
// Check system (manifest) caps first
|
|
38
|
+
const manifest = nodeRegistry.getNodeManifest(nodeId);
|
|
39
|
+
if (manifest !== undefined &&
|
|
40
|
+
manifest.some((addon) => addon.capabilities.includes(capName))) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
// Also check device-scoped native caps — these are NOT in the addon manifest
|
|
44
|
+
const nativeEntries = nodeRegistry.listNativeCapEntries?.() ?? [];
|
|
45
|
+
return nativeEntries.some((n) => n.nodeId === nodeId && n.capName === capName);
|
|
46
|
+
},
|
|
47
|
+
getAddonId(nodeId, capName) {
|
|
48
|
+
// Check system (manifest) caps first
|
|
49
|
+
const manifest = nodeRegistry.getNodeManifest(nodeId);
|
|
50
|
+
if (manifest !== undefined) {
|
|
51
|
+
const manifestAddons = manifest
|
|
52
|
+
.filter((addon) => addon.capabilities.includes(capName))
|
|
53
|
+
.map((addon) => addon.addonId);
|
|
54
|
+
if (manifestAddons.length > 0) {
|
|
55
|
+
// Per-node override / global-default resolution, constrained to what
|
|
56
|
+
// the node actually hosts. Falls back to the first manifest match.
|
|
57
|
+
const resolved = resolver?.resolveSingleton(capName, nodeId) ?? null;
|
|
58
|
+
if (resolved !== null && manifestAddons.includes(resolved))
|
|
59
|
+
return resolved;
|
|
60
|
+
return manifestAddons[0] ?? null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Check device-scoped native caps (unchanged path)
|
|
64
|
+
const nativeEntries = nodeRegistry.listNativeCapEntries?.() ?? [];
|
|
65
|
+
const nat = nativeEntries.find((n) => n.nodeId === nodeId && n.capName === capName);
|
|
66
|
+
return nat?.addonId ?? null;
|
|
67
|
+
},
|
|
68
|
+
nodeIsAgent(nodeId) {
|
|
69
|
+
return nodeId !== 'hub' && !nodeId.includes('/');
|
|
70
|
+
},
|
|
71
|
+
nodeOnline(nodeId) {
|
|
72
|
+
// O(1) Map lookup — registry membership is the authoritative liveness
|
|
73
|
+
// check: HubNodeRegistry.removeNode is called on $node.disconnected,
|
|
74
|
+
// so a defined manifest means the node is connected.
|
|
75
|
+
return nodeRegistry.getNodeManifest(nodeId) !== undefined;
|
|
76
|
+
},
|
|
77
|
+
listNodeIds() {
|
|
78
|
+
return nodeRegistry.listNodeIds();
|
|
79
|
+
},
|
|
80
|
+
getAgentChildId(_agentNodeId, _capName) {
|
|
81
|
+
// The hub cannot resolve the agent's child — the agent resolves locally (Task 6).
|
|
82
|
+
return null;
|
|
83
|
+
},
|
|
84
|
+
isNativeCap(nodeId, capName, deviceId) {
|
|
85
|
+
const nativeEntries = nodeRegistry.listNativeCapEntries?.() ?? [];
|
|
86
|
+
if (deviceId !== undefined) {
|
|
87
|
+
return nativeEntries.some((n) => n.nodeId === nodeId && n.capName === capName && n.deviceId === deviceId);
|
|
88
|
+
}
|
|
89
|
+
return nativeEntries.some((n) => n.nodeId === nodeId && n.capName === capName);
|
|
90
|
+
},
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
// ---------------------------------------------------------------------------
|
|
94
|
+
// createInProcessProviderLookup
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
/**
|
|
97
|
+
* Build an InProcessProviderLookup backed by a CapabilityService.
|
|
98
|
+
*
|
|
99
|
+
* Cast-free design: `getSingleton<Record<string, unknown>>` makes the provider
|
|
100
|
+
* indexable without any `as` casts. `typeof fn === 'function'` narrows to
|
|
101
|
+
* Function (implicit `any` return); calling via `fn.call(provider, args)` is
|
|
102
|
+
* then safe and the result is captured as `unknown`. No `as` casts anywhere.
|
|
103
|
+
*/
|
|
104
|
+
function createInProcessProviderLookup(capabilityService) {
|
|
105
|
+
return (capName) => {
|
|
106
|
+
const provider = capabilityService.getSingletonForNode?.(capName, 'hub') ??
|
|
107
|
+
capabilityService.getSingleton(capName);
|
|
108
|
+
if (provider === null || provider === undefined)
|
|
109
|
+
return null;
|
|
110
|
+
const ref = {
|
|
111
|
+
invoke: (method, args) => {
|
|
112
|
+
const fn = provider[method];
|
|
113
|
+
if (typeof fn !== 'function') {
|
|
114
|
+
return Promise.reject(new Error(`method "${method}" not found on cap "${capName}"`));
|
|
115
|
+
}
|
|
116
|
+
const result = fn.call(provider, args);
|
|
117
|
+
return Promise.resolve(result);
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
return ref;
|
|
121
|
+
};
|
|
122
|
+
}
|