@camstack/server 1.1.75 → 1.2.0

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.
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ /**
3
+ * Extracted, testable helper for registering the agent-side cap-dispatch
4
+ * service with a Moleculer broker.
5
+ *
6
+ * The helper is separated from `agent-bootstrap.ts` so the wiring decision
7
+ * (undefined → no-op; defined → create service) can be exercised in a unit
8
+ * test independently of the full bootstrap lifecycle.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.registerAgentCapDispatch = registerAgentCapDispatch;
12
+ const agent_cap_dispatch_service_js_1 = require("./agent-cap-dispatch-service.js");
13
+ // ---------------------------------------------------------------------------
14
+ // Exported helper
15
+ // ---------------------------------------------------------------------------
16
+ /**
17
+ * Registers the `$agent-cap-fwd` Moleculer service with `registrar` when
18
+ * `agentUdsRegistry` is defined; otherwise this is a no-op.
19
+ *
20
+ * Called from `agent-bootstrap.ts` after the UDS child registry starts
21
+ * successfully so the hub can route `agent-child-forward` cap calls to this
22
+ * agent's forked children.
23
+ *
24
+ * @param registrar Moleculer broker (or compatible double in tests).
25
+ * @param agentUdsRegistry The agent's `LocalChildRegistry`; pass `undefined`
26
+ * to skip registration (UDS failed to start).
27
+ * @param inProcessLookup Optional resolver for caps hosted in the agent's own
28
+ * main process (core addons); forwarded to the service
29
+ * so hub-dispatched calls to those caps resolve instead
30
+ * of failing with "no provider".
31
+ * @param logger Optional scoped logger forwarded to the service.
32
+ */
33
+ function registerAgentCapDispatch(registrar, agentUdsRegistry, inProcessLookup, logger) {
34
+ if (agentUdsRegistry === undefined)
35
+ return;
36
+ registrar.createService((0, agent_cap_dispatch_service_js_1.createAgentCapDispatchService)(registrar.nodeID, agentUdsRegistry, inProcessLookup, logger));
37
+ }
@@ -5385,13 +5385,6 @@ function createCapRouter_pipelineExecutor(getProvider, createRemoteProxy) {
5385
5385
  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
5386
5386
  return p.getDefaultSteps(methodInput);
5387
5387
  }),
5388
- reprobeEngine: trpc_middleware_js_1.adminProcedure
5389
- .input(zod_1.z.object({ nodeId: zod_1.z.string().optional() }).optional())
5390
- .output(types_79.pipelineExecutorCapability.methods.reprobeEngine.output)
5391
- .mutation(async ({ input, ctx }) => {
5392
- const p = resolveProvider('pipeline-executor', input?.nodeId, () => getProvider(ctx), createRemoteProxy);
5393
- return p.reprobeEngine();
5394
- }),
5395
5388
  getEngineProvisioning: trpc_middleware_js_1.protectedProcedure
5396
5389
  .input(types_79.pipelineExecutorCapability.methods.getEngineProvisioning.input.loose())
5397
5390
  .output(types_79.pipelineExecutorCapability.methods.getEngineProvisioning.output)
@@ -5707,6 +5700,22 @@ function createCapRouter_pipelineOrchestrator(getProvider, _createRemoteProxy) {
5707
5700
  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
5708
5701
  return p.unassignPipeline(input);
5709
5702
  }),
5703
+ setPipelineDevicePin: trpc_middleware_js_1.adminProcedure
5704
+ .input(types_80.pipelineOrchestratorCapability.methods.setPipelineDevicePin.input.loose())
5705
+ .output(types_80.pipelineOrchestratorCapability.methods.setPipelineDevicePin.output)
5706
+ .mutation(async ({ input, ctx }) => {
5707
+ const p = requireCapProvider('pipeline-orchestrator', () => getProvider(ctx));
5708
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
5709
+ return p.setPipelineDevicePin(input);
5710
+ }),
5711
+ getPipelineDevicePin: trpc_middleware_js_1.protectedProcedure
5712
+ .input(types_80.pipelineOrchestratorCapability.methods.getPipelineDevicePin.input.loose())
5713
+ .output(types_80.pipelineOrchestratorCapability.methods.getPipelineDevicePin.output)
5714
+ .query(async ({ input, ctx }) => {
5715
+ const p = requireCapProvider('pipeline-orchestrator', () => getProvider(ctx));
5716
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
5717
+ return p.getPipelineDevicePin(input);
5718
+ }),
5710
5719
  rebalance: trpc_middleware_js_1.adminProcedure
