@camstack/agent 1.1.18 → 1.1.20
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/{chunk-XEHYJVPH.mjs → chunk-BWAPSCEC.mjs} +143 -20
- package/dist/chunk-BWAPSCEC.mjs.map +1 -0
- package/dist/cli.js +145 -19
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +4 -1
- package/dist/cli.mjs.map +1 -1
- package/dist/index.js +142 -19
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/dist/chunk-XEHYJVPH.mjs.map +0 -1
package/dist/cli.mjs
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
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-
|
|
5
|
+
} from "./chunk-BWAPSCEC.mjs";
|
|
6
6
|
|
|
7
7
|
// src/cli.ts
|
|
8
8
|
import { existsSync } from "fs";
|
|
@@ -86,6 +86,9 @@ Environment variables (override CLI args):
|
|
|
86
86
|
CAMSTACK_DATA_DIR Data directory path
|
|
87
87
|
CAMSTACK_LOG_LEVEL Log level
|
|
88
88
|
CAMSTACK_CLUSTER_SECRET Cluster secret
|
|
89
|
+
CAMSTACK_HUB_URL Derived automatically from the hub address; set
|
|
90
|
+
only to override the cross-node stream/recording
|
|
91
|
+
pull host
|
|
89
92
|
|
|
90
93
|
Examples:
|
|
91
94
|
camstack-agent --hub 192.168.1.100
|
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 { existsSync } from 'node:fs'\nimport { Module as nodeModule } 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 nodeModule._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"]}
|
|
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 as nodeModule } 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 CAMSTACK_HUB_URL Derived automatically from the hub address; set\n only to override the cross-node stream/recording\n pull host\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 nodeModule._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":";;;;;;;AASA,SAAS,kBAAkB;AAC3B,SAAS,UAAU,kBAAkB;AACrC,YAAY,QAAQ;AACpB,YAAY,UAAU;AAuBtB,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;AAAA;AAAA;AAAA,EA6BF,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;AAWA,IAAI,CAAC,QAAQ,IAAI,wBAAwB,GAAG;AAC1C,QAAM,aAAa,QAAQ,KAAK,CAAC;AACjC,MAAI,YAAY;AACd,UAAM,eAAoB,aAAa,aAAQ,UAAU,GAAG,MAAM,IAAI;AACtE,UAAM,mBAAwB,UAAK,cAAc,cAAc;AAG/D,QAAI,WAAgB,UAAK,kBAAkB,aAAa,UAAU,CAAC,GAAG;AACpE,cAAQ,IAAI,wBAAwB,IAAI;AACxC,YAAM,mBAAmB,QAAQ,IAAI,WAAW;AAChD,cAAQ,IAAI,WAAW,IAAI,mBACvB,GAAG,gBAAgB,GAAQ,cAAS,GAAG,gBAAgB,KACvD;AAQJ,iBAAW,WAAW;AACtB,cAAQ;AAAA,QACN,2EAAsE,YAAY;AAAA,MACpF;AAQA,UAAI,CAAC,QAAQ,IAAI,6BAA6B,GAAG;AAC/C,cAAM,gBAAqB,UAAK,cAAc,QAAQ;AACtD,YAAI,WAAW,aAAa,GAAG;AAC7B,kBAAQ,IAAI,6BAA6B,IAAI;AAAA,QAC/C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;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"]}
|
package/dist/index.js
CHANGED
|
@@ -643,17 +643,108 @@ var path6 = __toESM(require("path"));
|
|
|
643
643
|
var fs4 = __toESM(require("fs"));
|
|
644
644
|
var path4 = __toESM(require("path"));
|
|
645
645
|
var import_fastify = __toESM(require("fastify"));
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
646
|
+
|
|
647
|
+
// src/derive-hub-url.ts
|
|
648
|
+
var HUB_API_PORT = 4443;
|
|
649
|
+
function deriveHubUrlFromHubAddress(hubAddress) {
|
|
650
|
+
const trimmed = hubAddress.trim();
|
|
651
|
+
if (trimmed.length === 0) return void 0;
|
|
652
|
+
const afterAt = trimmed.includes("@") ? trimmed.split("@")[1] ?? "" : trimmed;
|
|
653
|
+
const authority = afterAt.split("/")[0] ?? "";
|
|
654
|
+
const host = extractHostFromAuthority(authority);
|
|
655
|
+
if (host === void 0) return void 0;
|
|
656
|
+
return `https://${host}:${HUB_API_PORT}`;
|
|
657
|
+
}
|
|
658
|
+
function deriveHubUrlForExport(hubUrlWasExplicit, hubAddress) {
|
|
659
|
+
if (hubUrlWasExplicit) return void 0;
|
|
660
|
+
if (hubAddress === void 0) return void 0;
|
|
661
|
+
return deriveHubUrlFromHubAddress(hubAddress);
|
|
662
|
+
}
|
|
663
|
+
function extractHostFromAuthority(authority) {
|
|
664
|
+
const value = authority.trim();
|
|
665
|
+
if (value.length === 0) return void 0;
|
|
666
|
+
if (value.startsWith("[")) {
|
|
667
|
+
const end = value.indexOf("]");
|
|
668
|
+
if (end > 0) return value.slice(0, end + 1);
|
|
669
|
+
return void 0;
|
|
670
|
+
}
|
|
671
|
+
const host = value.split(":")[0] ?? "";
|
|
672
|
+
return host.length > 0 ? host : void 0;
|
|
673
|
+
}
|
|
674
|
+
var HUB_NODE_ID = "hub";
|
|
675
|
+
var IPV4_MAPPED_PREFIX = "::ffff:";
|
|
676
|
+
var IPV4_DOTTED_QUAD = /^\d{1,3}(?:\.\d{1,3}){3}$/;
|
|
677
|
+
function pickIpv4(ipList) {
|
|
678
|
+
if (!ipList) return null;
|
|
679
|
+
for (const ip of ipList) {
|
|
680
|
+
if (ip.includes(":")) continue;
|
|
681
|
+
if (ip.startsWith("127.")) continue;
|
|
682
|
+
return ip;
|
|
654
683
|
}
|
|
655
684
|
return null;
|
|
656
685
|
}
|
|
686
|
+
function normalizeObservedHost(raw) {
|
|
687
|
+
if (raw === void 0 || raw === null) return void 0;
|
|
688
|
+
const trimmed = raw.trim();
|
|
689
|
+
if (trimmed.length === 0) return void 0;
|
|
690
|
+
if (trimmed.startsWith("[")) return trimmed;
|
|
691
|
+
if (trimmed.toLowerCase().startsWith(IPV4_MAPPED_PREFIX)) {
|
|
692
|
+
const mapped = trimmed.slice(IPV4_MAPPED_PREFIX.length);
|
|
693
|
+
if (IPV4_DOTTED_QUAD.test(mapped)) return mapped;
|
|
694
|
+
}
|
|
695
|
+
if (trimmed.includes(":")) return `[${trimmed}]`;
|
|
696
|
+
return trimmed;
|
|
697
|
+
}
|
|
698
|
+
function deriveHubUrlFromRegistry(nodes) {
|
|
699
|
+
const hub = nodes.find((node) => node.id === HUB_NODE_ID);
|
|
700
|
+
if (hub === void 0) return void 0;
|
|
701
|
+
const host = normalizeObservedHost(hub.udpAddress) ?? pickIpv4(hub.ipList) ?? hub.hostname;
|
|
702
|
+
if (host === void 0 || host.length === 0) return void 0;
|
|
703
|
+
return `https://${host}:${HUB_API_PORT}`;
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// src/agent-http.ts
|
|
707
|
+
function compareVersions(a, b) {
|
|
708
|
+
if (a === null && b === null) return 0;
|
|
709
|
+
if (a === null) return -1;
|
|
710
|
+
if (b === null) return 1;
|
|
711
|
+
const pa = a.split(".").map((s) => Number.parseInt(s, 10) || 0);
|
|
712
|
+
const pb = b.split(".").map((s) => Number.parseInt(s, 10) || 0);
|
|
713
|
+
const len = Math.max(pa.length, pb.length);
|
|
714
|
+
for (let i = 0; i < len; i++) {
|
|
715
|
+
const diff = (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
716
|
+
if (diff !== 0) return diff;
|
|
717
|
+
}
|
|
718
|
+
return 0;
|
|
719
|
+
}
|
|
720
|
+
function readUiDistCandidate(baseDir) {
|
|
721
|
+
const dir = path4.join(baseDir, "dist");
|
|
722
|
+
if (!fs4.existsSync(path4.join(dir, "index.html"))) return null;
|
|
723
|
+
let version = null;
|
|
724
|
+
try {
|
|
725
|
+
const parsed = JSON.parse(fs4.readFileSync(path4.join(baseDir, "package.json"), "utf-8"));
|
|
726
|
+
version = typeof parsed.version === "string" ? parsed.version : null;
|
|
727
|
+
} catch {
|
|
728
|
+
version = null;
|
|
729
|
+
}
|
|
730
|
+
return { dir, version };
|
|
731
|
+
}
|
|
732
|
+
function pickUiDist(installed, bundled) {
|
|
733
|
+
if (installed && bundled) {
|
|
734
|
+
return compareVersions(bundled.version, installed.version) > 0 ? bundled.dir : installed.dir;
|
|
735
|
+
}
|
|
736
|
+
return installed?.dir ?? bundled?.dir ?? null;
|
|
737
|
+
}
|
|
738
|
+
function resolveUiDistDir(dataDir, bundledAddonsDir, siblingDistDir = path4.resolve(__dirname, "../../addon-agent-ui/dist")) {
|
|
739
|
+
if (fs4.existsSync(path4.join(siblingDistDir, "index.html"))) return siblingDistDir;
|
|
740
|
+
const installed = readUiDistCandidate(path4.join(dataDir, "addons", "@camstack", "addon-agent-ui"));
|
|
741
|
+
const bundled = bundledAddonsDir ? readUiDistCandidate(path4.join(bundledAddonsDir, "addon-agent-ui")) : null;
|
|
742
|
+
const picked = pickUiDist(installed, bundled);
|
|
743
|
+
if (picked) return picked;
|
|
744
|
+
const legacy = path4.join(dataDir, "agent-ui");
|
|
745
|
+
if (fs4.existsSync(path4.join(legacy, "index.html"))) return legacy;
|
|
746
|
+
return null;
|
|
747
|
+
}
|
|
657
748
|
function readConfigFile(configPath) {
|
|
658
749
|
if (!fs4.existsSync(configPath)) return {};
|
|
659
750
|
try {
|
|
@@ -674,15 +765,6 @@ function getRegistryNodes(broker) {
|
|
|
674
765
|
return [];
|
|
675
766
|
}
|
|
676
767
|
}
|
|
677
|
-
function pickIpv4(ipList) {
|
|
678
|
-
if (!ipList) return null;
|
|
679
|
-
for (const ip of ipList) {
|
|
680
|
-
if (ip.includes(":")) continue;
|
|
681
|
-
if (ip.startsWith("127.")) continue;
|
|
682
|
-
return ip;
|
|
683
|
-
}
|
|
684
|
-
return null;
|
|
685
|
-
}
|
|
686
768
|
function resolveHubEndpoint(nodes) {
|
|
687
769
|
const hub = nodes.find((n) => n.id === "hub");
|
|
688
770
|
if (!hub) return null;
|
|
@@ -830,19 +912,26 @@ async function createAgentHttpServer(getBroker, config) {
|
|
|
830
912
|
const nodes = getRegistryNodes(b);
|
|
831
913
|
return mapDiscoveredNodes(nodes, b.nodeID);
|
|
832
914
|
});
|
|
833
|
-
const uiDir = resolveUiDistDir(config.dataDir);
|
|
915
|
+
const uiDir = resolveUiDistDir(config.dataDir, process.env["CAMSTACK_BUNDLED_ADDONS_DIR"]);
|
|
834
916
|
if (uiDir) {
|
|
835
917
|
const fastifyStatic = await import("@fastify/static");
|
|
836
918
|
await app.register(fastifyStatic.default, {
|
|
837
919
|
root: uiDir,
|
|
838
920
|
prefix: "/",
|
|
839
921
|
wildcard: false,
|
|
840
|
-
|
|
922
|
+
// MUST stay true: the SPA-fallback below uses `reply.sendFile`, which
|
|
923
|
+
// only exists when the plugin decorates Reply. With `false` every
|
|
924
|
+
// fallback request 500'd with "reply.sendFile is not a function".
|
|
925
|
+
decorateReply: true
|
|
841
926
|
});
|
|
842
927
|
app.setNotFoundHandler(async (req, reply) => {
|
|
843
928
|
if (req.url.startsWith("/api/") || req.url.startsWith("/health")) {
|
|
844
929
|
return reply.status(404).send({ error: "Not found" });
|
|
845
930
|
}
|
|
931
|
+
if (req.url.startsWith("/assets/")) {
|
|
932
|
+
console.warn(`[Agent] UI asset not found (stale index.html?): ${req.url}`);
|
|
933
|
+
return reply.status(404).send({ error: "Asset not found" });
|
|
934
|
+
}
|
|
846
935
|
return reply.type("text/html").sendFile("index.html");
|
|
847
936
|
});
|
|
848
937
|
console.log(`[Agent] UI served from: ${uiDir}`);
|
|
@@ -1014,6 +1103,14 @@ var import_system4 = require("@camstack/system");
|
|
|
1014
1103
|
var agentLogManager = new import_system4.LogManager(5e3);
|
|
1015
1104
|
async function startAgent(configPath) {
|
|
1016
1105
|
const config = loadAgentConfig(configPath);
|
|
1106
|
+
const hubUrlWasExplicit = process.env["CAMSTACK_HUB_URL"] !== void 0;
|
|
1107
|
+
const derivedHubUrl = deriveHubUrlForExport(hubUrlWasExplicit, config.hubAddress);
|
|
1108
|
+
if (derivedHubUrl !== void 0) {
|
|
1109
|
+
process.env["CAMSTACK_HUB_URL"] = derivedHubUrl;
|
|
1110
|
+
console.log(
|
|
1111
|
+
`[Agent] Derived CAMSTACK_HUB_URL=${derivedHubUrl} from hub address "${config.hubAddress}"`
|
|
1112
|
+
);
|
|
1113
|
+
}
|
|
1017
1114
|
if (config.hubAddress) {
|
|
1018
1115
|
console.log(
|
|
1019
1116
|
`[Agent] Starting node "${config.nodeId}" (name="${config.name}") connecting to ${config.hubAddress}`
|
|
@@ -1060,6 +1157,13 @@ async function startAgent(configPath) {
|
|
|
1060
1157
|
console.log(
|
|
1061
1158
|
`[Agent] New config: hub=${fresh.hubAddress ?? "discovery"}, secret=${fresh.secret ? "yes" : "none"}`
|
|
1062
1159
|
);
|
|
1160
|
+
const freshHubUrl = deriveHubUrlForExport(hubUrlWasExplicit, fresh.hubAddress);
|
|
1161
|
+
if (freshHubUrl !== void 0 && process.env["CAMSTACK_HUB_URL"] !== freshHubUrl) {
|
|
1162
|
+
process.env["CAMSTACK_HUB_URL"] = freshHubUrl;
|
|
1163
|
+
console.log(
|
|
1164
|
+
`[Agent] Derived CAMSTACK_HUB_URL=${freshHubUrl} from hub address "${fresh.hubAddress}"`
|
|
1165
|
+
);
|
|
1166
|
+
}
|
|
1063
1167
|
broker = (0, import_system3.createBroker)({
|
|
1064
1168
|
nodeID: config.nodeId,
|
|
1065
1169
|
mode: "agent",
|
|
@@ -1324,6 +1428,25 @@ async function startAgent(configPath) {
|
|
|
1324
1428
|
(0, import_system3.registerEventBusService)(broker);
|
|
1325
1429
|
broker.createService((0, import_system3.createHwAccelService)((0, import_system3.createKernelHwAccel)()));
|
|
1326
1430
|
await broker.start();
|
|
1431
|
+
let hubUrlFromRegistry;
|
|
1432
|
+
const reconcileHubUrlFromRegistry = () => {
|
|
1433
|
+
if (hubUrlWasExplicit) return;
|
|
1434
|
+
const current = process.env["CAMSTACK_HUB_URL"];
|
|
1435
|
+
if (current !== void 0 && current !== hubUrlFromRegistry) return;
|
|
1436
|
+
const fromRegistry = deriveHubUrlFromRegistry(
|
|
1437
|
+
getRegistryNodes(broker)
|
|
1438
|
+
);
|
|
1439
|
+
if (fromRegistry === void 0 || fromRegistry === current) return;
|
|
1440
|
+
process.env["CAMSTACK_HUB_URL"] = fromRegistry;
|
|
1441
|
+
hubUrlFromRegistry = fromRegistry;
|
|
1442
|
+
console.log(`[Agent] Derived CAMSTACK_HUB_URL=${fromRegistry} from live hub connection`);
|
|
1443
|
+
};
|
|
1444
|
+
reconcileHubUrlFromRegistry();
|
|
1445
|
+
broker.localBus.on("$node.connected", (data) => {
|
|
1446
|
+
const node = data.node;
|
|
1447
|
+
if (node?.id !== "hub") return;
|
|
1448
|
+
reconcileHubUrlFromRegistry();
|
|
1449
|
+
});
|
|
1327
1450
|
let udsEventBridgeDispose = null;
|
|
1328
1451
|
if (agentUdsRegistry !== void 0) {
|
|
1329
1452
|
const agentBrokerEventBus = (0, import_system3.getBrokerEventBus)(broker);
|