@camstack/agent 1.0.4 → 1.0.5
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-5JHGVZFN.mjs → chunk-GM2IV7NO.mjs} +354 -111
- package/dist/chunk-GM2IV7NO.mjs.map +1 -0
- package/dist/cli.js +360 -117
- package/dist/cli.js.map +1 -1
- package/dist/cli.mjs +1 -1
- package/dist/index.d.ts +9 -0
- package/dist/index.js +370 -127
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +1 -1
- package/dist/chunk-5JHGVZFN.mjs.map +0 -1
|
@@ -66,10 +66,87 @@ function loadAgentConfig(configPath, dataDirOverride) {
|
|
|
66
66
|
|
|
67
67
|
// src/agent-service.ts
|
|
68
68
|
import * as os2 from "os";
|
|
69
|
-
import * as
|
|
70
|
-
import * as
|
|
69
|
+
import * as fs3 from "fs";
|
|
70
|
+
import * as path3 from "path";
|
|
71
71
|
import { Errors } from "moleculer";
|
|
72
72
|
import { clusterSecretMatches, CLUSTER_SECRET_MISMATCH_TYPE } from "@camstack/system";
|
|
73
|
+
|
|
74
|
+
// src/agent-deploy-swap.ts
|
|
75
|
+
import * as fs2 from "fs";
|
|
76
|
+
import * as path2 from "path";
|
|
77
|
+
import { randomBytes as randomBytes2 } from "crypto";
|
|
78
|
+
function rmrf(target) {
|
|
79
|
+
fs2.rmSync(target, { recursive: true, force: true });
|
|
80
|
+
}
|
|
81
|
+
function moveDir(from, to) {
|
|
82
|
+
try {
|
|
83
|
+
fs2.renameSync(from, to);
|
|
84
|
+
} catch (err) {
|
|
85
|
+
const code = err.code;
|
|
86
|
+
if (code === "EXDEV") {
|
|
87
|
+
fs2.cpSync(from, to, { recursive: true });
|
|
88
|
+
rmrf(from);
|
|
89
|
+
} else {
|
|
90
|
+
throw err;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
async function applyDeployedBundle(input) {
|
|
95
|
+
const { addonsDir, addonId, bundle, extract, logger } = input;
|
|
96
|
+
fs2.mkdirSync(addonsDir, { recursive: true });
|
|
97
|
+
const liveDir = path2.join(addonsDir, addonId);
|
|
98
|
+
const liveParent = path2.dirname(liveDir);
|
|
99
|
+
const liveBase = path2.basename(liveDir);
|
|
100
|
+
fs2.mkdirSync(liveParent, { recursive: true });
|
|
101
|
+
const token = randomBytes2(6).toString("hex");
|
|
102
|
+
const nextDir = path2.join(liveParent, `.${liveBase}.next.${token}`);
|
|
103
|
+
const backupDir = path2.join(liveParent, `.${liveBase}.backup.${token}`);
|
|
104
|
+
fs2.mkdirSync(nextDir, { recursive: true });
|
|
105
|
+
try {
|
|
106
|
+
await extract(bundle, nextDir);
|
|
107
|
+
} catch (err) {
|
|
108
|
+
rmrf(nextDir);
|
|
109
|
+
throw err;
|
|
110
|
+
}
|
|
111
|
+
const hadLive = fs2.existsSync(liveDir);
|
|
112
|
+
if (hadLive) {
|
|
113
|
+
fs2.renameSync(liveDir, backupDir);
|
|
114
|
+
}
|
|
115
|
+
try {
|
|
116
|
+
moveDir(nextDir, liveDir);
|
|
117
|
+
} catch (swapErr) {
|
|
118
|
+
if (hadLive) {
|
|
119
|
+
try {
|
|
120
|
+
if (fs2.existsSync(liveDir)) rmrf(liveDir);
|
|
121
|
+
fs2.renameSync(backupDir, liveDir);
|
|
122
|
+
} catch (restoreErr) {
|
|
123
|
+
logger.error(
|
|
124
|
+
`agent deploy swap: restore of "${addonId}" failed after a failed swap \u2014 manual recovery may be needed`,
|
|
125
|
+
{
|
|
126
|
+
meta: {
|
|
127
|
+
backupDir,
|
|
128
|
+
error: restoreErr instanceof Error ? restoreErr.message : String(restoreErr)
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
rmrf(nextDir);
|
|
135
|
+
throw swapErr;
|
|
136
|
+
}
|
|
137
|
+
if (hadLive) rmrf(backupDir);
|
|
138
|
+
return { addonDir: liveDir };
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// src/agent-service.ts
|
|
142
|
+
var deploySwapLogger = {
|
|
143
|
+
info: (msg) => console.log(`[Agent] ${msg}`),
|
|
144
|
+
warn: (msg) => console.warn(`[Agent] ${msg}`),
|
|
145
|
+
error: (msg) => console.error(`[Agent] ${msg}`),
|
|
146
|
+
debug: (msg) => console.debug(`[Agent] ${msg}`),
|
|
147
|
+
child: () => deploySwapLogger,
|
|
148
|
+
withTags: () => deploySwapLogger
|
|
149
|
+
};
|
|
73
150
|
function getLocalIps() {
|
|
74
151
|
const interfaces = os2.networkInterfaces();
|
|
75
152
|
const ips = [];
|
|
@@ -82,11 +159,23 @@ function getLocalIps() {
|
|
|
82
159
|
}
|
|
83
160
|
return ips;
|
|
84
161
|
}
|
|
162
|
+
async function withTimeout(p, ms, fallback) {
|
|
163
|
+
let timer;
|
|
164
|
+
const timeout = new Promise((resolve5) => {
|
|
165
|
+
timer = setTimeout(() => resolve5(fallback), ms);
|
|
166
|
+
});
|
|
167
|
+
try {
|
|
168
|
+
return await Promise.race([p, timeout]);
|
|
169
|
+
} finally {
|
|
170
|
+
if (timer) clearTimeout(timer);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
var METRICS_SNAPSHOT_TIMEOUT_MS = 2e3;
|
|
85
174
|
function readHubAddressFromConfig(configPath) {
|
|
86
175
|
if (!configPath) return null;
|
|
87
176
|
try {
|
|
88
|
-
if (!
|
|
89
|
-
const raw = JSON.parse(
|
|
177
|
+
if (!fs3.existsSync(configPath)) return null;
|
|
178
|
+
const raw = JSON.parse(fs3.readFileSync(configPath, "utf-8"));
|
|
90
179
|
return typeof raw.hubAddress === "string" && raw.hubAddress.length > 0 ? raw.hubAddress : null;
|
|
91
180
|
} catch {
|
|
92
181
|
return null;
|
|
@@ -94,9 +183,9 @@ function readHubAddressFromConfig(configPath) {
|
|
|
94
183
|
}
|
|
95
184
|
function readDeployedAddonIds(addonDir) {
|
|
96
185
|
try {
|
|
97
|
-
const manifestPath =
|
|
98
|
-
if (!
|
|
99
|
-
const raw = JSON.parse(
|
|
186
|
+
const manifestPath = path3.join(addonDir, "package.json");
|
|
187
|
+
if (!fs3.existsSync(manifestPath)) return [];
|
|
188
|
+
const raw = JSON.parse(fs3.readFileSync(manifestPath, "utf-8"));
|
|
100
189
|
const entries = raw.camstack?.addons ?? [];
|
|
101
190
|
const ids = [];
|
|
102
191
|
for (const entry of entries) {
|
|
@@ -128,7 +217,11 @@ function createAgentService(deps) {
|
|
|
128
217
|
let memoryPercent = 0;
|
|
129
218
|
const metrics = deps.getMetricsProvider?.();
|
|
130
219
|
if (metrics) {
|
|
131
|
-
const snapshot = await
|
|
220
|
+
const snapshot = await withTimeout(
|
|
221
|
+
metrics.getCached(),
|
|
222
|
+
METRICS_SNAPSHOT_TIMEOUT_MS,
|
|
223
|
+
null
|
|
224
|
+
);
|
|
132
225
|
if (snapshot) {
|
|
133
226
|
cpuPercent = snapshot.cpu.total;
|
|
134
227
|
memoryPercent = snapshot.memory.percent;
|
|
@@ -151,7 +244,9 @@ function createAgentService(deps) {
|
|
|
151
244
|
addons: [...deps.loadedAddons.values()].map((a) => ({
|
|
152
245
|
id: a.id,
|
|
153
246
|
status: a.status,
|
|
154
|
-
version
|
|
247
|
+
// Report the PACKAGE version (falls back to the declaration
|
|
248
|
+
// version) so the hub's per-node update check is accurate.
|
|
249
|
+
version: a.packageVersion ?? a.version,
|
|
155
250
|
packageName: a.packageName
|
|
156
251
|
}))
|
|
157
252
|
};
|
|
@@ -165,7 +260,11 @@ function createAgentService(deps) {
|
|
|
165
260
|
const metrics = deps.getMetricsProvider?.();
|
|
166
261
|
if (metrics) {
|
|
167
262
|
try {
|
|
168
|
-
const snapshot = await
|
|
263
|
+
const snapshot = await withTimeout(
|
|
264
|
+
metrics.getCached(),
|
|
265
|
+
METRICS_SNAPSHOT_TIMEOUT_MS,
|
|
266
|
+
null
|
|
267
|
+
);
|
|
169
268
|
if (snapshot) {
|
|
170
269
|
cpuPercent = snapshot.cpu.total;
|
|
171
270
|
memoryPercent = snapshot.memory.percent;
|
|
@@ -215,17 +314,17 @@ function createAgentService(deps) {
|
|
|
215
314
|
deps.agentName = newName.trim();
|
|
216
315
|
broker.logger.info(`Agent renamed: "${oldName}" \u2192 "${deps.agentName}"`);
|
|
217
316
|
try {
|
|
218
|
-
const configFile =
|
|
317
|
+
const configFile = path3.resolve(deps.configPath);
|
|
219
318
|
let raw = {};
|
|
220
|
-
if (
|
|
319
|
+
if (fs3.existsSync(configFile)) {
|
|
221
320
|
try {
|
|
222
|
-
raw = JSON.parse(
|
|
321
|
+
raw = JSON.parse(fs3.readFileSync(configFile, "utf-8"));
|
|
223
322
|
} catch {
|
|
224
323
|
}
|
|
225
324
|
}
|
|
226
325
|
raw.name = deps.agentName;
|
|
227
|
-
|
|
228
|
-
|
|
326
|
+
fs3.mkdirSync(path3.dirname(configFile), { recursive: true });
|
|
327
|
+
fs3.writeFileSync(configFile, JSON.stringify(raw, null, 2), "utf-8");
|
|
229
328
|
broker.logger.info(`Agent name persisted to ${configFile}`);
|
|
230
329
|
} catch (err) {
|
|
231
330
|
broker.logger.warn(
|
|
@@ -245,20 +344,31 @@ function createAgentService(deps) {
|
|
|
245
344
|
handler: async (ctx) => {
|
|
246
345
|
const { params } = ctx;
|
|
247
346
|
const { addonId, bundle } = params;
|
|
248
|
-
const addonDir = path2.join(deps.addonsDir, addonId);
|
|
249
|
-
fs2.mkdirSync(deps.addonsDir, { recursive: true });
|
|
250
|
-
const bundlePath = path2.join(deps.addonsDir, `${addonId}.tgz`);
|
|
251
347
|
const bufferData = typeof bundle === "string" ? Buffer.from(bundle, "base64") : bundle;
|
|
252
|
-
|
|
253
|
-
const {
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
348
|
+
const { execFile } = await import("child_process");
|
|
349
|
+
const { promisify } = await import("util");
|
|
350
|
+
const execFileAsync = promisify(execFile);
|
|
351
|
+
const extract = async (tgz, destDir) => {
|
|
352
|
+
const tgzPath = path3.join(destDir, "..", `.${path3.basename(destDir)}.tgz`);
|
|
353
|
+
fs3.writeFileSync(tgzPath, tgz);
|
|
354
|
+
try {
|
|
355
|
+
await execFileAsync("tar", ["-xzf", tgzPath, "-C", destDir, "--strip-components=1"], {
|
|
356
|
+
timeout: 6e4
|
|
357
|
+
});
|
|
358
|
+
} finally {
|
|
359
|
+
try {
|
|
360
|
+
fs3.unlinkSync(tgzPath);
|
|
361
|
+
} catch {
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
};
|
|
365
|
+
const { addonDir } = await applyDeployedBundle({
|
|
366
|
+
addonsDir: deps.addonsDir,
|
|
367
|
+
addonId,
|
|
368
|
+
bundle: bufferData,
|
|
369
|
+
extract,
|
|
370
|
+
logger: deploySwapLogger
|
|
260
371
|
});
|
|
261
|
-
fs2.unlinkSync(bundlePath);
|
|
262
372
|
for (const declId of readDeployedAddonIds(addonDir)) {
|
|
263
373
|
deps.loadedAddons.delete(declId);
|
|
264
374
|
}
|
|
@@ -305,15 +415,15 @@ function createAgentService(deps) {
|
|
|
305
415
|
} catch {
|
|
306
416
|
}
|
|
307
417
|
deps.loadedAddons.delete(addonId);
|
|
308
|
-
const addonDir =
|
|
309
|
-
if (
|
|
310
|
-
|
|
418
|
+
const addonDir = path3.join(deps.addonsDir, addonId);
|
|
419
|
+
if (fs3.existsSync(addonDir)) {
|
|
420
|
+
fs3.rmSync(addonDir, { recursive: true, force: true });
|
|
311
421
|
}
|
|
312
422
|
const pkgName = entry?.packageName;
|
|
313
423
|
if (pkgName && pkgName !== addonId) {
|
|
314
|
-
const pkgDir =
|
|
315
|
-
if (
|
|
316
|
-
|
|
424
|
+
const pkgDir = path3.join(deps.addonsDir, pkgName);
|
|
425
|
+
if (fs3.existsSync(pkgDir)) {
|
|
426
|
+
fs3.rmSync(pkgDir, { recursive: true, force: true });
|
|
317
427
|
}
|
|
318
428
|
}
|
|
319
429
|
broker.logger.info(`$agent.undeploy: ${addonId} disposed (instance + service + folder)`);
|
|
@@ -369,30 +479,30 @@ function createAgentService(deps) {
|
|
|
369
479
|
|
|
370
480
|
// src/agent-http.ts
|
|
371
481
|
import Fastify from "fastify";
|
|
372
|
-
import * as
|
|
373
|
-
import * as
|
|
482
|
+
import * as fs4 from "fs";
|
|
483
|
+
import * as path4 from "path";
|
|
374
484
|
function resolveUiDistDir(dataDir) {
|
|
375
485
|
const candidates = [
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
486
|
+
path4.resolve(__dirname, "../../addon-agent-ui/dist"),
|
|
487
|
+
path4.join(dataDir, "addons", "@camstack", "addon-agent-ui", "dist"),
|
|
488
|
+
path4.join(dataDir, "agent-ui")
|
|
379
489
|
];
|
|
380
490
|
for (const dir of candidates) {
|
|
381
|
-
if (
|
|
491
|
+
if (fs4.existsSync(path4.join(dir, "index.html"))) return dir;
|
|
382
492
|
}
|
|
383
493
|
return null;
|
|
384
494
|
}
|
|
385
495
|
function readConfigFile(configPath) {
|
|
386
|
-
if (!
|
|
496
|
+
if (!fs4.existsSync(configPath)) return {};
|
|
387
497
|
try {
|
|
388
|
-
return JSON.parse(
|
|
498
|
+
return JSON.parse(fs4.readFileSync(configPath, "utf-8"));
|
|
389
499
|
} catch {
|
|
390
500
|
return {};
|
|
391
501
|
}
|
|
392
502
|
}
|
|
393
503
|
function writeConfigFile(configPath, data) {
|
|
394
|
-
|
|
395
|
-
|
|
504
|
+
fs4.mkdirSync(path4.dirname(configPath), { recursive: true });
|
|
505
|
+
fs4.writeFileSync(configPath, JSON.stringify(data, null, 2), "utf-8");
|
|
396
506
|
}
|
|
397
507
|
function getRegistryNodes(broker) {
|
|
398
508
|
try {
|
|
@@ -555,8 +665,8 @@ async function startAgentHttpServer(getBroker, config) {
|
|
|
555
665
|
}
|
|
556
666
|
|
|
557
667
|
// src/agent-bootstrap.ts
|
|
558
|
-
import * as
|
|
559
|
-
import * as
|
|
668
|
+
import * as fs6 from "fs";
|
|
669
|
+
import * as path6 from "path";
|
|
560
670
|
import {
|
|
561
671
|
createBroker,
|
|
562
672
|
createAddonService,
|
|
@@ -589,7 +699,7 @@ import {
|
|
|
589
699
|
normalizeAddonInitResult,
|
|
590
700
|
isDeployableToAgent,
|
|
591
701
|
resolveRunnerId,
|
|
592
|
-
resolveAddonPlacement
|
|
702
|
+
resolveAddonPlacement as resolveAddonPlacement2
|
|
593
703
|
} from "@camstack/types";
|
|
594
704
|
import {
|
|
595
705
|
storageCapability,
|
|
@@ -606,6 +716,84 @@ import {
|
|
|
606
716
|
errMsg
|
|
607
717
|
} from "@camstack/types";
|
|
608
718
|
|
|
719
|
+
// src/agent-group-runner.ts
|
|
720
|
+
import * as fs5 from "fs";
|
|
721
|
+
import * as path5 from "path";
|
|
722
|
+
import { resolveAddonPlacement } from "@camstack/types";
|
|
723
|
+
function readPackageManifestAddons(dir) {
|
|
724
|
+
try {
|
|
725
|
+
const pkgPath = path5.join(dir, "package.json");
|
|
726
|
+
if (!fs5.existsSync(pkgPath)) return null;
|
|
727
|
+
const raw = JSON.parse(fs5.readFileSync(pkgPath, "utf-8"));
|
|
728
|
+
const packageName = typeof raw.name === "string" ? raw.name : "";
|
|
729
|
+
const packageVersion = typeof raw.version === "string" ? raw.version : "0.0.0";
|
|
730
|
+
const entries = Array.isArray(raw.camstack?.addons) ? raw.camstack.addons : [];
|
|
731
|
+
const declarations = [];
|
|
732
|
+
for (const entry of entries) {
|
|
733
|
+
if (entry !== null && typeof entry === "object" && typeof entry.id === "string") {
|
|
734
|
+
declarations.push(entry);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
if (!packageName || declarations.length === 0) return null;
|
|
738
|
+
return { packageName, packageVersion, declarations };
|
|
739
|
+
} catch {
|
|
740
|
+
return null;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
function isGroupRunnerAddon(decl) {
|
|
744
|
+
return decl.execution !== void 0 && resolveAddonPlacement(decl) !== "hub-only";
|
|
745
|
+
}
|
|
746
|
+
function registerGroupRunnerProviders(deps, addons) {
|
|
747
|
+
for (const a of addons) {
|
|
748
|
+
for (const cap of a.capabilities) {
|
|
749
|
+
const capName = typeof cap === "string" ? cap : cap.name;
|
|
750
|
+
const proxy = new Proxy(
|
|
751
|
+
{},
|
|
752
|
+
{
|
|
753
|
+
get(_target, prop) {
|
|
754
|
+
if (prop === "then" || typeof prop === "symbol") return void 0;
|
|
755
|
+
return (params) => deps.broker.call(`${a.addonId}.${capName}.${prop}`, params);
|
|
756
|
+
}
|
|
757
|
+
}
|
|
758
|
+
);
|
|
759
|
+
deps.capabilityRegistry.registerProvider(capName, a.addonId, proxy);
|
|
760
|
+
}
|
|
761
|
+
deps.loadedAddons.set(a.addonId, {
|
|
762
|
+
id: a.addonId,
|
|
763
|
+
status: "running",
|
|
764
|
+
version: a.version,
|
|
765
|
+
packageName: a.packageName,
|
|
766
|
+
packageVersion: a.packageVersion
|
|
767
|
+
});
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
async function ensureGroupRunner(deps, groupId, addons) {
|
|
771
|
+
try {
|
|
772
|
+
const restart = await deps.broker.call("$process.restart", { name: groupId });
|
|
773
|
+
if (!restart.success) {
|
|
774
|
+
if (restart.reason !== void 0 && restart.reason !== "not found") {
|
|
775
|
+
deps.logger.warn(
|
|
776
|
+
`group "${groupId}" restart returned "${restart.reason}" \u2014 spawning a fresh runner`
|
|
777
|
+
);
|
|
778
|
+
}
|
|
779
|
+
await deps.broker.call("$process.spawnRunner", {
|
|
780
|
+
runnerId: groupId,
|
|
781
|
+
addons: addons.map((a) => ({ addonId: a.addonId, addonDir: a.addonDir }))
|
|
782
|
+
});
|
|
783
|
+
}
|
|
784
|
+
registerGroupRunnerProviders(deps, addons);
|
|
785
|
+
deps.logger.info(
|
|
786
|
+
`group "${groupId}" live with ${addons.length} addon(s): ${addons.map((a) => a.addonId).join(", ")}`
|
|
787
|
+
);
|
|
788
|
+
} catch (err) {
|
|
789
|
+
const msg = err instanceof Error ? err.stack ?? err.message : String(err);
|
|
790
|
+
deps.logger.error(`failed to ensure group "${groupId}": ${msg}`);
|
|
791
|
+
for (const a of addons) {
|
|
792
|
+
deps.loadedAddons.set(a.addonId, { id: a.addonId, status: "error" });
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
|
|
609
797
|
// src/agent-cap-dispatch-service.ts
|
|
610
798
|
import { AGENT_CAP_FWD_SERVICE } from "@camstack/system";
|
|
611
799
|
function narrowParams(raw) {
|
|
@@ -687,7 +875,7 @@ async function startAgent(configPath) {
|
|
|
687
875
|
const bundledDir = process.env["CAMSTACK_BUNDLED_ADDONS_DIR"];
|
|
688
876
|
let workspaceDir = null;
|
|
689
877
|
let resolvedSource = explicitSource;
|
|
690
|
-
if (bundledDir &&
|
|
878
|
+
if (bundledDir && fs6.existsSync(bundledDir)) {
|
|
691
879
|
workspaceDir = bundledDir;
|
|
692
880
|
resolvedSource = "local";
|
|
693
881
|
console.log(`[Agent] Using bundled addons from ${bundledDir}`);
|
|
@@ -1044,7 +1232,12 @@ async function bootCoreAddons(broker, config, registry, loadedAddons, loggerFact
|
|
|
1044
1232
|
storageProvider,
|
|
1045
1233
|
addonConfig: { rootPath: config.dataDir },
|
|
1046
1234
|
createLogger: loggerFactory,
|
|
1047
|
-
capabilityRegistry: registry
|
|
1235
|
+
capabilityRegistry: registry,
|
|
1236
|
+
// Feed the storage-orchestrator its first-boot seed declarations
|
|
1237
|
+
// (addons-data:default, models:default, …). Without this the agent's
|
|
1238
|
+
// sqlite-settings aborts boot: "No default storage location
|
|
1239
|
+
// configured for type addons-data" → fatal crash-loop.
|
|
1240
|
+
listStorageLocationDeclarations: () => loader.listStorageLocationDeclarations()
|
|
1048
1241
|
}
|
|
1049
1242
|
);
|
|
1050
1243
|
const initResult = normalizeAddonInitResult(await instance.initialize(context));
|
|
@@ -1058,6 +1251,7 @@ async function bootCoreAddons(broker, config, registry, loadedAddons, loggerFact
|
|
|
1058
1251
|
status: "running",
|
|
1059
1252
|
version: addon.declaration.version,
|
|
1060
1253
|
packageName: addon.packageName,
|
|
1254
|
+
packageVersion: addon.packageVersion,
|
|
1061
1255
|
addon: instance
|
|
1062
1256
|
});
|
|
1063
1257
|
console.log(`[Agent] Core addon "${addonId}" initialized`);
|
|
@@ -1087,7 +1281,7 @@ async function loadClusterCapableAddons(broker, config, capabilityRegistry, load
|
|
|
1087
1281
|
for (const registered of loader.listAddons()) {
|
|
1088
1282
|
if (loadedAddons.has(registered.declaration.id)) continue;
|
|
1089
1283
|
if (registered.declaration.execution === void 0) continue;
|
|
1090
|
-
const placement =
|
|
1284
|
+
const placement = resolveAddonPlacement2(registered.declaration);
|
|
1091
1285
|
if (placement === "hub-only") continue;
|
|
1092
1286
|
allGroupCandidates.push({
|
|
1093
1287
|
groupId: resolveRunnerId(registered.declaration, registered.declaration.id),
|
|
@@ -1095,6 +1289,7 @@ async function loadClusterCapableAddons(broker, config, capabilityRegistry, load
|
|
|
1095
1289
|
addonDir: dir,
|
|
1096
1290
|
version: registered.declaration.version ?? "0.0.0",
|
|
1097
1291
|
packageName: registered.packageName,
|
|
1292
|
+
packageVersion: registered.packageVersion,
|
|
1098
1293
|
capabilities: registered.declaration.capabilities ?? []
|
|
1099
1294
|
});
|
|
1100
1295
|
}
|
|
@@ -1112,27 +1307,7 @@ async function loadClusterCapableAddons(broker, config, capabilityRegistry, load
|
|
|
1112
1307
|
runnerId: groupId,
|
|
1113
1308
|
addons: addons.map((a) => ({ addonId: a.addonId, addonDir: a.addonDir }))
|
|
1114
1309
|
});
|
|
1115
|
-
|
|
1116
|
-
for (const cap of a.capabilities) {
|
|
1117
|
-
const capName = typeof cap === "string" ? cap : cap.name;
|
|
1118
|
-
const proxy = new Proxy(
|
|
1119
|
-
{},
|
|
1120
|
-
{
|
|
1121
|
-
get(_target, prop) {
|
|
1122
|
-
if (prop === "then" || typeof prop === "symbol") return void 0;
|
|
1123
|
-
return (params) => broker.call(`${a.addonId}.${capName}.${prop}`, params);
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
);
|
|
1127
|
-
capabilityRegistry.registerProvider(capName, a.addonId, proxy);
|
|
1128
|
-
}
|
|
1129
|
-
loadedAddons.set(a.addonId, {
|
|
1130
|
-
id: a.addonId,
|
|
1131
|
-
status: "running",
|
|
1132
|
-
version: a.version,
|
|
1133
|
-
packageName: a.packageName
|
|
1134
|
-
});
|
|
1135
|
-
}
|
|
1310
|
+
registerGroupRunnerProviders({ broker, capabilityRegistry, loadedAddons }, addons);
|
|
1136
1311
|
console.log(
|
|
1137
1312
|
`[Agent] Group "${groupId}" spawned with ${addons.length} addon(s): ${addons.map((a) => a.addonId).join(", ")}`
|
|
1138
1313
|
);
|
|
@@ -1159,9 +1334,66 @@ async function loadClusterCapableAddons(broker, config, capabilityRegistry, load
|
|
|
1159
1334
|
}
|
|
1160
1335
|
}
|
|
1161
1336
|
async function loadDeployedAddons(broker, addonsDir, dataDir, loadedAddons, storageProvider, loggerFactory, capabilityRegistry) {
|
|
1162
|
-
if (!
|
|
1163
|
-
const
|
|
1164
|
-
|
|
1337
|
+
if (!fs6.existsSync(addonsDir)) return;
|
|
1338
|
+
const packageDirs = resolveAddonPackageDirs(addonsDir);
|
|
1339
|
+
const groupCandidates = [];
|
|
1340
|
+
const inProcessDirs = /* @__PURE__ */ new Set();
|
|
1341
|
+
for (const dir of packageDirs) {
|
|
1342
|
+
const manifest = readPackageManifestAddons(dir);
|
|
1343
|
+
if (!manifest) continue;
|
|
1344
|
+
for (const decl of manifest.declarations) {
|
|
1345
|
+
const addonId = decl.id;
|
|
1346
|
+
if (loadedAddons.has(addonId)) continue;
|
|
1347
|
+
if (!isDeployableToAgent(decl)) continue;
|
|
1348
|
+
if (isGroupRunnerAddon(decl)) {
|
|
1349
|
+
groupCandidates.push({
|
|
1350
|
+
groupId: resolveRunnerId(decl, addonId),
|
|
1351
|
+
addonId,
|
|
1352
|
+
addonDir: dir,
|
|
1353
|
+
version: decl.version ?? "0.0.0",
|
|
1354
|
+
packageName: manifest.packageName,
|
|
1355
|
+
packageVersion: manifest.packageVersion,
|
|
1356
|
+
capabilities: decl.capabilities ?? []
|
|
1357
|
+
});
|
|
1358
|
+
} else {
|
|
1359
|
+
inProcessDirs.add(dir);
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
if (inProcessDirs.size > 0) {
|
|
1364
|
+
await loadInProcessDeployedAddons(
|
|
1365
|
+
broker,
|
|
1366
|
+
[...inProcessDirs],
|
|
1367
|
+
dataDir,
|
|
1368
|
+
loadedAddons,
|
|
1369
|
+
storageProvider,
|
|
1370
|
+
loggerFactory,
|
|
1371
|
+
capabilityRegistry
|
|
1372
|
+
);
|
|
1373
|
+
}
|
|
1374
|
+
if (groupCandidates.length === 0) return;
|
|
1375
|
+
if (!capabilityRegistry) {
|
|
1376
|
+
console.warn(
|
|
1377
|
+
`[Agent] ${groupCandidates.length} deployed group addon(s) skipped \u2014 no capabilityRegistry passed`
|
|
1378
|
+
);
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
const grouped = /* @__PURE__ */ new Map();
|
|
1382
|
+
for (const c of groupCandidates) {
|
|
1383
|
+
const arr = grouped.get(c.groupId) ?? [];
|
|
1384
|
+
arr.push(c);
|
|
1385
|
+
grouped.set(c.groupId, arr);
|
|
1386
|
+
}
|
|
1387
|
+
const deployLogger = loggerFactory("agent-group-runner");
|
|
1388
|
+
for (const [groupId, addons] of grouped) {
|
|
1389
|
+
await ensureGroupRunner(
|
|
1390
|
+
{ broker, capabilityRegistry, loadedAddons, logger: deployLogger },
|
|
1391
|
+
groupId,
|
|
1392
|
+
addons
|
|
1393
|
+
);
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
async function loadInProcessDeployedAddons(broker, dirs, dataDir, loadedAddons, storageProvider, loggerFactory, capabilityRegistry) {
|
|
1165
1397
|
const modelsDir = storageProvider ? await storageProvider.resolve({ location: "models", relativePath: "" }).catch(() => void 0) : void 0;
|
|
1166
1398
|
const contextOptions = {
|
|
1167
1399
|
storageProvider,
|
|
@@ -1169,45 +1401,56 @@ async function loadDeployedAddons(broker, addonsDir, dataDir, loadedAddons, stor
|
|
|
1169
1401
|
createLogger: loggerFactory,
|
|
1170
1402
|
capabilityRegistry
|
|
1171
1403
|
};
|
|
1172
|
-
for (const
|
|
1173
|
-
const
|
|
1174
|
-
if (loadedAddons.has(addonId)) continue;
|
|
1175
|
-
if (!isDeployableToAgent(registered.declaration)) continue;
|
|
1404
|
+
for (const dir of dirs) {
|
|
1405
|
+
const loader = new AddonLoader();
|
|
1176
1406
|
try {
|
|
1177
|
-
|
|
1178
|
-
const context = await createAddonContext(
|
|
1179
|
-
broker,
|
|
1180
|
-
registered.declaration,
|
|
1181
|
-
dataDir,
|
|
1182
|
-
contextOptions
|
|
1183
|
-
);
|
|
1184
|
-
await instance.initialize(context);
|
|
1185
|
-
const serviceSchema = createAddonService(instance, registered.declaration);
|
|
1186
|
-
broker.createService(serviceSchema);
|
|
1187
|
-
loadedAddons.set(addonId, {
|
|
1188
|
-
id: addonId,
|
|
1189
|
-
status: "running",
|
|
1190
|
-
version: registered.declaration.version,
|
|
1191
|
-
packageName: registered.packageName,
|
|
1192
|
-
addon: instance
|
|
1193
|
-
});
|
|
1194
|
-
console.log(`[Agent] Deployed addon "${addonId}" loaded`);
|
|
1407
|
+
await loader.loadFromAddonDir(dir);
|
|
1195
1408
|
} catch (err) {
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1409
|
+
console.warn(`[Agent] Skipping ${dir}: ${errMsg(err)}`);
|
|
1410
|
+
continue;
|
|
1411
|
+
}
|
|
1412
|
+
for (const registered of loader.listAddons()) {
|
|
1413
|
+
const addonId = registered.declaration.id;
|
|
1414
|
+
if (loadedAddons.has(addonId)) continue;
|
|
1415
|
+
if (!isDeployableToAgent(registered.declaration)) continue;
|
|
1416
|
+
if (isGroupRunnerAddon(registered.declaration)) continue;
|
|
1417
|
+
try {
|
|
1418
|
+
const instance = new registered.addonClass();
|
|
1419
|
+
const context = await createAddonContext(
|
|
1420
|
+
broker,
|
|
1421
|
+
registered.declaration,
|
|
1422
|
+
dataDir,
|
|
1423
|
+
contextOptions
|
|
1424
|
+
);
|
|
1425
|
+
await instance.initialize(context);
|
|
1426
|
+
const serviceSchema = createAddonService(instance, registered.declaration);
|
|
1427
|
+
broker.createService(serviceSchema);
|
|
1428
|
+
loadedAddons.set(addonId, {
|
|
1429
|
+
id: addonId,
|
|
1430
|
+
status: "running",
|
|
1431
|
+
version: registered.declaration.version,
|
|
1432
|
+
packageName: registered.packageName,
|
|
1433
|
+
packageVersion: registered.packageVersion,
|
|
1434
|
+
addon: instance
|
|
1435
|
+
});
|
|
1436
|
+
console.log(`[Agent] Deployed addon "${addonId}" loaded in-process`);
|
|
1437
|
+
} catch (err) {
|
|
1438
|
+
const msg = errMsg(err);
|
|
1439
|
+
console.error(`[Agent] Failed to load deployed addon "${addonId}": ${msg}`);
|
|
1440
|
+
loadedAddons.set(addonId, { id: addonId, status: "error" });
|
|
1441
|
+
}
|
|
1199
1442
|
}
|
|
1200
1443
|
}
|
|
1201
1444
|
}
|
|
1202
1445
|
function readAgentVersion() {
|
|
1203
1446
|
const candidates = [
|
|
1204
|
-
|
|
1205
|
-
|
|
1447
|
+
path6.resolve(__dirname, "..", "package.json"),
|
|
1448
|
+
path6.resolve(__dirname, "..", "..", "package.json")
|
|
1206
1449
|
];
|
|
1207
1450
|
for (const candidate of candidates) {
|
|
1208
1451
|
try {
|
|
1209
|
-
if (!
|
|
1210
|
-
const raw = JSON.parse(
|
|
1452
|
+
if (!fs6.existsSync(candidate)) continue;
|
|
1453
|
+
const raw = JSON.parse(fs6.readFileSync(candidate, "utf-8"));
|
|
1211
1454
|
if (raw.name === "@camstack/agent" && typeof raw.version === "string") return raw.version;
|
|
1212
1455
|
} catch {
|
|
1213
1456
|
}
|
|
@@ -1216,24 +1459,24 @@ function readAgentVersion() {
|
|
|
1216
1459
|
}
|
|
1217
1460
|
function isDir(p) {
|
|
1218
1461
|
try {
|
|
1219
|
-
return
|
|
1462
|
+
return fs6.statSync(p).isDirectory();
|
|
1220
1463
|
} catch {
|
|
1221
1464
|
return false;
|
|
1222
1465
|
}
|
|
1223
1466
|
}
|
|
1224
1467
|
function resolveAddonPackageDirs(addonsDir) {
|
|
1225
1468
|
const dirs = [];
|
|
1226
|
-
if (!
|
|
1227
|
-
for (const name of
|
|
1228
|
-
const full =
|
|
1469
|
+
if (!fs6.existsSync(addonsDir)) return dirs;
|
|
1470
|
+
for (const name of fs6.readdirSync(addonsDir)) {
|
|
1471
|
+
const full = path6.join(addonsDir, name);
|
|
1229
1472
|
if (name.startsWith("@") && isDir(full)) {
|
|
1230
|
-
for (const sub of
|
|
1231
|
-
const subFull =
|
|
1232
|
-
if (isDir(subFull) &&
|
|
1473
|
+
for (const sub of fs6.readdirSync(full)) {
|
|
1474
|
+
const subFull = path6.join(full, sub);
|
|
1475
|
+
if (isDir(subFull) && fs6.existsSync(path6.join(subFull, "package.json"))) {
|
|
1233
1476
|
dirs.push(subFull);
|
|
1234
1477
|
}
|
|
1235
1478
|
}
|
|
1236
|
-
} else if (isDir(full) &&
|
|
1479
|
+
} else if (isDir(full) && fs6.existsSync(path6.join(full, "package.json"))) {
|
|
1237
1480
|
dirs.push(full);
|
|
1238
1481
|
}
|
|
1239
1482
|
}
|
|
@@ -1266,4 +1509,4 @@ export {
|
|
|
1266
1509
|
startAgentHttpServer,
|
|
1267
1510
|
startAgent
|
|
1268
1511
|
};
|
|
1269
|
-
//# sourceMappingURL=chunk-
|
|
1512
|
+
//# sourceMappingURL=chunk-GM2IV7NO.mjs.map
|