5711
5720
  .input(zod_1.z.object({ nodeId: zod_1.z.string().optional() }).optional())
5712
5721
  .output(types_80.pipelineOrchestratorCapability.methods.rebalance.output)
@@ -5835,14 +5844,6 @@ function createCapRouter_pipelineOrchestrator(getProvider, _createRemoteProxy) {
5835
5844
  const p = requireCapProvider('pipeline-orchestrator', () => getProvider(ctx));
5836
5845
  return p.listAgentSettings();
5837
5846
  }),
5838
- setAgentAddonDefaults: trpc_middleware_js_1.adminProcedure
5839
- .input(types_80.pipelineOrchestratorCapability.methods.setAgentAddonDefaults.input.loose())
5840
- .output(types_80.pipelineOrchestratorCapability.methods.setAgentAddonDefaults.output)
5841
- .mutation(async ({ input, ctx }) => {
5842
- const p = requireCapProvider('pipeline-orchestrator', () => getProvider(ctx));
5843
- // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
5844
- return p.setAgentAddonDefaults(input);
5845
- }),
5846
5847
  removeAgentSettings: trpc_middleware_js_1.adminProcedure
5847
5848
  .input(types_80.pipelineOrchestratorCapability.methods.removeAgentSettings.input.loose())
5848
5849
  .output(types_80.pipelineOrchestratorCapability.methods.removeAgentSettings.output)
@@ -5891,6 +5892,14 @@ function createCapRouter_pipelineOrchestrator(getProvider, _createRemoteProxy) {
5891
5892
  // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
5892
5893
  return p.setAgentInferenceDevices(input);
5893
5894
  }),
5895
+ getNodeInferenceDevices: trpc_middleware_js_1.protectedProcedure
5896
+ .input(types_80.pipelineOrchestratorCapability.methods.getNodeInferenceDevices.input.loose())
5897
+ .output(types_80.pipelineOrchestratorCapability.methods.getNodeInferenceDevices.output)
5898
+ .query(async ({ input, ctx }) => {
5899
+ const p = requireCapProvider('pipeline-orchestrator', () => getProvider(ctx));
5900
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-argument
5901
+ return p.getNodeInferenceDevices(input);
5902
+ }),
5894
5903
  resetNodePipelineDefaults: trpc_middleware_js_1.adminProcedure
5895
5904
  .input(types_80.pipelineOrchestratorCapability.methods.resetNodePipelineDefaults.input.loose())
5896
5905
  .output(types_80.pipelineOrchestratorCapability.methods.resetNodePipelineDefaults.output)
@@ -64,9 +64,10 @@ const AGENT_RECONCILE_RPC_TIMEOUT_MS = 8_000;
64
64
  * from `hubPlacements`). The hub's deploy planner already SKIPS
65
65
  * agent-only addons, so undeploying one is a reconcile asymmetry bug.
66
66
  *
67
- * The hub cannot consult `AddonInstaller.AGENT_PACKAGES` directly: it
68
- * derives from `@camstack/agent`, which is not installed in a hub image,
69
- * so that list is empty hub-side. Hence this explicit allowlist.
67
+ * The hub keeps this explicit allowlist rather than importing
68
+ * `AddonInstaller.AGENT_PACKAGES` (agent-role bootstrap plumbing) into the
69
+ * registry service the reconcile only needs the agent-only addon names to
70
+ * skip them, so a small local set keeps the two concerns decoupled.
70
71
  */
