@camstack/agent 1.0.8 → 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/{chunk-U6SD2QE6.mjs → chunk-EMAD7O4T.mjs} +132 -16
- package/dist/chunk-EMAD7O4T.mjs.map +1 -0
- package/dist/cli.d.ts +5 -0
- package/dist/cli.js +153 -16
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +26 -1
- package/dist/cli.mjs.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.js +126 -14
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +3 -2
- package/dist/chunk-U6SD2QE6.mjs.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -24,7 +24,10 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
24
24
|
));
|
|
25
25
|
|
|
26
26
|
// src/cli.ts
|
|
27
|
+
var import_node_fs = require("fs");
|
|
28
|
+
var import_node_module = require("module");
|
|
27
29
|
var os3 = __toESM(require("os"));
|
|
30
|
+
var path7 = __toESM(require("path"));
|
|
28
31
|
|
|
29
32
|
// src/agent-bootstrap.ts
|
|
30
33
|
var fs6 = __toESM(require("fs"));
|
|
@@ -369,6 +372,37 @@ async function applyDeployedBundle(input) {
|
|
|
369
372
|
return { addonDir: liveDir };
|
|
370
373
|
}
|
|
371
374
|
|
|
375
|
+
// src/fetch-bundle-from-hub.ts
|
|
376
|
+
var import_node_crypto2 = require("crypto");
|
|
377
|
+
var import_undici = require("undici");
|
|
378
|
+
async function fetchBundleFromHub(opts) {
|
|
379
|
+
const authHeader = { Authorization: `Bearer ${opts.token}`, "accept-encoding": "identity" };
|
|
380
|
+
const res = opts.fetchImpl ? await opts.fetchImpl(opts.url, { headers: authHeader }) : await (0, import_undici.fetch)(opts.url, {
|
|
381
|
+
headers: authHeader,
|
|
382
|
+
dispatcher: new import_undici.Agent({ connect: { rejectUnauthorized: false } })
|
|
383
|
+
});
|
|
384
|
+
if (!res.ok) {
|
|
385
|
+
throw new Error(`bundle fetch failed: HTTP ${res.status}`);
|
|
386
|
+
}
|
|
387
|
+
const buffer = Buffer.from(await res.arrayBuffer());
|
|
388
|
+
if (buffer.length !== opts.bytes) {
|
|
389
|
+
throw new Error(`bundle bytes mismatch: expected ${opts.bytes}, got ${buffer.length}`);
|
|
390
|
+
}
|
|
391
|
+
const sha = (0, import_node_crypto2.createHash)("sha256").update(buffer).digest("hex");
|
|
392
|
+
if (sha !== opts.sha256) {
|
|
393
|
+
throw new Error(`bundle sha256 mismatch: expected ${opts.sha256}, got ${sha}`);
|
|
394
|
+
}
|
|
395
|
+
return buffer;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// src/apply-model-distribution.ts
|
|
399
|
+
async function applyModelDistribution(seam, params) {
|
|
400
|
+
seam.mkdirp(seam.modelsDir);
|
|
401
|
+
const buffer = await seam.fetchBundle(params.source);
|
|
402
|
+
await seam.extract(buffer, seam.modelsDir);
|
|
403
|
+
return { success: true, modelId: params.modelId, format: params.format, path: seam.modelsDir };
|
|
404
|
+
}
|
|
405
|
+
|
|
372
406
|
// src/agent-service.ts
|
|
373
407
|
var deploySwapLogger = {
|
|
374
408
|
info: (msg) => console.log(`[Agent] ${msg}`),
|
|
@@ -392,8 +426,8 @@ function getLocalIps() {
|
|
|
392
426
|
}
|
|
393
427
|
async function withTimeout(p, ms, fallback) {
|
|
394
428
|
let timer;
|
|
395
|
-
const timeout = new Promise((
|
|
396
|
-
timer = setTimeout(() =>
|
|
429
|
+
const timeout = new Promise((resolve6) => {
|
|
430
|
+
timer = setTimeout(() => resolve6(fallback), ms);
|
|
397
431
|
});
|
|
398
432
|
try {
|
|
399
433
|
return await Promise.race([p, timeout]);
|
|
@@ -402,6 +436,28 @@ async function withTimeout(p, ms, fallback) {
|
|
|
402
436
|
}
|
|
403
437
|
}
|
|
404
438
|
var METRICS_SNAPSHOT_TIMEOUT_MS = 2e3;
|
|
439
|
+
function resolveDeployAction(seam) {
|
|
440
|
+
return async (params) => {
|
|
441
|
+
const { addonId, source, bundle } = params;
|
|
442
|
+
if (source && (0, import_system.isAddonDeploySource)(source)) {
|
|
443
|
+
if (source.kind === "npm") {
|
|
444
|
+
await seam.installFromNpm(addonId, source.version);
|
|
445
|
+
return { success: true, addonId };
|
|
446
|
+
}
|
|
447
|
+
const buf2 = await seam.fetchBundle(source);
|
|
448
|
+
const { addonDir: addonDir2 } = await seam.applyBundle(buf2);
|
|
449
|
+
seam.onApplied(addonDir2);
|
|
450
|
+
return { success: true, addonId, path: addonDir2 };
|
|
451
|
+
}
|
|
452
|
+
if (bundle === void 0) {
|
|
453
|
+
throw new Error("$agent.deploy: no source descriptor and no legacy bundle provided");
|
|
454
|
+
}
|
|
455
|
+
const buf = typeof bundle === "string" ? Buffer.from(bundle, "base64") : bundle;
|
|
456
|
+
const { addonDir } = await seam.applyBundle(buf);
|
|
457
|
+
seam.onApplied(addonDir);
|
|
458
|
+
return { success: true, addonId, path: addonDir };
|
|
459
|
+
};
|
|
460
|
+
}
|
|
405
461
|
function readHubAddressFromConfig(configPath) {
|
|
406
462
|
if (!configPath) return null;
|
|
407
463
|
try {
|
|
@@ -574,8 +630,7 @@ function createAgentService(deps) {
|
|
|
574
630
|
deploy: {
|
|
575
631
|
handler: async (ctx) => {
|
|
576
632
|
const { params } = ctx;
|
|
577
|
-
const { addonId, bundle } = params;
|
|
578
|
-
const bufferData = typeof bundle === "string" ? Buffer.from(bundle, "base64") : bundle;
|
|
633
|
+
const { addonId, source, bundle } = params;
|
|
579
634
|
const { execFile } = await import("child_process");
|
|
580
635
|
const { promisify } = await import("util");
|
|
581
636
|
const execFileAsync = promisify(execFile);
|
|
@@ -593,17 +648,74 @@ function createAgentService(deps) {
|
|
|
593
648
|
}
|
|
594
649
|
}
|
|
595
650
|
};
|
|
596
|
-
const
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
651
|
+
const seam = {
|
|
652
|
+
installFromNpm: (pkg, v) => {
|
|
653
|
+
if (!deps.installFromNpm) {
|
|
654
|
+
throw new Error("npm deploy source unsupported on this agent");
|
|
655
|
+
}
|
|
656
|
+
return deps.installFromNpm(pkg, v);
|
|
657
|
+
},
|
|
658
|
+
applyBundle: (buf) => applyDeployedBundle({
|
|
659
|
+
addonsDir: deps.addonsDir,
|
|
660
|
+
addonId,
|
|
661
|
+
bundle: buf,
|
|
662
|
+
extract,
|
|
663
|
+
logger: deploySwapLogger
|
|
664
|
+
}),
|
|
665
|
+
fetchBundle: (s) => fetchBundleFromHub(s),
|
|
666
|
+
// Evict every addon DECLARATION id this package contributes
|
|
667
|
+
// from `loadedAddons` so the follow-up `$agent.reload` actually
|
|
668
|
+
// re-instantiates it. `loadDeployedAddons` skips any addon
|
|
669
|
+
// still present in `loadedAddons`; without this eviction a
|
|
670
|
+
// redeploy would leave the agent pinned to the pre-update
|
|
671
|
+
// version. The `deploy` param `addonId` is the PACKAGE name
|
|
672
|
+
// (used only as the on-disk dir), whereas `loadedAddons` is
|
|
673
|
+
// keyed by the addon DECLARATION id — they differ for scoped
|
|
674
|
+
// packages, so we read the extracted manifest to bridge them.
|
|
675
|
+
onApplied: (addonDir) => {
|
|
676
|
+
for (const declId of readDeployedAddonIds(addonDir)) {
|
|
677
|
+
deps.loadedAddons.delete(declId);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
};
|
|
681
|
+
return resolveDeployAction(seam)({ addonId, source, bundle });
|
|
682
|
+
}
|
|
683
|
+
},
|
|
684
|
+
/**
|
|
685
|
+
* Pull a staged model tarball from the hub and untar it into this node's
|
|
686
|
+
* `<dataDir>/models` (Model Studio P2). Reuses the agent-pull machinery:
|
|
687
|
+
* `fetchBundleFromHub` verifies bytes + sha256 before extraction. The
|
|
688
|
+
* existing `isModelDownloaded` then reports the model present, so the
|
|
689
|
+
* detection-pipeline can load it locally.
|
|
690
|
+
*/
|
|
691
|
+
distributeModel: {
|
|
692
|
+
handler: async (ctx) => {
|
|
693
|
+
const { params } = ctx;
|
|
694
|
+
const { execFile } = await import("child_process");
|
|
695
|
+
const { promisify } = await import("util");
|
|
696
|
+
const execFileAsync = promisify(execFile);
|
|
697
|
+
const modelsDir = path4.join(deps.dataDir, "models");
|
|
698
|
+
const seam = {
|
|
699
|
+
modelsDir,
|
|
700
|
+
fetchBundle: (s) => fetchBundleFromHub(s),
|
|
701
|
+
extract: async (tgz, destDir) => {
|
|
702
|
+
const tmp = path4.join(
|
|
703
|
+
destDir,
|
|
704
|
+
`.dist-${Date.now()}-${Math.random().toString(36).slice(2)}.tgz`
|
|
705
|
+
);
|
|
706
|
+
fs4.writeFileSync(tmp, tgz);
|
|
707
|
+
try {
|
|
708
|
+
await execFileAsync("tar", ["-xzf", tmp, "-C", destDir], { timeout: 6e4 });
|
|
709
|
+
} finally {
|
|
710
|
+
try {
|
|
711
|
+
fs4.unlinkSync(tmp);
|
|
712
|
+
} catch {
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
},
|
|
716
|
+
mkdirp: (dir) => fs4.mkdirSync(dir, { recursive: true })
|
|
717
|
+
};
|
|
718
|
+
return applyModelDistribution(seam, params);
|
|
607
719
|
}
|
|
608
720
|
},
|
|
609
721
|
/**
|
|
@@ -1070,7 +1182,10 @@ async function startAgent(configPath) {
|
|
|
1070
1182
|
subtree.registerNode(params);
|
|
1071
1183
|
triggerUpwardRegistration();
|
|
1072
1184
|
},
|
|
1073
|
-
expectedClusterSecretHash: config.secret ? (0, import_system3.hashClusterSecret)(config.secret) : void 0
|
|
1185
|
+
expectedClusterSecretHash: config.secret ? (0, import_system3.hashClusterSecret)(config.secret) : void 0,
|
|
1186
|
+
installFromNpm: async (pkg, version) => {
|
|
1187
|
+
await installer.install(pkg, version);
|
|
1188
|
+
}
|
|
1074
1189
|
});
|
|
1075
1190
|
broker.createService(agentServiceSchema);
|
|
1076
1191
|
const agentTcpPort = (0, import_system3.deriveAgentListenPort)(broker.nodeID);
|
|
@@ -1649,6 +1764,28 @@ if (args.secret && !process.env["CAMSTACK_CLUSTER_SECRET"]) {
|
|
|
1649
1764
|
if (args.statusPort && !process.env["CAMSTACK_STATUS_PORT"]) {
|
|
1650
1765
|
process.env["CAMSTACK_STATUS_PORT"] = args.statusPort;
|
|
1651
1766
|
}
|
|
1767
|
+
if (!process.env["CAMSTACK_FRAMEWORK_DIR"]) {
|
|
1768
|
+
const scriptPath = process.argv[1];
|
|
1769
|
+
if (scriptPath) {
|
|
1770
|
+
const frameworkDir = path7.resolve(path7.dirname(scriptPath), "..", "..");
|
|
1771
|
+
const frameworkModules = path7.join(frameworkDir, "node_modules");
|
|
1772
|
+
if ((0, import_node_fs.existsSync)(path7.join(frameworkModules, "@camstack", "shm-ring"))) {
|
|
1773
|
+
process.env["CAMSTACK_FRAMEWORK_DIR"] = frameworkDir;
|
|
1774
|
+
const existingNodePath = process.env["NODE_PATH"];
|
|
1775
|
+
process.env["NODE_PATH"] = existingNodePath ? `${frameworkModules}${path7.delimiter}${existingNodePath}` : frameworkModules;
|
|
1776
|
+
import_node_module.Module._initPaths();
|
|
1777
|
+
console.log(
|
|
1778
|
+
`[Agent] Self-contained framework detected \u2014 CAMSTACK_FRAMEWORK_DIR=${frameworkDir}`
|
|
1779
|
+
);
|
|
1780
|
+
if (!process.env["CAMSTACK_BUNDLED_ADDONS_DIR"]) {
|
|
1781
|
+
const bundledAddons = path7.join(frameworkDir, "addons");
|
|
1782
|
+
if ((0, import_node_fs.existsSync)(bundledAddons)) {
|
|
1783
|
+
process.env["CAMSTACK_BUNDLED_ADDONS_DIR"] = bundledAddons;
|
|
1784
|
+
}
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1652
1789
|
if (!process.env["CAMSTACK_NODE_ID"]) {
|
|
1653
1790
|
process.env["CAMSTACK_NODE_ID"] = os3.hostname();
|
|
1654
1791
|
}
|