@camstack/agent 1.1.0 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.mjs CHANGED
@@ -2,10 +2,13 @@
2
2
  import { createRequire as __cr } from 'node:module'; const require = globalThis.require ?? __cr(import.meta.url);
3
3
  import {
4
4
  startAgent
5
- } from "./chunk-PFQZTXGW.mjs";
5
+ } from "./chunk-EMAD7O4T.mjs";
6
6
 
7
7
  // src/cli.ts
8
+ import { existsSync } from "fs";
9
+ import { Module } from "module";
8
10
  import * as os from "os";
11
+ import * as path from "path";
9
12
  function parseArgs(argv) {
10
13
  const args2 = {};
11
14
  for (let i = 2; i < argv.length; i++) {
@@ -114,6 +117,28 @@ if (args.secret && !process.env["CAMSTACK_CLUSTER_SECRET"]) {
114
117
  if (args.statusPort && !process.env["CAMSTACK_STATUS_PORT"]) {
115
118
  process.env["CAMSTACK_STATUS_PORT"] = args.statusPort;
116
119
  }
120
+ if (!process.env["CAMSTACK_FRAMEWORK_DIR"]) {
121
+ const scriptPath = process.argv[1];
122
+ if (scriptPath) {
123
+ const frameworkDir = path.resolve(path.dirname(scriptPath), "..", "..");
124
+ const frameworkModules = path.join(frameworkDir, "node_modules");
125
+ if (existsSync(path.join(frameworkModules, "@camstack", "shm-ring"))) {
126
+ process.env["CAMSTACK_FRAMEWORK_DIR"] = frameworkDir;
127
+ const existingNodePath = process.env["NODE_PATH"];
128
+ process.env["NODE_PATH"] = existingNodePath ? `${frameworkModules}${path.delimiter}${existingNodePath}` : frameworkModules;
129
+ Module._initPaths();
130
+ console.log(
131
+ `[Agent] Self-contained framework detected \u2014 CAMSTACK_FRAMEWORK_DIR=${frameworkDir}`
132
+ );
133
+ if (!process.env["CAMSTACK_BUNDLED_ADDONS_DIR"]) {
134
+ const bundledAddons = path.join(frameworkDir, "addons");
135
+ if (existsSync(bundledAddons)) {
136
+ process.env["CAMSTACK_BUNDLED_ADDONS_DIR"] = bundledAddons;
137
+ }
138
+ }
139
+ }
140
+ }
141
+ }
117
142
  if (!process.env["CAMSTACK_NODE_ID"]) {
118
143
  process.env["CAMSTACK_NODE_ID"] = os.hostname();
119
144
  }
package/dist/cli.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * CamStack Agent CLI — starts a remote agent node that connects to a hub.\n *\n * Usage:\n * camstack-agent --hub 192.168.1.100\n * camstack-agent --hub 192.168.1.100 --name gpu-box --data ./agent-data\n * CAMSTACK_HUB_ADDRESS=192.168.1.100 camstack-agent\n */\nimport * as os from 'node:os'\nimport { startAgent } from './agent-bootstrap.js'\n\ninterface CliArgs {\n hub?: string\n name?: string\n data?: string\n logLevel?: string\n secret?: string\n statusPort?: string\n help?: boolean\n}\n\nfunction parseArgs(argv: readonly string[]): CliArgs {\n const args: CliArgs = {}\n for (let i = 2; i < argv.length; i++) {\n const arg = argv[i]!\n const next = argv[i + 1]\n switch (arg) {\n case '--hub':\n case '-h':\n if (arg === '-h' && !next?.match(/^[\\d.]+/)) {\n args.help = true\n break\n }\n args.hub = next\n i++\n break\n case '--name':\n case '-n':\n args.name = next\n i++\n break\n case '--data':\n case '-d':\n args.data = next\n i++\n break\n case '--log-level':\n case '-l':\n args.logLevel = next\n i++\n break\n case '--secret':\n case '-s':\n args.secret = next\n i++\n break\n case '--status-port':\n case '-p':\n args.statusPort = next\n i++\n break\n case '--help':\n args.help = true\n break\n default:\n if (!arg.startsWith('-') && !args.hub) {\n args.hub = arg\n break\n }\n console.error(`Unknown argument: ${arg}`)\n args.help = true\n }\n }\n return args\n}\n\nfunction printHelp(): void {\n console.log(\n `\ncamstack-agent — CamStack remote agent node\n\nUsage:\n camstack-agent --hub <hub-address> [options]\n camstack-agent <hub-address> [options]\n\nOptions:\n --hub, -h <address> Hub address (IP or hostname, e.g. 192.168.1.100)\n --name, -n <name> Node name (default: hostname)\n --data, -d <path> Data directory (default: ./camstack-data)\n --log-level, -l <level> Log level: trace|debug|info|warn|error (default: info)\n --secret, -s <secret> Cluster secret (nodes must share the same secret)\n --help Show this help\n\nEnvironment variables (override CLI args):\n CAMSTACK_NODE_ID Node identifier\n CAMSTACK_HUB_ADDRESS Hub address\n CAMSTACK_DATA_DIR Data directory path\n CAMSTACK_LOG_LEVEL Log level\n CAMSTACK_CLUSTER_SECRET Cluster secret\n\nExamples:\n camstack-agent --hub 192.168.1.100\n camstack-agent --hub 192.168.1.100 --name gpu-box --log-level debug\n CAMSTACK_HUB_ADDRESS=192.168.1.100 camstack-agent\n`.trim(),\n )\n}\n\nconst args = parseArgs(process.argv)\n\nif (args.help) {\n printHelp()\n process.exit(0)\n}\n\n// CLI args → env vars (env vars set externally take precedence via agent-config.ts)\nif (args.hub && !process.env['CAMSTACK_HUB_ADDRESS']) {\n process.env['CAMSTACK_HUB_ADDRESS'] = args.hub\n}\nif (args.name && !process.env['CAMSTACK_NODE_ID']) {\n process.env['CAMSTACK_NODE_ID'] = args.name\n}\nif (args.data && !process.env['CAMSTACK_DATA_DIR']) {\n process.env['CAMSTACK_DATA_DIR'] = args.data\n}\nif (args.logLevel && !process.env['CAMSTACK_LOG_LEVEL']) {\n process.env['CAMSTACK_LOG_LEVEL'] = args.logLevel\n}\nif (args.secret && !process.env['CAMSTACK_CLUSTER_SECRET']) {\n process.env['CAMSTACK_CLUSTER_SECRET'] = args.secret\n}\nif (args.statusPort && !process.env['CAMSTACK_STATUS_PORT']) {\n process.env['CAMSTACK_STATUS_PORT'] = args.statusPort\n}\n\n// Default node name to hostname if not set\nif (!process.env['CAMSTACK_NODE_ID']) {\n process.env['CAMSTACK_NODE_ID'] = os.hostname()\n}\n\n// Hub address is optional — agent starts in discovery mode without it.\n// The agent UI on :4444 lets the user configure the hub connection.\nif (!process.env['CAMSTACK_HUB_ADDRESS']) {\n console.log('[Agent] No hub address specified — starting in discovery mode')\n console.log('[Agent] Configure hub connection at http://localhost:4444')\n}\n\nstartAgent().catch((err: unknown) => {\n console.error('[Agent] Fatal error:', err)\n process.exit(1)\n})\n"],"mappings":";;;;;;;AASA,YAAY,QAAQ;AAapB,SAAS,UAAU,MAAkC;AACnD,QAAMA,QAAgB,CAAC;AACvB,WAAS,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;AACpC,UAAM,MAAM,KAAK,CAAC;AAClB,UAAM,OAAO,KAAK,IAAI,CAAC;AACvB,YAAQ,KAAK;AAAA,MACX,KAAK;AAAA,MACL,KAAK;AACH,YAAI,QAAQ,QAAQ,CAAC,MAAM,MAAM,SAAS,GAAG;AAC3C,UAAAA,MAAK,OAAO;AACZ;AAAA,QACF;AACA,QAAAA,MAAK,MAAM;AACX;AACA;AAAA,MACF,KAAK;AAAA,MACL,KAAK;AACH,QAAAA,MAAK,OAAO;AACZ;AACA;AAAA,MACF,KAAK;AAAA,MACL,KAAK;AACH,QAAAA,MAAK,OAAO;AACZ;AACA;AAAA,MACF,KAAK;AAAA,MACL,KAAK;AACH,QAAAA,MAAK,WAAW;AAChB;AACA;AAAA,MACF,KAAK;AAAA,MACL,KAAK;AACH,QAAAA,MAAK,SAAS;AACd;AACA;AAAA,MACF,KAAK;AAAA,MACL,KAAK;AACH,QAAAA,MAAK,aAAa;AAClB;AACA;AAAA,MACF,KAAK;AACH,QAAAA,MAAK,OAAO;AACZ;AAAA,MACF;AACE,YAAI,CAAC,IAAI,WAAW,GAAG,KAAK,CAACA,MAAK,KAAK;AACrC,UAAAA,MAAK,MAAM;AACX;AAAA,QACF;AACA,gBAAQ,MAAM,qBAAqB,GAAG,EAAE;AACxC,QAAAA,MAAK,OAAO;AAAA,IAChB;AAAA,EACF;AACA,SAAOA;AACT;AAEA,SAAS,YAAkB;AACzB,UAAQ;AAAA,IACN;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EA0BF,KAAK;AAAA,EACL;AACF;AAEA,IAAM,OAAO,UAAU,QAAQ,IAAI;AAEnC,IAAI,KAAK,MAAM;AACb,YAAU;AACV,UAAQ,KAAK,CAAC;AAChB;AAGA,IAAI,KAAK,OAAO,CAAC,QAAQ,IAAI,sBAAsB,GAAG;AACpD,UAAQ,IAAI,sBAAsB,IAAI,KAAK;AAC7C;AACA,IAAI,KAAK,QAAQ,CAAC,QAAQ,IAAI,kBAAkB,GAAG;AACjD,UAAQ,IAAI,kBAAkB,IAAI,KAAK;AACzC;AACA,IAAI,KAAK,QAAQ,CAAC,QAAQ,IAAI,mBAAmB,GAAG;AAClD,UAAQ,IAAI,mBAAmB,IAAI,KAAK;AAC1C;AACA,IAAI,KAAK,YAAY,CAAC,QAAQ,IAAI,oBAAoB,GAAG;AACvD,UAAQ,IAAI,oBAAoB,IAAI,KAAK;AAC3C;AACA,IAAI,KAAK,UAAU,CAAC,QAAQ,IAAI,yBAAyB,GAAG;AAC1D,UAAQ,IAAI,yBAAyB,IAAI,KAAK;AAChD;AACA,IAAI,KAAK,cAAc,CAAC,QAAQ,IAAI,sBAAsB,GAAG;AAC3D,UAAQ,IAAI,sBAAsB,IAAI,KAAK;AAC7C;AAGA,IAAI,CAAC,QAAQ,IAAI,kBAAkB,GAAG;AACpC,UAAQ,IAAI,kBAAkB,IAAO,YAAS;AAChD;AAIA,IAAI,CAAC,QAAQ,IAAI,sBAAsB,GAAG;AACxC,UAAQ,IAAI,oEAA+D;AAC3E,UAAQ,IAAI,2DAA2D;AACzE;AAEA,WAAW,EAAE,MAAM,CAAC,QAAiB;AACnC,UAAQ,MAAM,wBAAwB,GAAG;AACzC,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["args"]}
1
+ {"version":3,"sources":["../src/cli.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * CamStack Agent CLI — starts a remote agent node that connects to a hub.\n *\n * Usage:\n * camstack-agent --hub 192.168.1.100\n * camstack-agent --hub 192.168.1.100 --name gpu-box --data ./agent-data\n * CAMSTACK_HUB_ADDRESS=192.168.1.100 camstack-agent\n */\nimport { existsSync } from 'node:fs'\nimport { Module } from 'node:module'\nimport * as os from 'node:os'\nimport * as path from 'node:path'\nimport { startAgent } from './agent-bootstrap.js'\n\n// `Module._initPaths()` re-derives the CJS search paths from a NODE_PATH set\n// after process start. It's a stable Node internal backing NODE_PATH support\n// but is absent from @types/node — declare it via merging (no cast).\ndeclare module 'node:module' {\n // eslint-disable-next-line @typescript-eslint/no-namespace\n namespace Module {\n function _initPaths(): void\n }\n}\n\ninterface CliArgs {\n hub?: string\n name?: string\n data?: string\n logLevel?: string\n secret?: string\n statusPort?: string\n help?: boolean\n}\n\nfunction parseArgs(argv: readonly string[]): CliArgs {\n const args: CliArgs = {}\n for (let i = 2; i < argv.length; i++) {\n const arg = argv[i]!\n const next = argv[i + 1]\n switch (arg) {\n case '--hub':\n case '-h':\n if (arg === '-h' && !next?.match(/^[\\d.]+/)) {\n args.help = true\n break\n }\n args.hub = next\n i++\n break\n case '--name':\n case '-n':\n args.name = next\n i++\n break\n case '--data':\n case '-d':\n args.data = next\n i++\n break\n case '--log-level':\n case '-l':\n args.logLevel = next\n i++\n break\n case '--secret':\n case '-s':\n args.secret = next\n i++\n break\n case '--status-port':\n case '-p':\n args.statusPort = next\n i++\n break\n case '--help':\n args.help = true\n break\n default:\n if (!arg.startsWith('-') && !args.hub) {\n args.hub = arg\n break\n }\n console.error(`Unknown argument: ${arg}`)\n args.help = true\n }\n }\n return args\n}\n\nfunction printHelp(): void {\n console.log(\n `\ncamstack-agent — CamStack remote agent node\n\nUsage:\n camstack-agent --hub <hub-address> [options]\n camstack-agent <hub-address> [options]\n\nOptions:\n --hub, -h <address> Hub address (IP or hostname, e.g. 192.168.1.100)\n --name, -n <name> Node name (default: hostname)\n --data, -d <path> Data directory (default: ./camstack-data)\n --log-level, -l <level> Log level: trace|debug|info|warn|error (default: info)\n --secret, -s <secret> Cluster secret (nodes must share the same secret)\n --help Show this help\n\nEnvironment variables (override CLI args):\n CAMSTACK_NODE_ID Node identifier\n CAMSTACK_HUB_ADDRESS Hub address\n CAMSTACK_DATA_DIR Data directory path\n CAMSTACK_LOG_LEVEL Log level\n CAMSTACK_CLUSTER_SECRET Cluster secret\n\nExamples:\n camstack-agent --hub 192.168.1.100\n camstack-agent --hub 192.168.1.100 --name gpu-box --log-level debug\n CAMSTACK_HUB_ADDRESS=192.168.1.100 camstack-agent\n`.trim(),\n )\n}\n\nconst args = parseArgs(process.argv)\n\nif (args.help) {\n printHelp()\n process.exit(0)\n}\n\n// CLI args → env vars (env vars set externally take precedence via agent-config.ts)\nif (args.hub && !process.env['CAMSTACK_HUB_ADDRESS']) {\n process.env['CAMSTACK_HUB_ADDRESS'] = args.hub\n}\nif (args.name && !process.env['CAMSTACK_NODE_ID']) {\n process.env['CAMSTACK_NODE_ID'] = args.name\n}\nif (args.data && !process.env['CAMSTACK_DATA_DIR']) {\n process.env['CAMSTACK_DATA_DIR'] = args.data\n}\nif (args.logLevel && !process.env['CAMSTACK_LOG_LEVEL']) {\n process.env['CAMSTACK_LOG_LEVEL'] = args.logLevel\n}\nif (args.secret && !process.env['CAMSTACK_CLUSTER_SECRET']) {\n process.env['CAMSTACK_CLUSTER_SECRET'] = args.secret\n}\nif (args.statusPort && !process.env['CAMSTACK_STATUS_PORT']) {\n process.env['CAMSTACK_STATUS_PORT'] = args.statusPort\n}\n\n// Packaged-app framework resolution. In a self-contained build (e.g. the\n// Electron `.app`, where this CLI lives at `<root>/agent/dist/cli.js` and the\n// host-provided package closure at `<root>/node_modules`), addons installed\n// under the data dir cannot walk up to the framework's node_modules. Mirror the\n// Docker image's setup: point the addon-runner's resolver hook at the framework\n// (CAMSTACK_FRAMEWORK_DIR, for ESM imports) AND seed NODE_PATH (for CJS\n// `require`) so host externals (@camstack/shm-ring, sharp, node-av, …) resolve.\n// No-op in dev (workspace symlinks resolve the framework) and when an operator\n// or the Docker image has already set CAMSTACK_FRAMEWORK_DIR.\nif (!process.env['CAMSTACK_FRAMEWORK_DIR']) {\n const scriptPath = process.argv[1]\n if (scriptPath) {\n const frameworkDir = path.resolve(path.dirname(scriptPath), '..', '..')\n const frameworkModules = path.join(frameworkDir, 'node_modules')\n // Sentinel: a host-provided native package present in the framework's\n // node_modules confirms the self-contained layout (absent in dev).\n if (existsSync(path.join(frameworkModules, '@camstack', 'shm-ring'))) {\n process.env['CAMSTACK_FRAMEWORK_DIR'] = frameworkDir\n const existingNodePath = process.env['NODE_PATH']\n process.env['NODE_PATH'] = existingNodePath\n ? `${frameworkModules}${path.delimiter}${existingNodePath}`\n : frameworkModules\n // Node reads NODE_PATH once at startup into the CJS module search paths;\n // setting it now (post-start) only reaches CHILD processes that inherit\n // it at spawn (the forked addon-runners). The agent's OWN main process —\n // where bootCoreAddons imports the in-process infra builtins (storage /\n // settings / metrics from @camstack/system) — must re-derive its search\n // paths or those addons' host-dep requires (@camstack/types, …) fail with\n // \"Cannot find module\" and the agent boots with NO infrastructure.\n Module._initPaths()\n console.log(\n `[Agent] Self-contained framework detected — CAMSTACK_FRAMEWORK_DIR=${frameworkDir}`,\n )\n // Also point the bootstrap installer at the bundled addons so a fresh\n // (empty) data dir gets seeded with the agent's required packages\n // (@camstack/system — platform-probe/storage/settings/metrics builtins —\n // plus the bundled addons). Without this, bootCoreAddons finds no addon\n // packages and the agent runs with NO infrastructure (no platform-probe →\n // detection engine can't auto-pick the node's accelerator, no storage,\n // etc.). Mirrors the Docker image's CAMSTACK_BUNDLED_ADDONS_DIR.\n if (!process.env['CAMSTACK_BUNDLED_ADDONS_DIR']) {\n const bundledAddons = path.join(frameworkDir, 'addons')\n if (existsSync(bundledAddons)) {\n process.env['CAMSTACK_BUNDLED_ADDONS_DIR'] = bundledAddons\n }\n }\n }\n }\n}\n\n// Default node name to hostname if not set\nif (!process.env['CAMSTACK_NODE_ID']) {\n process.env['CAMSTACK_NODE_ID'] = os.hostname()\n}\n\n// Hub address is optional — agent starts in discovery mode without it.\n// The agent UI on :4444 lets the user configure the hub connection.\nif (!process.env['CAMSTACK_HUB_ADDRESS']) {\n console.log('[Agent] No hub address specified — starting in discovery mode')\n console.log('[Agent] Configure hub connection at http://localhost:4444')\n}\n\nstartAgent().catch((err: unknown) => {\n console.error('[Agent] Fatal error:', err)\n process.exit(1)\n})\n"],"mappings":"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","names":["args"]}
package/dist/index.js CHANGED
@@ -180,7 +180,7 @@ async function applyDeployedBundle(input) {
180
180
  var import_node_crypto2 = require("crypto");
181
181
  var import_undici = require("undici");
182
182
  async function fetchBundleFromHub(opts) {
183
- const authHeader = { Authorization: `Bearer ${opts.token}` };
183
+ const authHeader = { Authorization: `Bearer ${opts.token}`, "accept-encoding": "identity" };
184
184
  const res = opts.fetchImpl ? await opts.fetchImpl(opts.url, { headers: authHeader }) : await (0, import_undici.fetch)(opts.url, {
185
185
  headers: authHeader,
186
186
  dispatcher: new import_undici.Agent({ connect: { rejectUnauthorized: false } })
@@ -199,6 +199,14 @@ async function fetchBundleFromHub(opts) {
199
199
  return buffer;
200
200
  }
201
201
 
202
+ // src/apply-model-distribution.ts
203
+ async function applyModelDistribution(seam, params) {
204
+ seam.mkdirp(seam.modelsDir);
205
+ const buffer = await seam.fetchBundle(params.source);
206
+ await seam.extract(buffer, seam.modelsDir);
207
+ return { success: true, modelId: params.modelId, format: params.format, path: seam.modelsDir };
208
+ }
209
+
202
210
  // src/agent-service.ts
203
211
  var deploySwapLogger = {
204
212
  info: (msg) => console.log(`[Agent] ${msg}`),
@@ -477,6 +485,43 @@ function createAgentService(deps) {
477
485
  return resolveDeployAction(seam)({ addonId, source, bundle });
478
486
  }
479
487
  },
488
+ /**
489
+ * Pull a staged model tarball from the hub and untar it into this node's
490
+ * `<dataDir>/models` (Model Studio P2). Reuses the agent-pull machinery:
491
+ * `fetchBundleFromHub` verifies bytes + sha256 before extraction. The
492
+ * existing `isModelDownloaded` then reports the model present, so the
493
+ * detection-pipeline can load it locally.
494
+ */
495
+ distributeModel: {
496
+ handler: async (ctx) => {
497
+ const { params } = ctx;
498
+ const { execFile } = await import("child_process");
499
+ const { promisify } = await import("util");
500
+ const execFileAsync = promisify(execFile);
501
+ const modelsDir = path3.join(deps.dataDir, "models");
502
+ const seam = {
503
+ modelsDir,
504
+ fetchBundle: (s) => fetchBundleFromHub(s),
505
+ extract: async (tgz, destDir) => {
506
+ const tmp = path3.join(
507
+ destDir,
508
+ `.dist-${Date.now()}-${Math.random().toString(36).slice(2)}.tgz`
509
+ );
510
+ fs3.writeFileSync(tmp, tgz);
511
+ try {
512
+ await execFileAsync("tar", ["-xzf", tmp, "-C", destDir], { timeout: 6e4 });
513
+ } finally {
514
+ try {
515
+ fs3.unlinkSync(tmp);
516
+ } catch {
517
+ }
518
+ }
519
+ },
520
+ mkdirp: (dir) => fs3.mkdirSync(dir, { recursive: true })
521
+ };
522
+ return applyModelDistribution(seam, params);
523
+ }
524
+ },
480
525
  /**
481
526
  * Genuinely drop a deployed addon from this agent.
482
527
  *