71
72
  const AGENT_BOOTSTRAP_PACKAGES = new Set([
72
73
  '@camstack/system',
@@ -53,8 +53,13 @@ exports.ServerUpdateService = void 0;
53
53
  */
54
54
  const path = __importStar(require("node:path"));
55
55
  const index_js_1 = require("../../server-root/index.js");
56
+ const system_exec_npm_js_1 = require("./system-exec-npm.js");
56
57
  class ServerUpdateService extends index_js_1.RootUpdateService {
57
58
  constructor(options) {
59
+ const dataDir = path.resolve(options.dataDir ??
60
+ options.env?.['CAMSTACK_DATA'] ??
61
+ process.env['CAMSTACK_DATA'] ??
62
+ 'camstack-data');
58
63
  super({
59
64
  spec: index_js_1.HUB_ROOT_SPEC,
60
65
  envNames: {
@@ -64,15 +69,19 @@ class ServerUpdateService extends index_js_1.RootUpdateService {
64
69
  },
65
70
  logger: options.logger,
66
71
  restartServer: options.restartServer,
67
- dataDir: path.resolve(options.dataDir ??
68
- options.env?.['CAMSTACK_DATA'] ??
69
- process.env['CAMSTACK_DATA'] ??
70
- 'camstack-data'),
72
+ dataDir,
71
73
  // dist/core/server-update/ → ../../../package.json = the running copy's
72
74
  // package.json (workspace server/backend OR the active/baked closure).
73
75
  runningPackageJsonPath: options.runningPackageJsonPath ?? path.resolve(__dirname, '..', '..', '..', 'package.json'),
74
76
  workspaceProbeDir: __dirname,
75
- execNpm: options.execNpm,
77
+ // Default to the runNpm-backed adapter so a host without system npm (a
78
+ // packaged desktop app with a bare bundled Node) can still stage a root
79
+ // update. `cacheDir` mirrors AddonInstaller's `<dataDir>/addons/.npm-bootstrap`.
80
+ execNpm: options.execNpm ??
81
+ (0, system_exec_npm_js_1.buildSystemExecNpm)({
82
+ cacheDir: path.join(dataDir, 'addons', '.npm-bootstrap'),
83
+ logger: options.logger,
84
+ }),
76
85
  env: options.env,
77
86
  now: options.now,
78
87
  });
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.buildSystemExecNpm = buildSystemExecNpm;
4
+ /**
5
+ * `buildSystemExecNpm` — the runNpm-backed `execNpm` the shared
6
+ * `RootUpdateService` uses to shell out to npm when staging a root-package
7
+ * closure (`@camstack/server` — the single root package for every node role).
8
+ *
9
+ * The engine's built-in default is a raw `execFile('npm', ...)`: on a host with
10
+ * NO system npm on PATH — a packaged desktop app shipping a bare bundled Node —
11
+ * that dies with ENOENT and a root update can never stage. Routing through
12
+ * `@camstack/system`'s `runNpm` solves it uniformly: resolve a working npm
13
+ * (system → previously-bootstrapped copy under `cacheDir` → bootstrap from the
14
+ * registry) and run the SAME args with it. The `cacheDir` mirrors the value
15
+ * `AddonInstaller` uses for its own npm bootstrap (`<dataDir>/addons/.npm-bootstrap`)
16
+ * so both share one bootstrapped npm per host.
17
+ *
18
+ * `runNpmFn` is a module-level DI seam (defaults to the real `runNpm`) so the
19
+ * arg/cwd/timeout mapping is unit-testable without touching the network.
20
+ */
21
+ const system_1 = require("@camstack/system");
22
+ function buildSystemExecNpm(options, runNpmFn = system_1.runNpm) {
23
+ return async (args, opts) => {
24
+ const { stdout } = await runNpmFn(args, {
25
+ cacheDir: options.cacheDir,
26
+ registry: options.registry,
27
+ logger: options.logger,
28
+ ...(opts.cwd !== undefined ? { cwd: opts.cwd } : {}),
29
+ timeout: opts.timeoutMs,
30
+ });
31
+ return { stdout };
32
+ };
33
+ }
package/dist/launcher.js CHANGED
@@ -236,6 +236,14 @@ function readBootstrapRequiredAddons(dataDir, bootstrapSchema) {
236
236
  }
237
237
  async function launch() {
238
238
  const dataDir = DATA_DIR;
239
+ // Node role (hub | agent) — resolved ONCE here. Dynamic import keeps the
240
+ // launcher's zero-static-@camstack/*-import invariant intact even though
241
+ // `node-role.ts` imports no @camstack/* itself. The role selects both the
242
+ // bootstrap package set (hub → all addons; agent → AGENT_PACKAGES) and,
243
+ // at the very end of launch(), which runtime entry is loaded.
244
+ const { resolveNodeRole } = await Promise.resolve().then(() => __importStar(require('./node-role.js')));
245
+ const role = resolveNodeRole(process.env);
246
+ console.log(`[launcher] Node role: ${role}`);
239
247
  // Addons live in a real `node_modules`-shaped dir so ESM `import` resolves
240
248
  // every @camstack/* (system + addon-*) from there via the standard
241
249
  // walk-up — NODE_PATH only covers CJS, which is why forked ESM addon
@@ -319,8 +327,13 @@ async function launch() {
319
327
  // would never get installed into data/addons, aborting boot with
320
328
  // "No addon provides required infrastructure capability storage-provider".
321
329
  // The server's own package.json is always readable next to this launcher.
322
- const bootstrapRequired = readBootstrapRequiredAddons(dataDir, bootstrapSchema) ?? deriveBootstrapFromSelf();
323
- console.log(`[launcher] bootstrap: ${bootstrapRequired.length} required package(s)`);
330
+ // Role-aware default bootstrap set: the hub installs every addon-* dep of
331
+ // its own package.json; the agent installs ONLY the agent bootstrap subset
332
+ // (AGENT_PACKAGES, derived from `camstack.agentBootstrapAddons`). A
333
+ // config.yaml `bootstrap.requiredAddons` override still wins for both roles.
334
+ const roleDefaultBootstrap = role === 'agent' ? AddonInstaller.AGENT_PACKAGES : deriveBootstrapFromSelf();
335
+ const bootstrapRequired = readBootstrapRequiredAddons(dataDir, bootstrapSchema) ?? roleDefaultBootstrap;
336
+ console.log(`[launcher] bootstrap (${role}): ${bootstrapRequired.length} required package(s)`);
324
337
  await installer.ensureRequiredPackages(bootstrapRequired);
325
338
  // Reconcile the install manifest with what is on disk. Addons baked
326
339
  // into the image are copied straight into addonsDir by the container
@@ -382,9 +395,19 @@ async function launch() {
382
395
  Module._initPaths();
383
396
  console.log(`[launcher] NODE_PATH extended with: ${extraNodePaths.join(sep)}`);
384
397
  }
385
- // Now safe to load main.ts (which has static imports from @camstack/system)
386
- console.log('[launcher] Starting server...');
387
- await Promise.resolve().then(() => __importStar(require('./main')));
398
+ // Now safe to load the role's runtime entry (both have static imports from
399
+ // @camstack/system, resolved only after the framework dir + NODE_PATH setup
400
+ // above). The agent boots from THIS same @camstack/server closure — there is
401
+ // no separate agent root package.
402
+ if (role === 'agent') {
403
+ console.log('[launcher] Starting agent runtime...');
404
+ const { startAgent } = await Promise.resolve().then(() => __importStar(require('./agent/main.js')));
405
+ await startAgent();
406
+ }
407
+ else {
408
+ console.log('[launcher] Starting server...');
409
+ await Promise.resolve().then(() => __importStar(require('./main')));
410
+ }
388
411
  }
389
412
  // Signal handlers — forward to default exit so NestJS shutdown hooks
390
413
  // (Moleculer broker stopped() → kills spawned workers) run cleanly.
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveNodeRole = resolveNodeRole;
4
+ function resolveNodeRole(env) {
5
+ const raw = (env['CAMSTACK_ROLE'] ?? '').trim().toLowerCase();
6
+ if (raw === '' || raw === 'hub')
7
+ return 'hub';
8
+ if (raw === 'agent')
9
+ return 'agent';
10
+ throw new Error(`Invalid CAMSTACK_ROLE "${raw}" — expected "hub" or "agent"`);
11
+ }
@@ -73,7 +73,6 @@ var require_dist = __commonJS({
73
73
  }), mod), "__toCommonJS");
74
74
  var src_exports = {};
75
75
  __export2(src_exports, {
76
- AGENT_ROOT_SPEC: /* @__PURE__ */ __name(() => AGENT_ROOT_SPEC, "AGENT_ROOT_SPEC"),
77
76
  CURRENT_DIRNAME: /* @__PURE__ */ __name(() => CURRENT_DIRNAME, "CURRENT_DIRNAME"),
78
77
  DEV_UPLOADS_DIRNAME: /* @__PURE__ */ __name(() => DEV_UPLOADS_DIRNAME, "DEV_UPLOADS_DIRNAME"),
79
78
  DEV_UPLOADS_KEEP_COUNT: /* @__PURE__ */ __name(() => DEV_UPLOADS_KEEP_COUNT, "DEV_UPLOADS_KEEP_COUNT"),
@@ -193,13 +192,6 @@ var require_dist = __commonJS({
193
192
  "launcher.js"
194
193
  ]
195
194
  };
196
- var AGENT_ROOT_SPEC = {
197
- packageName: "@camstack/agent",
198
- entryRelPath: [
199
- "dist",
200
- "cli.js"
201
- ]
202
- };
203
195
  var fs2 = __toESM2(require("fs"));
204
196
  var path2 = __toESM2(require("path"));
205
197
  var SERVER_ROOT_DIRNAME = "server-root";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/server",
3
- "version": "1.1.75",
3
+ "version": "1.2.0",
4
4
  "private": false,
5
5
  "files": [
6
6
  "dist",
@@ -22,22 +22,34 @@
22
22
  "test": "vitest run --reporter verbose",
23
23
  "test:watch": "vitest"
24
24
  },
25
+ "camstack": {
26
+ "agentBootstrapAddons": [
27
+ "@camstack/addon-pipeline",
28
+ "@camstack/addon-decoder-nodeav",
29
+ "@camstack/addon-agent-ui"
30
+ ],
31
+ "agentOnlyBootstrapAddons": [
32
+ "@camstack/addon-agent-ui"
33
+ ]
34
+ },
25
35
  "dependencies": {
26
- "@camstack/addon-admin-ui": "1.1.62",
27
- "@camstack/addon-advanced-notifier": "1.1.30",
28
- "@camstack/addon-auth": "1.1.15",
29
- "@camstack/addon-decoder-nodeav": "1.1.18",
30
- "@camstack/addon-notifiers": "1.1.30",
31
- "@camstack/addon-pipeline": "1.1.70",
32
- "@camstack/addon-pipeline-orchestrator": "1.1.59",
33
- "@camstack/addon-post-analysis": "1.1.41",
34
- "@camstack/sdk": "1.1.30",
35
- "@camstack/shm-ring": "1.0.29",
36
- "@camstack/system": "1.1.63",
37
- "@camstack/types": "1.1.57",
38
- "@camstack/ui-library": "1.1.45",
36
+ "@camstack/addon-admin-ui": "1.2.0",
37
+ "@camstack/addon-advanced-notifier": "1.2.0",
38
+ "@camstack/addon-agent-ui": "1.2.0",
39
+ "@camstack/addon-auth": "1.2.0",
40
+ "@camstack/addon-decoder-nodeav": "1.2.0",
41
+ "@camstack/addon-notifiers": "1.2.0",
42
+ "@camstack/addon-pipeline": "1.2.0",
43
+ "@camstack/addon-pipeline-orchestrator": "1.2.0",
44
+ "@camstack/addon-post-analysis": "1.2.0",
45
+ "@camstack/sdk": "1.2.0",
46
+ "@camstack/shm-ring": "1.1.0",
47
+ "@camstack/system": "1.2.0",
48
+ "@camstack/types": "1.2.0",
49
+ "@camstack/ui-library": "1.2.0",
39
50
  "@fastify/compress": "^9.0.0",
40
51
  "@fastify/cookie": "^11.0.2",
52
+ "@fastify/cors": "^11.2.0",
41
53
  "@fastify/multipart": "^10.0.0",
42
54
  "@fastify/static": "^9.1.3",
43
55
  "@trpc/client": "^11.16.0",
@@ -47,6 +59,7 @@
47
59
  "moleculer": "^0.15.0",
48
60
  "superjson": "^2.2.6",
49
61
  "tar": "7.5.16",
62
+ "undici": "^7.28.0",
50
63
  "ws": "^8.20.0",
51
64
  "zod": "^4.3.6"
52
65
  },