@camstack/server 1.2.73 → 1.2.75
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 +7 -0
- package/dist/agent/builtins-seed.js +84 -0
- package/dist/agent/infra-boot-guard.js +55 -0
- package/dist/agent/infra-boot-plan.js +45 -0
- package/dist/agent/main.js +51 -14
- package/dist/api/sse-no-compression.js +12 -0
- package/dist/api/trpc/generated-cap-routers.js +99 -0
- package/dist/main.js +18 -1
- package/package.json +8 -8
|
@@ -545,6 +545,13 @@ function createAgentService(deps) {
|
|
|
545
545
|
return { success: false, loaded: [] };
|
|
546
546
|
}
|
|
547
547
|
const loaded = await deps.reloadDeployedAddons();
|
|
548
|
+
// The reload just changed this node's capability set. Re-run the
|
|
549
|
+
// atomic registerNode so the hub's routing authority learns it now,
|
|
550
|
+
// instead of at the next restart / reconnect. Unconditional: an
|
|
551
|
+
// empty `loaded` still covers a RE-load of an existing addon whose
|
|
552
|
+
// manifest gained a capability, and registerNode is an idempotent
|
|
553
|
+
// replace (D3), so a no-op re-register costs one acked RPC.
|
|
554
|
+
deps.reconcileNodeRegistration?.();
|
|
548
555
|
return { success: true, loaded };
|
|
549
556
|
},
|
|
550
557
|
},
|
|
@@ -0,0 +1,84 @@
|
|
|
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.seedBuiltinsFromClosure = seedBuiltinsFromClosure;
|
|
37
|
+
/**
|
|
38
|
+
* Self-heal the agent's builtins package before the infra scan.
|
|
39
|
+
*
|
|
40
|
+
* `bootCoreAddons` scans `config.addonsDir` and nothing else, the image seed
|
|
41
|
+
* ships no `@camstack/system`, and the running closure's copy is NOT loaded
|
|
42
|
+
* as an addon — so an agent whose `/data/addons/@camstack/system` is missing
|
|
43
|
+
* (greenfield container, an operator cleanup, a failed deploy) boots with
|
|
44
|
+
* zero infrastructure. Before D87 that produced a silently gutted node; with
|
|
45
|
+
* the D87 guard it produces a crash-loop. Both are wrong answers to a
|
|
46
|
+
* question the node can answer itself: the closure it is RUNNING carries the
|
|
47
|
+
* exact builtins package it needs.
|
|
48
|
+
*
|
|
49
|
+
* The copy is files-list shaped — `package.json` + `dist` — and NEVER
|
|
50
|
+
* `node_modules`: a nested `node_modules/@camstack/*` carries its own
|
|
51
|
+
* `package.json`s, the addon scan discovers those too, and the same addon
|
|
52
|
+
* initializing twice is a boot abort (measured live 2026-08-08, twice, two
|
|
53
|
+
* different vehicles).
|
|
54
|
+
*/
|
|
55
|
+
const fs = __importStar(require("node:fs"));
|
|
56
|
+
const path = __importStar(require("node:path"));
|
|
57
|
+
function seedBuiltinsFromClosure(addonsDir, log = console.log, resolveClosurePkg = () => require.resolve('@camstack/system/package.json')) {
|
|
58
|
+
const target = path.join(addonsDir, '@camstack', 'system');
|
|
59
|
+
if (fs.existsSync(path.join(target, 'package.json')))
|
|
60
|
+
return 'present';
|
|
61
|
+
let closurePkgJson;
|
|
62
|
+
try {
|
|
63
|
+
closurePkgJson = resolveClosurePkg();
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
log('[Agent] builtins seed: @camstack/system not resolvable from the closure — cannot self-heal');
|
|
67
|
+
return 'unavailable';
|
|
68
|
+
}
|
|
69
|
+
const closureRoot = path.dirname(closurePkgJson);
|
|
70
|
+
const closureDist = path.join(closureRoot, 'dist');
|
|
71
|
+
if (!fs.existsSync(closureDist)) {
|
|
72
|
+
log(`[Agent] builtins seed: closure copy at ${closureRoot} has no dist — cannot self-heal`);
|
|
73
|
+
return 'unavailable';
|
|
74
|
+
}
|
|
75
|
+
fs.mkdirSync(target, { recursive: true });
|
|
76
|
+
fs.copyFileSync(closurePkgJson, path.join(target, 'package.json'));
|
|
77
|
+
fs.cpSync(closureDist, path.join(target, 'dist'), {
|
|
78
|
+
recursive: true,
|
|
79
|
+
filter: (src) => !src.split(path.sep).includes('node_modules'),
|
|
80
|
+
});
|
|
81
|
+
log(`[Agent] builtins seed: @camstack/system was missing under ${addonsDir} — ` +
|
|
82
|
+
`seeded package.json + dist from the running closure (${closureRoot})`);
|
|
83
|
+
return 'seeded';
|
|
84
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* infra-boot-guard.ts — the agent's "did my infrastructure actually load?" gate.
|
|
4
|
+
*
|
|
5
|
+
* `bootCoreAddons` resolves each {@link INFRA_CAPABILITIES} entry to an addon
|
|
6
|
+
* package found under `<dataDir>/addons`. Two things can go wrong, and until
|
|
7
|
+
* 2026-08-08 only ONE of them was fatal:
|
|
8
|
+
*
|
|
9
|
+
* - the addon was found but its `initialize()` threw → boot aborted (correct);
|
|
10
|
+
* - the addon was NOT FOUND at all → `console.error`, continue.
|
|
11
|
+
*
|
|
12
|
+
* The second branch is strictly worse than the first and it was the quiet one.
|
|
13
|
+
* On `little-unraid` the whole `@camstack/system` package was renamed out of
|
|
14
|
+
* `/data/addons/@camstack/` at 16:40 on 2026-08-07 (a builtin-shadow cleanup).
|
|
15
|
+
* On an AGENT that directory is the SOLE source of the builtins — there is no
|
|
16
|
+
* seed fallback — so the next boot came up with ZERO infra addons: no storage,
|
|
17
|
+
* no settings-store, no metrics, no `platform-probe`, and no `hub-forwarder`.
|
|
18
|
+
*
|
|
19
|
+
* The node then reported `healthy`, registered its 14 forked addons with the
|
|
20
|
+
* hub, and kept running. Nothing said otherwise, because the ONE addon that
|
|
21
|
+
* ships an agent's logs to the hub (`hub-forwarder`, a `log-destination`) is in
|
|
22
|
+
* the same missing package: the failure was unobservable BY CONSTRUCTION. It
|
|
23
|
+
* surfaced five hours later, and only indirectly, as
|
|
24
|
+
* `getNodeInferenceDevices → reachable:false` on the hub — because the node's
|
|
25
|
+
* `registerNode` manifest legitimately no longer contained `platform-probe`,
|
|
26
|
+
* so `CapRouteResolver.nodeKnowsCap` correctly answered "no provider".
|
|
27
|
+
*
|
|
28
|
+
* `InfraCapability.required` is documented as "boot aborts when this
|
|
29
|
+
* capability's addon fails to initialize". A capability whose addon is not
|
|
30
|
+
* even present has failed harder. This guard makes both branches agree.
|
|
31
|
+
*
|
|
32
|
+
* It reports EVERY missing required capability in one throw, not just the
|
|
33
|
+
* first — an operator staring at a crash-loop needs the whole list, and the
|
|
34
|
+
* four that vanished together on little-unraid vanished for one single reason.
|
|
35
|
+
*/
|
|
36
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
+
exports.assertRequiredInfraResolved = assertRequiredInfraResolved;
|
|
38
|
+
/**
|
|
39
|
+
* Throw when any `required` infra capability resolved to no addon at all.
|
|
40
|
+
*
|
|
41
|
+
* Called AFTER the resolution pass so the message names every missing
|
|
42
|
+
* capability at once. Non-required misses (`platform-probe`,
|
|
43
|
+
* `metrics-provider`, `log-destination`) are the caller's business — they are
|
|
44
|
+
* already logged individually and are legitimately absent on some nodes.
|
|
45
|
+
*/
|
|
46
|
+
function assertRequiredInfraResolved(resolutions, addonsDir) {
|
|
47
|
+
const missing = resolutions.filter((r) => r.required && r.addonId === null).map((r) => r.name);
|
|
48
|
+
if (missing.length === 0)
|
|
49
|
+
return;
|
|
50
|
+
throw new Error(`Required infrastructure capabilities have no addon under "${addonsDir}": ` +
|
|
51
|
+
`${missing.join(', ')}. The agent's builtins package (@camstack/system) is ` +
|
|
52
|
+
`missing or unreadable — this node cannot store, configure or probe itself, ` +
|
|
53
|
+
`and every hub routing decision that consults its manifest would be wrong. ` +
|
|
54
|
+
`Refusing to boot degraded.`);
|
|
55
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.planInfraBoot = planInfraBoot;
|
|
4
|
+
exports.planToResolutions = planToResolutions;
|
|
5
|
+
/**
|
|
6
|
+
* `log-destination` prefers `hub-forwarder` (ships agent logs to the hub)
|
|
7
|
+
* over a local winston — mirrors the historical inline choice.
|
|
8
|
+
*/
|
|
9
|
+
function pickCandidate(capName, candidates) {
|
|
10
|
+
if (capName === 'log-destination') {
|
|
11
|
+
return candidates.find((a) => a.id === 'hub-forwarder') ?? candidates[0];
|
|
12
|
+
}
|
|
13
|
+
return candidates[0];
|
|
14
|
+
}
|
|
15
|
+
function planInfraBoot(addons, infraList) {
|
|
16
|
+
const initialized = new Set();
|
|
17
|
+
const steps = [];
|
|
18
|
+
for (const infra of infraList) {
|
|
19
|
+
const candidates = addons.filter((a) => a.capabilities.includes(infra.name));
|
|
20
|
+
const chosen = pickCandidate(infra.name, candidates);
|
|
21
|
+
if (!chosen) {
|
|
22
|
+
steps.push({
|
|
23
|
+
capName: infra.name,
|
|
24
|
+
required: infra.required,
|
|
25
|
+
addonId: null,
|
|
26
|
+
initialize: false,
|
|
27
|
+
});
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const first = !initialized.has(chosen.id);
|
|
31
|
+
if (first)
|
|
32
|
+
initialized.add(chosen.id);
|
|
33
|
+
steps.push({
|
|
34
|
+
capName: infra.name,
|
|
35
|
+
required: infra.required,
|
|
36
|
+
addonId: chosen.id,
|
|
37
|
+
initialize: first,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return steps;
|
|
41
|
+
}
|
|
42
|
+
/** Project a plan onto the guard's resolution shape. */
|
|
43
|
+
function planToResolutions(steps) {
|
|
44
|
+
return steps.map((s) => ({ name: s.capName, required: s.required, addonId: s.addonId }));
|
|
45
|
+
}
|
package/dist/agent/main.js
CHANGED
|
@@ -45,6 +45,9 @@ const types_2 = require("@camstack/types");
|
|
|
45
45
|
const agent_config_js_1 = require("./agent-config.js");
|
|
46
46
|
const agent_update_service_js_1 = require("./agent-update-service.js");
|
|
47
47
|
const server_management_provider_js_1 = require("../api/core/server-management.provider.js");
|
|
48
|
+
const infra_boot_guard_js_1 = require("./infra-boot-guard.js");
|
|
49
|
+
const infra_boot_plan_js_1 = require("./infra-boot-plan.js");
|
|
50
|
+
const builtins_seed_js_1 = require("./builtins-seed.js");
|
|
48
51
|
const agent_service_js_1 = require("./agent-service.js");
|
|
49
52
|
const agent_group_runner_js_1 = require("./agent-group-runner.js");
|
|
50
53
|
const register_agent_cap_dispatch_js_1 = require("./register-agent-cap-dispatch.js");
|
|
@@ -409,6 +412,13 @@ async function startAgent(configPath) {
|
|
|
409
412
|
}
|
|
410
413
|
return loaded;
|
|
411
414
|
},
|
|
415
|
+
// The reload above changed this node's capability set — re-derive the
|
|
416
|
+
// manifest from the live registry and re-run the atomic `registerNode`
|
|
417
|
+
// so the hub's `CapRouteResolver` can route to what this node now has.
|
|
418
|
+
// Hung on the existing `$agent.reload` trigger; no timer (D3).
|
|
419
|
+
reconcileNodeRegistration: () => {
|
|
420
|
+
triggerUpwardRegistration();
|
|
421
|
+
},
|
|
412
422
|
// D3 subtree aggregation: when a group-runner child delivers its manifest
|
|
413
423
|
// via `$agent.registerNode`, merge it into the local subtree registry and
|
|
414
424
|
// immediately re-register the complete union with the hub.
|
|
@@ -759,6 +769,10 @@ async function startAgent(configPath) {
|
|
|
759
769
|
// Core infra addons to load on agent — all infra including log-destination (hub-forwarder)
|
|
760
770
|
const AGENT_INFRA = system_1.INFRA_CAPABILITIES;
|
|
761
771
|
async function bootCoreAddons(broker, config, registry, loadedAddons, loggerFactory) {
|
|
772
|
+
// Self-heal FIRST: a missing builtins package is answerable from the
|
|
773
|
+
// running closure (see `builtins-seed.ts`) — without this, the D87 guard
|
|
774
|
+
// below turns a recoverable gap into a crash-loop.
|
|
775
|
+
(0, builtins_seed_js_1.seedBuiltinsFromClosure)(config.addonsDir);
|
|
762
776
|
// Scan every installed addon package — infra providers may live outside
|
|
763
777
|
// `@camstack/system` (e.g. `@camstack/addon-platform-probe-native`).
|
|
764
778
|
const packageDirs = resolveAddonPackageDirs(config.addonsDir);
|
|
@@ -776,21 +790,38 @@ async function bootCoreAddons(broker, config, registry, loadedAddons, loggerFact
|
|
|
776
790
|
console.warn(`[Agent] Failed to scan ${dir}: ${(0, types_2.errMsg)(err)}`);
|
|
777
791
|
}
|
|
778
792
|
}
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
793
|
+
// Resolution is per-CAPABILITY (the guard names every missing required
|
|
794
|
+
// cap), initialization is per-ADDON: since D44 one addon legitimately
|
|
795
|
+
// serves several infra caps (`storage-orchestrator` owns both `storage`
|
|
796
|
+
// and `settings-store`), and initializing it once per capability
|
|
797
|
+
// re-registers the same provider — a boot abort by the registry's own
|
|
798
|
+
// double-registration error. That combination took both agents down on
|
|
799
|
+
// 2026-08-08 the moment D87 promoted infra failures to fatal. The plan
|
|
800
|
+
// makes the dedupe explicit and testable (see `infra-boot-plan.ts`).
|
|
801
|
+
const byId = new Map(loader.listAddons().map((a) => [a.declaration.id, a]));
|
|
802
|
+
const plan = (0, infra_boot_plan_js_1.planInfraBoot)(loader.listAddons().map((a) => ({
|
|
803
|
+
id: a.declaration.id,
|
|
804
|
+
capabilities: (a.declaration.capabilities ?? []).map((c) => typeof c === 'string' ? c : c.name),
|
|
805
|
+
})), AGENT_INFRA);
|
|
806
|
+
const resolutions = (0, infra_boot_plan_js_1.planToResolutions)(plan);
|
|
807
|
+
for (const step of plan) {
|
|
808
|
+
if (step.addonId === null) {
|
|
809
|
+
// Say it per-capability (an OPTIONAL miss is the only thing this line
|
|
810
|
+
// will ever report given the guard below); the required ones abort
|
|
811
|
+
// boot together after the pass.
|
|
812
|
+
console.error(`[Agent] Infrastructure addon for "${step.capName}" not found`);
|
|
792
813
|
continue;
|
|
793
814
|
}
|
|
815
|
+
if (!step.initialize) {
|
|
816
|
+
// Served by an addon an earlier capability already booted — the
|
|
817
|
+
// entry exists so a deployment missing it fails loudly, not to boot
|
|
818
|
+
// the addon twice.
|
|
819
|
+
console.log(`[Agent] Infra capability "${step.capName}" served by already-booted "${step.addonId}"`);
|
|
820
|
+
continue;
|
|
821
|
+
}
|
|
822
|
+
const addon = byId.get(step.addonId);
|
|
823
|
+
if (!addon)
|
|
824
|
+
continue;
|
|
794
825
|
const addonId = addon.declaration.id;
|
|
795
826
|
try {
|
|
796
827
|
const instance = new addon.addonClass();
|
|
@@ -829,11 +860,17 @@ async function bootCoreAddons(broker, config, registry, loadedAddons, loggerFact
|
|
|
829
860
|
catch (err) {
|
|
830
861
|
const msg = (0, types_2.errMsg)(err);
|
|
831
862
|
console.error(`[Agent] Failed to initialize core addon "${addonId}": ${msg}`);
|
|
832
|
-
if (
|
|
863
|
+
if (step.required) {
|
|
833
864
|
throw new Error(`Required infrastructure addon "${addonId}" failed: ${msg}`, { cause: err });
|
|
834
865
|
}
|
|
835
866
|
}
|
|
836
867
|
}
|
|
868
|
+
// A required infra capability with NO addon at all is strictly worse than
|
|
869
|
+
// one whose addon threw (which already aborted above) — and it used to be
|
|
870
|
+
// the SILENT branch. Abort here instead of booting a node that cannot
|
|
871
|
+
// store, configure or probe itself and whose manifest then makes every hub
|
|
872
|
+
// routing decision about it wrong. See `infra-boot-guard.ts`.
|
|
873
|
+
(0, infra_boot_guard_js_1.assertRequiredInfraResolved)(resolutions, config.addonsDir);
|
|
837
874
|
}
|
|
838
875
|
// ---------------------------------------------------------------------------
|
|
839
876
|
// Phase 1.5: Load cluster-capable addon packages
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.markSseRequestNoCompression = markSseRequestNoCompression;
|
|
4
|
+
/** Mutates the request headers in place; returns whether it marked the request. */
|
|
5
|
+
function markSseRequestNoCompression(request) {
|
|
6
|
+
const accept = request.headers.accept;
|
|
7
|
+
if (typeof accept === 'string' && accept.includes('text/event-stream')) {
|
|
8
|
+
request.headers['x-no-compression'] = '1';
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
@@ -5817,6 +5817,105 @@ function createCapRouter_pipelineAnalytics(getProvider, createRemoteProxy) {
|
|
|
5817
5817
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
5818
5818
|
return p.getTrainingExportUrl(methodInput);
|
|
5819
5819
|
}),
|
|
5820
|
+
listRetrainStaging: trpc_middleware_js_1.adminProcedure
|
|
5821
|
+
.input(types_81.pipelineAnalyticsCapability.methods.listRetrainStaging.input.loose())
|
|
5822
|
+
.output(types_81.pipelineAnalyticsCapability.methods.listRetrainStaging.output)
|
|
5823
|
+
.query(async ({ input, ctx }) => {
|
|
5824
|
+
const { nodeId, ...methodInput } = input;
|
|
5825
|
+
const p = resolveProvider('pipeline-analytics', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
5826
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
5827
|
+
return p.listRetrainStaging(methodInput);
|
|
5828
|
+
}),
|
|
5829
|
+
listRetrainFrames: trpc_middleware_js_1.adminProcedure
|
|
5830
|
+
.input(types_81.pipelineAnalyticsCapability.methods.listRetrainFrames.input.loose())
|
|
5831
|
+
.output(types_81.pipelineAnalyticsCapability.methods.listRetrainFrames.output)
|
|
5832
|
+
.query(async ({ input, ctx }) => {
|
|
5833
|
+
const { nodeId, ...methodInput } = input;
|
|
5834
|
+
const p = resolveProvider('pipeline-analytics', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
5835
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
5836
|
+
return p.listRetrainFrames(methodInput);
|
|
5837
|
+
}),
|
|
5838
|
+
selectRetrainFrames: trpc_middleware_js_1.adminProcedure
|
|
5839
|
+
.input(types_81.pipelineAnalyticsCapability.methods.selectRetrainFrames.input.loose())
|
|
5840
|
+
.output(types_81.pipelineAnalyticsCapability.methods.selectRetrainFrames.output)
|
|
5841
|
+
.mutation(async ({ input, ctx }) => {
|
|
5842
|
+
const { nodeId, ...methodInput } = input;
|
|
5843
|
+
const p = resolveProvider('pipeline-analytics', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
5844
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
5845
|
+
return p.selectRetrainFrames(methodInput);
|
|
5846
|
+
}),
|
|
5847
|
+
deselectRetrainFrame: trpc_middleware_js_1.adminProcedure
|
|
5848
|
+
.input(types_81.pipelineAnalyticsCapability.methods.deselectRetrainFrame.input.loose())
|
|
5849
|
+
.output(types_81.pipelineAnalyticsCapability.methods.deselectRetrainFrame.output)
|
|
5850
|
+
.mutation(async ({ input, ctx }) => {
|
|
5851
|
+
const { nodeId, ...methodInput } = input;
|
|
5852
|
+
const p = resolveProvider('pipeline-analytics', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
5853
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
5854
|
+
return p.deselectRetrainFrame(methodInput);
|
|
5855
|
+
}),
|
|
5856
|
+
getRetrainFrameImage: trpc_middleware_js_1.adminProcedure
|
|
5857
|
+
.input(types_81.pipelineAnalyticsCapability.methods.getRetrainFrameImage.input.loose())
|
|
5858
|
+
.output(types_81.pipelineAnalyticsCapability.methods.getRetrainFrameImage.output)
|
|
5859
|
+
.query(async ({ input, ctx }) => {
|
|
5860
|
+
const { nodeId, ...methodInput } = input;
|
|
5861
|
+
const p = resolveProvider('pipeline-analytics', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
5862
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
5863
|
+
return p.getRetrainFrameImage(methodInput);
|
|
5864
|
+
}),
|
|
5865
|
+
proposeRetrainAnnotations: trpc_middleware_js_1.adminProcedure
|
|
5866
|
+
.input(types_81.pipelineAnalyticsCapability.methods.proposeRetrainAnnotations.input.loose())
|
|
5867
|
+
.output(types_81.pipelineAnalyticsCapability.methods.proposeRetrainAnnotations.output)
|
|
5868
|
+
.mutation(async ({ input, ctx }) => {
|
|
5869
|
+
const { nodeId, ...methodInput } = input;
|
|
5870
|
+
const p = resolveProvider('pipeline-analytics', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
5871
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
5872
|
+
return p.proposeRetrainAnnotations(methodInput);
|
|
5873
|
+
}),
|
|
5874
|
+
listRetrainAnnotations: trpc_middleware_js_1.adminProcedure
|
|
5875
|
+
.input(types_81.pipelineAnalyticsCapability.methods.listRetrainAnnotations.input.loose())
|
|
5876
|
+
.output(types_81.pipelineAnalyticsCapability.methods.listRetrainAnnotations.output)
|
|
5877
|
+
.query(async ({ input, ctx }) => {
|
|
5878
|
+
const { nodeId, ...methodInput } = input;
|
|
5879
|
+
const p = resolveProvider('pipeline-analytics', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
5880
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
5881
|
+
return p.listRetrainAnnotations(methodInput);
|
|
5882
|
+
}),
|
|
5883
|
+
saveRetrainAnnotations: trpc_middleware_js_1.adminProcedure
|
|
5884
|
+
.input(types_81.pipelineAnalyticsCapability.methods.saveRetrainAnnotations.input.loose())
|
|
5885
|
+
.output(types_81.pipelineAnalyticsCapability.methods.saveRetrainAnnotations.output)
|
|
5886
|
+
.mutation(async ({ input, ctx }) => {
|
|
5887
|
+
const { nodeId, ...methodInput } = input;
|
|
5888
|
+
const p = resolveProvider('pipeline-analytics', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
5889
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
5890
|
+
return p.saveRetrainAnnotations(methodInput);
|
|
5891
|
+
}),
|
|
5892
|
+
completeRetrainTrack: trpc_middleware_js_1.adminProcedure
|
|
5893
|
+
.input(types_81.pipelineAnalyticsCapability.methods.completeRetrainTrack.input.loose())
|
|
5894
|
+
.output(types_81.pipelineAnalyticsCapability.methods.completeRetrainTrack.output)
|
|
5895
|
+
.mutation(async ({ input, ctx }) => {
|
|
5896
|
+
const { nodeId, ...methodInput } = input;
|
|
5897
|
+
const p = resolveProvider('pipeline-analytics', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
5898
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
5899
|
+
return p.completeRetrainTrack(methodInput);
|
|
5900
|
+
}),
|
|
5901
|
+
restageRetrainTrack: trpc_middleware_js_1.adminProcedure
|
|
5902
|
+
.input(types_81.pipelineAnalyticsCapability.methods.restageRetrainTrack.input.loose())
|
|
5903
|
+
.output(types_81.pipelineAnalyticsCapability.methods.restageRetrainTrack.output)
|
|
5904
|
+
.mutation(async ({ input, ctx }) => {
|
|
5905
|
+
const { nodeId, ...methodInput } = input;
|
|
5906
|
+
const p = resolveProvider('pipeline-analytics', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
5907
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
5908
|
+
return p.restageRetrainTrack(methodInput);
|
|
5909
|
+
}),
|
|
5910
|
+
getRetrainExportUrl: trpc_middleware_js_1.adminProcedure
|
|
5911
|
+
.input(types_81.pipelineAnalyticsCapability.methods.getRetrainExportUrl.input.loose())
|
|
5912
|
+
.output(types_81.pipelineAnalyticsCapability.methods.getRetrainExportUrl.output)
|
|
5913
|
+
.query(async ({ input, ctx }) => {
|
|
5914
|
+
const { nodeId, ...methodInput } = input;
|
|
5915
|
+
const p = resolveProvider('pipeline-analytics', nodeId, () => getProvider(ctx), createRemoteProxy);
|
|
5916
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
|
|
5917
|
+
return p.getRetrainExportUrl(methodInput);
|
|
5918
|
+
}),
|
|
5820
5919
|
getEventMedia: trpc_middleware_js_1.protectedProcedure
|
|
5821
5920
|
.input(types_81.pipelineAnalyticsCapability.methods.getEventMedia.input.loose())
|
|
5822
5921
|
.output(types_81.pipelineAnalyticsCapability.methods.getEventMedia.output)
|
package/dist/main.js
CHANGED
|
@@ -42,6 +42,7 @@ const ws_1 = require("@trpc/server/adapters/ws");
|
|
|
42
42
|
const static_1 = __importDefault(require("@fastify/static"));
|
|
43
43
|
const compress_1 = __importDefault(require("@fastify/compress"));
|
|
44
44
|
const cookie_1 = __importDefault(require("@fastify/cookie"));
|
|
45
|
+
const sse_no_compression_js_1 = require("./api/sse-no-compression.js");
|
|
45
46
|
const ws_2 = require("ws");
|
|
46
47
|
const fs = __importStar(require("node:fs"));
|
|
47
48
|
const path = __importStar(require("node:path"));
|
|
@@ -223,7 +224,16 @@ async function bootstrap() {
|
|
|
223
224
|
// replaces — so the telemetry reaches a running hub the same day it is
|
|
224
225
|
// written. See packages/system/src/kernel/heap-watch.ts for why it exists
|
|
225
226
|
// (four silent OOMs in ~15h, one nine seconds after a viewer connected).
|
|
226
|
-
|
|
227
|
+
//
|
|
228
|
+
// The reclaimer is what turns the heartbeat from a report into a fix. hub-main's
|
|
229
|
+
// RSS is a HIGH-WATER MARK — measured over 1019 samples and seven boots, it never
|
|
230
|
+
// once fell below the running maximum of heapTotal+external — because V8 keeps the
|
|
231
|
+
// 256KB pages it committed at a sawtooth peak, and an ordinary major GC does not
|
|
232
|
+
// return them. `createV8Reclaimer()` returns undefined if V8 declines, and the
|
|
233
|
+
// heartbeat then behaves exactly as it did before.
|
|
234
|
+
const heapReclaimer = (0, system_2.createV8Reclaimer)();
|
|
235
|
+
const heapReclaim = heapReclaimer === undefined ? undefined : { reclaim: heapReclaimer };
|
|
236
|
+
(0, system_2.startHeapWatch)('hub-main', undefined, undefined, heapReclaim);
|
|
227
237
|
// Clean up orphaned processes from previous crashes before starting
|
|
228
238
|
cleanupOrphanProcesses();
|
|
229
239
|
// SPA fallback — set later when admin UI is resolved, used by addon route catch-all
|
|
@@ -252,6 +262,13 @@ async function bootstrap() {
|
|
|
252
262
|
// Registered before @fastify/static so the compress plugin wraps the
|
|
253
263
|
// static send path — hashed admin-ui chunks go from ~2MB to ~600KB on
|
|
254
264
|
// the wire. threshold:1024 skips compression for tiny payloads.
|
|
265
|
+
//
|
|
266
|
+
// SSE opt-out FIRST: without it a gzipped low-rate event stream starves
|
|
267
|
+
// silently in the compressor buffer — see `api/sse-no-compression.ts` for
|
|
268
|
+
// the mechanism and the live measurement.
|
|
269
|
+
fastify.addHook('onRequest', async (request) => {
|
|
270
|
+
(0, sse_no_compression_js_1.markSseRequestNoCompression)(request);
|
|
271
|
+
});
|
|
255
272
|
await fastify.register(compress_1.default, { global: true, threshold: 1024 });
|
|
256
273
|
// Data-plane POST bodies: the addon reverse-proxy (`proxyToUpstream`) pipes
|
|
257
274
|
// `request.raw` upstream, but Fastify's default application/json parser would
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/server",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.75",
|
|
4
4
|
"private": false,
|
|
5
5
|
"files": [
|
|
6
6
|
"dist",
|
|
@@ -33,19 +33,19 @@
|
|
|
33
33
|
]
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
|
-
"@camstack/addon-admin-ui": "1.2.
|
|
36
|
+
"@camstack/addon-admin-ui": "1.2.35",
|
|
37
37
|
"@camstack/addon-agent-ui": "1.2.10",
|
|
38
38
|
"@camstack/addon-auth": "1.2.11",
|
|
39
39
|
"@camstack/addon-decoder-nodeav": "1.2.9",
|
|
40
40
|
"@camstack/addon-notifiers": "1.2.13",
|
|
41
|
-
"@camstack/addon-pipeline": "1.2.
|
|
42
|
-
"@camstack/addon-pipeline-orchestrator": "1.2.
|
|
43
|
-
"@camstack/addon-post-analysis": "1.2.
|
|
41
|
+
"@camstack/addon-pipeline": "1.2.46",
|
|
42
|
+
"@camstack/addon-pipeline-orchestrator": "1.2.29",
|
|
43
|
+
"@camstack/addon-post-analysis": "1.2.51",
|
|
44
44
|
"@camstack/sdk": "1.2.10",
|
|
45
45
|
"@camstack/shm-ring": "1.1.9",
|
|
46
|
-
"@camstack/system": "1.2.
|
|
47
|
-
"@camstack/types": "1.2.
|
|
48
|
-
"@camstack/ui-library": "1.2.
|
|
46
|
+
"@camstack/system": "1.2.61",
|
|
47
|
+
"@camstack/types": "1.2.45",
|
|
48
|
+
"@camstack/ui-library": "1.2.33",
|
|
49
49
|
"@fastify/compress": "^9.0.0",
|
|
50
50
|
"@fastify/cookie": "^11.0.2",
|
|
51
51
|
"@fastify/cors": "^11.2.0",
|