@camstack/system 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/addon-runner.js +1 -1
- package/dist/addon-runner.mjs +1 -1
- package/dist/addon-utils.d.ts +1 -1
- package/dist/addon-utils.js +3 -1
- package/dist/addon-utils.mjs +2 -2
- package/dist/download/model-downloader.d.ts +25 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +74 -6
- package/dist/index.mjs +73 -7
- package/dist/kernel/addon-installer.d.ts +20 -0
- package/dist/kernel/index.d.ts +1 -0
- package/dist/kernel/moleculer/addon-deploy-source.d.ts +16 -0
- package/dist/kernel/moleculer/trpc-links.d.ts +11 -0
- package/dist/kernel/transport/cap-route-resolver.d.ts +15 -0
- package/dist/{manifest-python-deps-BcrTzHH_.mjs → manifest-python-deps-8Wvwz-d1.mjs} +865 -837
- package/dist/{manifest-python-deps-BWURo7dc.js → manifest-python-deps-RihGbmpb.js} +864 -836
- package/dist/{model-download-service-C7AjBsX9.mjs → model-download-service-C-IHWnXx.mjs} +69 -14
- package/dist/{model-download-service-JtVQtbb6.js → model-download-service-D-Umz4-L.js} +74 -13
- package/package.json +1 -1
package/dist/addon-runner.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const require_chunk = require("./chunk-Cek0wNdY.js");
|
|
2
|
-
const require_manifest_python_deps = require("./manifest-python-deps-
|
|
2
|
+
const require_manifest_python_deps = require("./manifest-python-deps-RihGbmpb.js");
|
|
3
3
|
const require_custom_action_registry = require("./custom-action-registry-vLYEFTtv.js");
|
|
4
4
|
let node_fs = require("node:fs");
|
|
5
5
|
node_fs = require_chunk.__toESM(node_fs);
|
package/dist/addon-runner.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $ as setWorkerNativeCapsChangeListener, A as createUdsLoggerWithControl,
|
|
1
|
+
import { $ as setWorkerNativeCapsChangeListener, A as createUdsLoggerWithControl, X as getWorkerNativeCapProvider, Z as getWorkerNativeCapSnapshot, i as createUdsAddonContext, j as LocalChildClient, mt as resolveAddonClass, pt as installManifestNativeDeps, t as installManifestPythonDeps, tt as validateProviderRegistrations } from "./manifest-python-deps-8Wvwz-d1.mjs";
|
|
2
2
|
import { t as CustomActionRegistry } from "./custom-action-registry-BEXwC-oo.mjs";
|
|
3
3
|
import { register } from "node:module";
|
|
4
4
|
import * as fs from "node:fs";
|
package/dist/addon-utils.d.ts
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
*
|
|
15
15
|
* See memory: camstack_runner_ram_and_build_rootcause.
|
|
16
16
|
*/
|
|
17
|
-
export { downloadFile, ensureModel, isModelDownloaded, deleteModelFromDisk, } from './download/model-downloader.js';
|
|
17
|
+
export { downloadFile, ensureModel, isModelDownloaded, deleteModelFromDisk, collectModelFiles, getModelFilePath, } from './download/model-downloader.js';
|
|
18
18
|
export { ModelDownloadService } from './download/model-download-service.js';
|
|
19
19
|
export { createFileDataPlaneHandler } from './http/file-data-plane.js';
|
|
20
20
|
export { CustomActionRegistry } from './kernel/custom-action-registry.js';
|
package/dist/addon-utils.js
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
require("./chunk-Cek0wNdY.js");
|
|
3
|
-
const require_model_download_service = require("./model-download-service-
|
|
3
|
+
const require_model_download_service = require("./model-download-service-D-Umz4-L.js");
|
|
4
4
|
const require_custom_action_registry = require("./custom-action-registry-vLYEFTtv.js");
|
|
5
5
|
exports.CustomActionRegistry = require_custom_action_registry.CustomActionRegistry;
|
|
6
6
|
exports.ModelDownloadService = require_model_download_service.ModelDownloadService;
|
|
7
|
+
exports.collectModelFiles = require_model_download_service.collectModelFiles;
|
|
7
8
|
exports.createFileDataPlaneHandler = require_model_download_service.createFileDataPlaneHandler;
|
|
8
9
|
exports.deleteModelFromDisk = require_model_download_service.deleteModelFromDisk;
|
|
9
10
|
exports.downloadFile = require_model_download_service.downloadFile;
|
|
10
11
|
exports.ensureModel = require_model_download_service.ensureModel;
|
|
12
|
+
exports.getModelFilePath = require_model_download_service.getModelFilePath;
|
|
11
13
|
exports.isModelDownloaded = require_model_download_service.isModelDownloaded;
|
package/dist/addon-utils.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { c as getModelFilePath, i as downloadFile, l as isModelDownloaded, n as collectModelFiles, o as ensureModel, r as deleteModelFromDisk, t as ModelDownloadService, u as createFileDataPlaneHandler } from "./model-download-service-C-IHWnXx.mjs";
|
|
2
2
|
import { t as CustomActionRegistry } from "./custom-action-registry-BEXwC-oo.mjs";
|
|
3
|
-
export { CustomActionRegistry, ModelDownloadService, createFileDataPlaneHandler, deleteModelFromDisk, downloadFile, ensureModel, isModelDownloaded };
|
|
3
|
+
export { CustomActionRegistry, ModelDownloadService, collectModelFiles, createFileDataPlaneHandler, deleteModelFromDisk, downloadFile, ensureModel, getModelFilePath, isModelDownloaded };
|
|
@@ -1,4 +1,28 @@
|
|
|
1
|
-
import { ModelCatalogEntry, ModelDownloadOptions, ModelDownloadResult, ModelFormat } from '@camstack/types';
|
|
1
|
+
import { ModelCatalogEntry, ModelDownloadOptions, ModelDownloadResult, ModelFormat, ModelFormatEntry } from '@camstack/types';
|
|
2
|
+
export declare function isNonEmptyFile(filePath: string): boolean;
|
|
3
|
+
/**
|
|
4
|
+
* Sibling files of a single-file (non-directory) format — extra files the
|
|
5
|
+
* format needs, fetched from the same remote directory as `url` and stored
|
|
6
|
+
* flat alongside the main file in `modelsDir`. Catalog-declared via
|
|
7
|
+
* `formatEntry.files`, e.g. OpenVINO IR lists its `.bin` weights next to the
|
|
8
|
+
* `.xml`. The downloader stays format-agnostic; the convention lives in the
|
|
9
|
+
* catalog data (like `MLPACKAGE_FILES` for the directory case).
|
|
10
|
+
*/
|
|
11
|
+
export declare function siblingFilesFor(formatEntry: ModelFormatEntry): readonly string[];
|
|
12
|
+
/** Resolve a sibling's remote URL relative to the main file's directory. */
|
|
13
|
+
export declare function siblingUrl(mainUrl: string, sibling: string): string;
|
|
14
|
+
/**
|
|
15
|
+
* The relative filenames a model format occupies under `modelsDir` — the main
|
|
16
|
+
* file (URL basename, or `${id}.${format}` fallback), its declared siblings
|
|
17
|
+
* (e.g. the OpenVINO `.bin` next to the `.xml`), and any `extraFiles`. For a
|
|
18
|
+
* directory bundle the single entry is the bundle dir itself (tar/extract
|
|
19
|
+
* recurse into it).
|
|
20
|
+
*
|
|
21
|
+
* Used by model distribution (P2): the hub tars exactly these names from its
|
|
22
|
+
* `modelsDir` and the agent untars them into its own `modelsDir`, so both ends
|
|
23
|
+
* agree on the on-disk layout without re-deriving it.
|
|
24
|
+
*/
|
|
25
|
+
export declare function collectModelFiles(entry: ModelCatalogEntry, format: ModelFormat): string[];
|
|
2
26
|
/**
|
|
3
27
|
* Download a single file from a URL to a destination path.
|
|
4
28
|
* Uses native fetch() (Node 22+) which handles redirects natively.
|
package/dist/index.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export { proxyToUpstream } from './http/reverse-proxy.js';
|
|
|
9
9
|
export type { ReverseProxyOptions } from './http/reverse-proxy.js';
|
|
10
10
|
export { createFileDataPlaneHandler } from './http/file-data-plane.js';
|
|
11
11
|
export type { FileDataPlaneOptions } from './http/file-data-plane.js';
|
|
12
|
-
export { downloadModel, downloadFile, fetchJson, ensureModel, getModelFilePath, isModelDownloaded, deleteModelFromDisk, } from './download/model-downloader.js';
|
|
12
|
+
export { downloadModel, downloadFile, fetchJson, ensureModel, getModelFilePath, isModelDownloaded, deleteModelFromDisk, collectModelFiles, } from './download/model-downloader.js';
|
|
13
13
|
export type { DownloadProgressCallback } from './download/model-downloader.js';
|
|
14
14
|
export { ModelDownloadService } from './download/model-download-service.js';
|
|
15
15
|
export { PythonEnvManager } from './python/python-env-manager.js';
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_chunk = require("./chunk-Cek0wNdY.js");
|
|
3
|
-
const require_model_download_service = require("./model-download-service-
|
|
3
|
+
const require_model_download_service = require("./model-download-service-D-Umz4-L.js");
|
|
4
4
|
const require_resource_monitor = require("./resource-monitor-DNNomR-i.js");
|
|
5
5
|
const require_builtins_sqlite_storage_filesystem_storage_addon = require("./builtins/sqlite-storage/filesystem-storage.addon.js");
|
|
6
6
|
const require_builtins_sqlite_storage_sqlite_settings_addon = require("./builtins/sqlite-storage/sqlite-settings.addon.js");
|
|
@@ -20,7 +20,7 @@ const require_builtins_local_auth_local_auth_addon = require("./builtins/local-a
|
|
|
20
20
|
require("./builtins/local-auth/index.js");
|
|
21
21
|
const require_builtins_device_manager_device_manager_addon = require("./builtins/device-manager/device-manager.addon.js");
|
|
22
22
|
require("./builtins/device-manager/index.js");
|
|
23
|
-
const require_manifest_python_deps = require("./manifest-python-deps-
|
|
23
|
+
const require_manifest_python_deps = require("./manifest-python-deps-RihGbmpb.js");
|
|
24
24
|
const require_custom_action_registry = require("./custom-action-registry-vLYEFTtv.js");
|
|
25
25
|
const require_graceful_fs$1 = require("./graceful-fs-lg19SZNz.js");
|
|
26
26
|
let _camstack_types_node = require("@camstack/types/node");
|
|
@@ -3284,6 +3284,66 @@ var AddonInstaller = class AddonInstaller {
|
|
|
3284
3284
|
node_fs.writeFileSync(tgzPath, buf);
|
|
3285
3285
|
return tgzPath;
|
|
3286
3286
|
}
|
|
3287
|
+
/**
|
|
3288
|
+
* Evict an existing install dir by atomic rename-aside, then best-effort
|
|
3289
|
+
* delete the aside copy.
|
|
3290
|
+
*
|
|
3291
|
+
* Why not a plain `fs.promises.rm(targetDir, {recursive})`: during a
|
|
3292
|
+
* `camstack deploy` the addon's live runner still holds its native
|
|
3293
|
+
* prebuilds (`.node`/`.so`) open. On the hub's shfs/FUSE `/data/addons`,
|
|
3294
|
+
* a recursive rm of a held subdir fails `ENOTEMPTY` mid-walk and leaves
|
|
3295
|
+
* the dir gutted. A rename only touches the dirent (metadata), so it
|
|
3296
|
+
* succeeds with the inodes still open — the old runner keeps its fds on
|
|
3297
|
+
* the moved inodes until it restarts.
|
|
3298
|
+
*
|
|
3299
|
+
* The aside copy is removed best-effort: the held native libs are only
|
|
3300
|
+
* released once the post-install `restartAddon` recycles the runner, so a
|
|
3301
|
+
* failure to delete it now must NOT fail the install. Any residue is swept
|
|
3302
|
+
* on the next deploy.
|
|
3303
|
+
*/
|
|
3304
|
+
async evictInstallDir(targetDir) {
|
|
3305
|
+
if (!node_fs.existsSync(targetDir)) return;
|
|
3306
|
+
const graveyard = node_path.join(this.addonsDir, ".evicting");
|
|
3307
|
+
ensureDir(graveyard);
|
|
3308
|
+
await this.sweepGraveyard(graveyard);
|
|
3309
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3310
|
+
const flatName = node_path.relative(this.addonsDir, targetDir).replace(/[/\\]/g, "-");
|
|
3311
|
+
const asideDir = node_path.join(graveyard, `${flatName}-${ts}`);
|
|
3312
|
+
try {
|
|
3313
|
+
node_fs.renameSync(targetDir, asideDir);
|
|
3314
|
+
} catch (renameErr) {
|
|
3315
|
+
if (renameErr.code === "EXDEV") {
|
|
3316
|
+
await node_fs.promises.rm(targetDir, {
|
|
3317
|
+
recursive: true,
|
|
3318
|
+
force: true
|
|
3319
|
+
});
|
|
3320
|
+
return;
|
|
3321
|
+
}
|
|
3322
|
+
throw renameErr;
|
|
3323
|
+
}
|
|
3324
|
+
await node_fs.promises.rm(asideDir, {
|
|
3325
|
+
recursive: true,
|
|
3326
|
+
force: true
|
|
3327
|
+
}).catch((cleanupErr) => {
|
|
3328
|
+
this.logger.debug("evictInstallDir: aside cleanup deferred (likely held open)", { meta: {
|
|
3329
|
+
asideDir,
|
|
3330
|
+
error: (0, _camstack_types.errMsg)(cleanupErr)
|
|
3331
|
+
} });
|
|
3332
|
+
});
|
|
3333
|
+
}
|
|
3334
|
+
/** Best-effort removal of leftover `.evicting/*` dirs from earlier deploys. */
|
|
3335
|
+
async sweepGraveyard(graveyard) {
|
|
3336
|
+
let entries;
|
|
3337
|
+
try {
|
|
3338
|
+
entries = node_fs.readdirSync(graveyard);
|
|
3339
|
+
} catch {
|
|
3340
|
+
return;
|
|
3341
|
+
}
|
|
3342
|
+
await Promise.all(entries.map((name) => node_fs.promises.rm(node_path.join(graveyard, name), {
|
|
3343
|
+
recursive: true,
|
|
3344
|
+
force: true
|
|
3345
|
+
}).catch(() => void 0)));
|
|
3346
|
+
}
|
|
3287
3347
|
/** Install addon from a tgz file (uploaded or downloaded) */
|
|
3288
3348
|
async installFromTgz(tgzPath) {
|
|
3289
3349
|
const tmpDir = node_fs.mkdtempSync(node_path.join(node_os.tmpdir(), "camstack-addon-install-"));
|
|
@@ -3301,10 +3361,7 @@ var AddonInstaller = class AddonInstaller {
|
|
|
3301
3361
|
if (!pkgView) throw new Error(`Invalid package.json at ${pkgJsonPath}`);
|
|
3302
3362
|
if (!pkgView.camstackAddons) throw new Error(`Package ${pkgView.name} has no camstack.addons manifest`);
|
|
3303
3363
|
const targetDir = node_path.join(this.addonsDir, pkgView.name);
|
|
3304
|
-
await
|
|
3305
|
-
recursive: true,
|
|
3306
|
-
force: true
|
|
3307
|
-
});
|
|
3364
|
+
await this.evictInstallDir(targetDir);
|
|
3308
3365
|
ensureDir(targetDir);
|
|
3309
3366
|
const sourceDir = node_path.dirname(pkgJsonPath);
|
|
3310
3367
|
const strippedManifest = stripCamstackDeps(pkgView.raw);
|
|
@@ -89904,6 +89961,15 @@ function normalizeHubUrl(raw, defaultPort) {
|
|
|
89904
89961
|
return `${/:\d+$/.test(hostPort) ? hostPort : `${hostPort}:${defaultPort}`}/${nodeId}`;
|
|
89905
89962
|
}
|
|
89906
89963
|
//#endregion
|
|
89964
|
+
//#region src/kernel/moleculer/addon-deploy-source.ts
|
|
89965
|
+
function isAddonDeploySource(v) {
|
|
89966
|
+
if (!v || typeof v !== "object") return false;
|
|
89967
|
+
const o = v;
|
|
89968
|
+
if (o["kind"] === "npm") return typeof o["version"] === "string";
|
|
89969
|
+
if (o["kind"] === "hub-http") return typeof o["url"] === "string" && typeof o["token"] === "string" && typeof o["sha256"] === "string" && typeof o["bytes"] === "number";
|
|
89970
|
+
return false;
|
|
89971
|
+
}
|
|
89972
|
+
//#endregion
|
|
89907
89973
|
//#region src/kernel/moleculer/core-cap-service.ts
|
|
89908
89974
|
/**
|
|
89909
89975
|
* Default Moleculer service name for the hub core-capability bridge.
|
|
@@ -91636,6 +91702,7 @@ exports.capServiceName = require_manifest_python_deps.capServiceName;
|
|
|
91636
91702
|
exports.classifyCapRoute = require_manifest_python_deps.classifyCapRoute;
|
|
91637
91703
|
exports.clearPendingRestart = clearPendingRestart;
|
|
91638
91704
|
exports.clusterSecretMatches = clusterSecretMatches;
|
|
91705
|
+
exports.collectModelFiles = require_model_download_service.collectModelFiles;
|
|
91639
91706
|
exports.contentTypeFor = require_model_download_service.contentTypeFor;
|
|
91640
91707
|
exports.copyDirRecursive = copyDirRecursive;
|
|
91641
91708
|
exports.copyExtraFileDirs = copyExtraFileDirs;
|
|
@@ -91755,6 +91822,7 @@ Object.defineProperty(exports, "installPythonRequirements", {
|
|
|
91755
91822
|
});
|
|
91756
91823
|
exports.ipcChildLink = require_manifest_python_deps.ipcChildLink;
|
|
91757
91824
|
exports.ipcParentLink = require_manifest_python_deps.ipcParentLink;
|
|
91825
|
+
exports.isAddonDeploySource = isAddonDeploySource;
|
|
91758
91826
|
exports.isClusterSecretMismatchError = isClusterSecretMismatchError;
|
|
91759
91827
|
exports.isInfraCapability = isInfraCapability;
|
|
91760
91828
|
exports.isModelDownloaded = require_model_download_service.isModelDownloaded;
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as __toCommonJS, i as __require, n as __esmMin, o as __toESM$1, r as __exportAll, t as __commonJSMin$1 } from "./chunk-CNf5ZN-e.mjs";
|
|
2
|
-
import { a as
|
|
2
|
+
import { a as downloadModel, c as getModelFilePath, d as contentTypeFor, f as createAuthenticatedFileServer, h as resolveFilePath, i as downloadFile, l as isModelDownloaded, m as parseTokenizedUrl, n as collectModelFiles, o as ensureModel, p as parseRangeHeader, r as deleteModelFromDisk, s as fetchJson, t as ModelDownloadService, u as createFileDataPlaneHandler } from "./model-download-service-C-IHWnXx.mjs";
|
|
3
3
|
import { n as getSinglePidStats, t as getPidStats } from "./resource-monitor-BkP504Vq.mjs";
|
|
4
4
|
import { FilesystemStorageAddon, t as FilesystemStorageProvider } from "./builtins/sqlite-storage/filesystem-storage.addon.mjs";
|
|
5
5
|
import { SqliteSettingsAddon, t as SqliteSettingsBackend } from "./builtins/sqlite-storage/sqlite-settings.addon.mjs";
|
|
@@ -19,7 +19,7 @@ import { LocalAuthAddon, a as require_safe_buffer, i as AuthManager, n as ApiKey
|
|
|
19
19
|
import "./builtins/local-auth/index.mjs";
|
|
20
20
|
import { DeviceManagerAddon } from "./builtins/device-manager/device-manager.addon.mjs";
|
|
21
21
|
import "./builtins/device-manager/index.mjs";
|
|
22
|
-
import { A as createUdsLoggerWithControl, B as createLocalTransport, C as ipcParentLink, D as createUdsEventBus, E as createUdsEventBridge, F as
|
|
22
|
+
import { A as createUdsLoggerWithControl, B as createLocalTransport, C as ipcParentLink, D as createUdsEventBus, E as createUdsEventBridge, F as AGENT_CAP_FWD_SERVICE, G as FrameDecoder, H as UdsLocalTransportServer, I as CapRouteResolver, J as buildUdsNativeCapProxy, K as encodeFrame, L as CapRouteError, M as LocalChildRegistry, N as UDS_NO_ROUTE_PREFIX, O as udsChildLogToWorkerEntry, P as AGENT_CAP_FWD_ACTION, Q as mountNativeCapService, R as classifyCapRoute, S as ipcChildLink, T as createParentUnownedCallHandler, U as SocketChannel, V as UdsLocalTransportClient, W as localEndpointPath, Y as createBrokerDeviceManagerApi, _ as __resetCapUsageRegistryForTests, a as getWorkerDeviceRegistry, at as capBareAction, b as brokerTransportLink, c as setHubConnected, ct as deserializeTypedArrays, d as registerEventBusService, dt as CapabilityHandle, et as createAddonService, f as AddonDepsManager, ft as CapabilityUnavailableError, g as CapUsageRegistry, h as createHwAccelService, i as createUdsAddonContext, it as capActionSuffix, j as LocalChildClient, k as createUdsLogger, l as EVENT_TOPIC_PREFIX, lt as serializeTypedArrays, m as resolveHwAccel, mt as resolveAddonClass, n as adaptBrokerToCluster, nt as NATIVE_PROVIDER_SERVICE_INFIX, o as getOrInitReadinessRegistry, ot as capServiceName, p as createKernelHwAccel, pt as installManifestNativeDeps, q as buildNativeCapProxy, r as createAddonContext, rt as capActionName, s as getOrInitReadinessRegistryForClient, st as parseCapAction, t as installManifestPythonDeps, tt as validateProviderRegistrations, u as getBrokerEventBus, ut as DeviceRegistry, v as getCapUsageRegistry, w as localProviderLink, x as buildLinkChain, y as brokerCallForCap, z as callWithServiceDiscovery } from "./manifest-python-deps-8Wvwz-d1.mjs";
|
|
23
23
|
import { t as CustomActionRegistry } from "./custom-action-registry-BEXwC-oo.mjs";
|
|
24
24
|
import { n as require_src, t as require_graceful_fs } from "./graceful-fs-BoR9GuPS.mjs";
|
|
25
25
|
import { PYTHON_VERSION, buildBinaryPath, downloadBinary, ensureBinary, ensureFfmpeg, ensurePython, findInPath, getFfmpegDownloadUrl, getPlatformInfo, getPythonDownloadUrl, installPythonPackages, installPythonRequirements } from "@camstack/types/node";
|
|
@@ -3278,6 +3278,66 @@ var AddonInstaller = class AddonInstaller {
|
|
|
3278
3278
|
fs$17.writeFileSync(tgzPath, buf);
|
|
3279
3279
|
return tgzPath;
|
|
3280
3280
|
}
|
|
3281
|
+
/**
|
|
3282
|
+
* Evict an existing install dir by atomic rename-aside, then best-effort
|
|
3283
|
+
* delete the aside copy.
|
|
3284
|
+
*
|
|
3285
|
+
* Why not a plain `fs.promises.rm(targetDir, {recursive})`: during a
|
|
3286
|
+
* `camstack deploy` the addon's live runner still holds its native
|
|
3287
|
+
* prebuilds (`.node`/`.so`) open. On the hub's shfs/FUSE `/data/addons`,
|
|
3288
|
+
* a recursive rm of a held subdir fails `ENOTEMPTY` mid-walk and leaves
|
|
3289
|
+
* the dir gutted. A rename only touches the dirent (metadata), so it
|
|
3290
|
+
* succeeds with the inodes still open — the old runner keeps its fds on
|
|
3291
|
+
* the moved inodes until it restarts.
|
|
3292
|
+
*
|
|
3293
|
+
* The aside copy is removed best-effort: the held native libs are only
|
|
3294
|
+
* released once the post-install `restartAddon` recycles the runner, so a
|
|
3295
|
+
* failure to delete it now must NOT fail the install. Any residue is swept
|
|
3296
|
+
* on the next deploy.
|
|
3297
|
+
*/
|
|
3298
|
+
async evictInstallDir(targetDir) {
|
|
3299
|
+
if (!fs$17.existsSync(targetDir)) return;
|
|
3300
|
+
const graveyard = path$40.join(this.addonsDir, ".evicting");
|
|
3301
|
+
ensureDir(graveyard);
|
|
3302
|
+
await this.sweepGraveyard(graveyard);
|
|
3303
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
3304
|
+
const flatName = path$40.relative(this.addonsDir, targetDir).replace(/[/\\]/g, "-");
|
|
3305
|
+
const asideDir = path$40.join(graveyard, `${flatName}-${ts}`);
|
|
3306
|
+
try {
|
|
3307
|
+
fs$17.renameSync(targetDir, asideDir);
|
|
3308
|
+
} catch (renameErr) {
|
|
3309
|
+
if (renameErr.code === "EXDEV") {
|
|
3310
|
+
await fs$17.promises.rm(targetDir, {
|
|
3311
|
+
recursive: true,
|
|
3312
|
+
force: true
|
|
3313
|
+
});
|
|
3314
|
+
return;
|
|
3315
|
+
}
|
|
3316
|
+
throw renameErr;
|
|
3317
|
+
}
|
|
3318
|
+
await fs$17.promises.rm(asideDir, {
|
|
3319
|
+
recursive: true,
|
|
3320
|
+
force: true
|
|
3321
|
+
}).catch((cleanupErr) => {
|
|
3322
|
+
this.logger.debug("evictInstallDir: aside cleanup deferred (likely held open)", { meta: {
|
|
3323
|
+
asideDir,
|
|
3324
|
+
error: errMsg(cleanupErr)
|
|
3325
|
+
} });
|
|
3326
|
+
});
|
|
3327
|
+
}
|
|
3328
|
+
/** Best-effort removal of leftover `.evicting/*` dirs from earlier deploys. */
|
|
3329
|
+
async sweepGraveyard(graveyard) {
|
|
3330
|
+
let entries;
|
|
3331
|
+
try {
|
|
3332
|
+
entries = fs$17.readdirSync(graveyard);
|
|
3333
|
+
} catch {
|
|
3334
|
+
return;
|
|
3335
|
+
}
|
|
3336
|
+
await Promise.all(entries.map((name) => fs$17.promises.rm(path$40.join(graveyard, name), {
|
|
3337
|
+
recursive: true,
|
|
3338
|
+
force: true
|
|
3339
|
+
}).catch(() => void 0)));
|
|
3340
|
+
}
|
|
3281
3341
|
/** Install addon from a tgz file (uploaded or downloaded) */
|
|
3282
3342
|
async installFromTgz(tgzPath) {
|
|
3283
3343
|
const tmpDir = fs$17.mkdtempSync(path$40.join(os$17.tmpdir(), "camstack-addon-install-"));
|
|
@@ -3295,10 +3355,7 @@ var AddonInstaller = class AddonInstaller {
|
|
|
3295
3355
|
if (!pkgView) throw new Error(`Invalid package.json at ${pkgJsonPath}`);
|
|
3296
3356
|
if (!pkgView.camstackAddons) throw new Error(`Package ${pkgView.name} has no camstack.addons manifest`);
|
|
3297
3357
|
const targetDir = path$40.join(this.addonsDir, pkgView.name);
|
|
3298
|
-
await
|
|
3299
|
-
recursive: true,
|
|
3300
|
-
force: true
|
|
3301
|
-
});
|
|
3358
|
+
await this.evictInstallDir(targetDir);
|
|
3302
3359
|
ensureDir(targetDir);
|
|
3303
3360
|
const sourceDir = path$40.dirname(pkgJsonPath);
|
|
3304
3361
|
const strippedManifest = stripCamstackDeps(pkgView.raw);
|
|
@@ -89898,6 +89955,15 @@ function normalizeHubUrl(raw, defaultPort) {
|
|
|
89898
89955
|
return `${/:\d+$/.test(hostPort) ? hostPort : `${hostPort}:${defaultPort}`}/${nodeId}`;
|
|
89899
89956
|
}
|
|
89900
89957
|
//#endregion
|
|
89958
|
+
//#region src/kernel/moleculer/addon-deploy-source.ts
|
|
89959
|
+
function isAddonDeploySource(v) {
|
|
89960
|
+
if (!v || typeof v !== "object") return false;
|
|
89961
|
+
const o = v;
|
|
89962
|
+
if (o["kind"] === "npm") return typeof o["version"] === "string";
|
|
89963
|
+
if (o["kind"] === "hub-http") return typeof o["url"] === "string" && typeof o["token"] === "string" && typeof o["sha256"] === "string" && typeof o["bytes"] === "number";
|
|
89964
|
+
return false;
|
|
89965
|
+
}
|
|
89966
|
+
//#endregion
|
|
89901
89967
|
//#region src/kernel/moleculer/core-cap-service.ts
|
|
89902
89968
|
/**
|
|
89903
89969
|
* Default Moleculer service name for the hub core-capability bridge.
|
|
@@ -91492,4 +91558,4 @@ async function stageFrameworkLockstep(input) {
|
|
|
91492
91558
|
return results;
|
|
91493
91559
|
}
|
|
91494
91560
|
//#endregion
|
|
91495
|
-
export { AGENT_CAP_FWD_ACTION, AGENT_CAP_FWD_SERVICE, AddonApiFactory, AddonDepsManager, AddonEngineManager, AddonHealthMonitor, AddonInstaller, AddonLoader, AddonManifest, AddonRouteRegistry, AlertCenterAddon, ApiKeyManager, AuthManager, CLUSTER_SECRET_MISMATCH_TYPE, CLUSTER_SECRET_REJECTED_EXIT_CODE, CORE_CAP_SERVICE_NAME, CapRouteError, CapRouteResolver, CapUsageRegistry, CapabilityHandle, CapabilityRegistry, CapabilityUnavailableError, ConfigManager, ConfigStore, ConsoleDestination, ConsoleLoggingAddon, CustomActionRegistry, DEFAULT_DATA_PATH, DataPlaneRegistry, DeviceManagerAddon, DeviceRegistry, DeviceStore, EVENT_TOPIC_PREFIX, EngineManagerResolver, EventBus, FRAMEWORK_LOCKSTEP, FeatureManager, FilesystemStorageAddon, FilesystemStorageProvider, FrameDecoder, FsStorageBackend, HEALTH_MONITOR_GRACE_PERIOD_MS, HEALTH_MONITOR_RETRY_INTERVALS_MS, HEALTH_MONITOR_TICK_MS, HubForwarderAddon, HubForwarderDestination, HubLogForwarder, HubNodeRegistry, INFRA_CAPABILITIES, IntegrationRegistry, JobJournal, LifecycleJobEngine, LifecycleStateMachine, LocalAuthAddon, LocalChildClient, LocalChildRegistry, LogManager, LogRingBuffer, ModelDownloadService, NATIVE_PROVIDER_SERVICE_INFIX, NativeMetricsAddon, NativeMetricsProvider, NetworkQualityTracker, NotificationService, PYTHON_VERSION, PipelineRunner, PipelineValidator, PythonEnvManager, RESTART_MARKER_FILE, RUNTIME_DEFAULTS, ReadinessRegistry, ReadinessTimeoutError, ReplEngine, RingBuffer, ScopedLogger, ScopedTokenManager, SocketChannel, SqliteSettingsAddon, SqliteSettingsBackend, StagingArea, StorageLocationManager, StorageManager, StorageOrchestratorAddon, StorageOrchestratorService, SystemConfigAddon, SystemEventBus, ToastService, UDS_NO_ROUTE_PREFIX, UdsLocalTransportClient, UdsLocalTransportServer, UserManager, WinstonDestination, WinstonLoggingAddon, __resetCapUsageRegistryForTests, adaptBrokerToCluster, bootstrapSchema, brokerCallForCap, brokerTransportLink, buildBinaryPath, buildLinkChain, buildNativeCapProxy, buildNodeManifest, buildStorageLocationRegistry, buildUdsNativeCapProxy, callRegisterNodeWithRetry, callWithServiceDiscovery, capActionName, capActionSuffix, capBareAction, capServiceName, classifyCapRoute, clearPendingRestart, clusterSecretMatches, contentTypeFor, copyDirRecursive, copyExtraFileDirs, createAddonContext, createAddonService, createAuthenticatedFileServer, createBroker, createBrokerDeviceManagerApi, createCoreCapService, createFileDataPlaneHandler, createHubService, createHwAccelService, createKernelHwAccel, createLocalTransport, createParentUnownedCallHandler, createProcessService, createReadinessService, createReadinessServiceForRegistry, createScopedProcessManager, createStreamProbeBrokerService, createUdsAddonContext, createUdsEventBridge, createUdsEventBus, createUdsLogger, createUdsLoggerWithControl, deleteModelFromDisk, deriveAgentListenPort, describeProviderKindDrift, detectWorkspacePackagesDir, downloadBinary, downloadFile, downloadModel, emitDownForOwnedCaps, encodeFrame, ensureBinary, ensureDir, ensureFfmpeg, ensureLibraryBuilt, ensureModel, ensurePython, ensureTlsCert, fetchJson, findInPath, formatLogLine, getBrokerEventBus, getCapUsageRegistry, getFfmpegDownloadUrl, getModelFilePath, getOrInitReadinessRegistry, getOrInitReadinessRegistryForClient, getPidStats, getPlatformInfo, getPythonDownloadUrl, getRestartMarkerPath, getSinglePidStats, getWorkerDeviceRegistry, hashClusterSecret, installManifestNativeDeps, installManifestPythonDeps, installPackageFromNpm, installPythonPackages, installPythonRequirements, ipcChildLink, ipcParentLink, isClusterSecretMismatchError, isInfraCapability, isModelDownloaded, isSourceNewer, loadTlsCert, localEndpointPath, localProviderLink, mountNativeCapService, parseCapAction, parseRangeHeader, parseTokenizedUrl, proxyToUpstream, readPendingRestart, readinessKey, registerEventBusService, resolveFilePath, resolveHwAccel, scheduleSelfRestart, scopeKey, scopesAllowDeviceCap, serializeTypedArrays, setHubConnected, stageFrameworkLockstep, stripCamstackDeps, udsChildLogToWorkerEntry, validateProviderRegistrations, writePendingRestart };
|
|
91561
|
+
export { AGENT_CAP_FWD_ACTION, AGENT_CAP_FWD_SERVICE, AddonApiFactory, AddonDepsManager, AddonEngineManager, AddonHealthMonitor, AddonInstaller, AddonLoader, AddonManifest, AddonRouteRegistry, AlertCenterAddon, ApiKeyManager, AuthManager, CLUSTER_SECRET_MISMATCH_TYPE, CLUSTER_SECRET_REJECTED_EXIT_CODE, CORE_CAP_SERVICE_NAME, CapRouteError, CapRouteResolver, CapUsageRegistry, CapabilityHandle, CapabilityRegistry, CapabilityUnavailableError, ConfigManager, ConfigStore, ConsoleDestination, ConsoleLoggingAddon, CustomActionRegistry, DEFAULT_DATA_PATH, DataPlaneRegistry, DeviceManagerAddon, DeviceRegistry, DeviceStore, EVENT_TOPIC_PREFIX, EngineManagerResolver, EventBus, FRAMEWORK_LOCKSTEP, FeatureManager, FilesystemStorageAddon, FilesystemStorageProvider, FrameDecoder, FsStorageBackend, HEALTH_MONITOR_GRACE_PERIOD_MS, HEALTH_MONITOR_RETRY_INTERVALS_MS, HEALTH_MONITOR_TICK_MS, HubForwarderAddon, HubForwarderDestination, HubLogForwarder, HubNodeRegistry, INFRA_CAPABILITIES, IntegrationRegistry, JobJournal, LifecycleJobEngine, LifecycleStateMachine, LocalAuthAddon, LocalChildClient, LocalChildRegistry, LogManager, LogRingBuffer, ModelDownloadService, NATIVE_PROVIDER_SERVICE_INFIX, NativeMetricsAddon, NativeMetricsProvider, NetworkQualityTracker, NotificationService, PYTHON_VERSION, PipelineRunner, PipelineValidator, PythonEnvManager, RESTART_MARKER_FILE, RUNTIME_DEFAULTS, ReadinessRegistry, ReadinessTimeoutError, ReplEngine, RingBuffer, ScopedLogger, ScopedTokenManager, SocketChannel, SqliteSettingsAddon, SqliteSettingsBackend, StagingArea, StorageLocationManager, StorageManager, StorageOrchestratorAddon, StorageOrchestratorService, SystemConfigAddon, SystemEventBus, ToastService, UDS_NO_ROUTE_PREFIX, UdsLocalTransportClient, UdsLocalTransportServer, UserManager, WinstonDestination, WinstonLoggingAddon, __resetCapUsageRegistryForTests, adaptBrokerToCluster, bootstrapSchema, brokerCallForCap, brokerTransportLink, buildBinaryPath, buildLinkChain, buildNativeCapProxy, buildNodeManifest, buildStorageLocationRegistry, buildUdsNativeCapProxy, callRegisterNodeWithRetry, callWithServiceDiscovery, capActionName, capActionSuffix, capBareAction, capServiceName, classifyCapRoute, clearPendingRestart, clusterSecretMatches, collectModelFiles, contentTypeFor, copyDirRecursive, copyExtraFileDirs, createAddonContext, createAddonService, createAuthenticatedFileServer, createBroker, createBrokerDeviceManagerApi, createCoreCapService, createFileDataPlaneHandler, createHubService, createHwAccelService, createKernelHwAccel, createLocalTransport, createParentUnownedCallHandler, createProcessService, createReadinessService, createReadinessServiceForRegistry, createScopedProcessManager, createStreamProbeBrokerService, createUdsAddonContext, createUdsEventBridge, createUdsEventBus, createUdsLogger, createUdsLoggerWithControl, deleteModelFromDisk, deriveAgentListenPort, describeProviderKindDrift, detectWorkspacePackagesDir, downloadBinary, downloadFile, downloadModel, emitDownForOwnedCaps, encodeFrame, ensureBinary, ensureDir, ensureFfmpeg, ensureLibraryBuilt, ensureModel, ensurePython, ensureTlsCert, fetchJson, findInPath, formatLogLine, getBrokerEventBus, getCapUsageRegistry, getFfmpegDownloadUrl, getModelFilePath, getOrInitReadinessRegistry, getOrInitReadinessRegistryForClient, getPidStats, getPlatformInfo, getPythonDownloadUrl, getRestartMarkerPath, getSinglePidStats, getWorkerDeviceRegistry, hashClusterSecret, installManifestNativeDeps, installManifestPythonDeps, installPackageFromNpm, installPythonPackages, installPythonRequirements, ipcChildLink, ipcParentLink, isAddonDeploySource, isClusterSecretMismatchError, isInfraCapability, isModelDownloaded, isSourceNewer, loadTlsCert, localEndpointPath, localProviderLink, mountNativeCapService, parseCapAction, parseRangeHeader, parseTokenizedUrl, proxyToUpstream, readPendingRestart, readinessKey, registerEventBusService, resolveFilePath, resolveHwAccel, scheduleSelfRestart, scopeKey, scopesAllowDeviceCap, serializeTypedArrays, setHubConnected, stageFrameworkLockstep, stripCamstackDeps, udsChildLogToWorkerEntry, validateProviderRegistrations, writePendingRestart };
|
|
@@ -167,6 +167,26 @@ export declare class AddonInstaller {
|
|
|
167
167
|
* the legacy fallback path remains the right answer for those.
|
|
168
168
|
*/
|
|
169
169
|
private downloadNpmTarball;
|
|
170
|
+
/**
|
|
171
|
+
* Evict an existing install dir by atomic rename-aside, then best-effort
|
|
172
|
+
* delete the aside copy.
|
|
173
|
+
*
|
|
174
|
+
* Why not a plain `fs.promises.rm(targetDir, {recursive})`: during a
|
|
175
|
+
* `camstack deploy` the addon's live runner still holds its native
|
|
176
|
+
* prebuilds (`.node`/`.so`) open. On the hub's shfs/FUSE `/data/addons`,
|
|
177
|
+
* a recursive rm of a held subdir fails `ENOTEMPTY` mid-walk and leaves
|
|
178
|
+
* the dir gutted. A rename only touches the dirent (metadata), so it
|
|
179
|
+
* succeeds with the inodes still open — the old runner keeps its fds on
|
|
180
|
+
* the moved inodes until it restarts.
|
|
181
|
+
*
|
|
182
|
+
* The aside copy is removed best-effort: the held native libs are only
|
|
183
|
+
* released once the post-install `restartAddon` recycles the runner, so a
|
|
184
|
+
* failure to delete it now must NOT fail the install. Any residue is swept
|
|
185
|
+
* on the next deploy.
|
|
186
|
+
*/
|
|
187
|
+
private evictInstallDir;
|
|
188
|
+
/** Best-effort removal of leftover `.evicting/*` dirs from earlier deploys. */
|
|
189
|
+
private sweepGraveyard;
|
|
170
190
|
/** Install addon from a tgz file (uploaded or downloaded) */
|
|
171
191
|
installFromTgz(tgzPath: string): Promise<{
|
|
172
192
|
name: string;
|
package/dist/kernel/index.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ export { buildStorageLocationRegistry } from './storage-location-registry.js';
|
|
|
27
27
|
export type { StorageLocationRegistry } from './storage-location-registry.js';
|
|
28
28
|
export { createBroker, deriveAgentListenPort } from './moleculer/broker-factory.js';
|
|
29
29
|
export type { BrokerConfig } from './moleculer/broker-factory.js';
|
|
30
|
+
export { type AddonDeploySource, isAddonDeploySource } from './moleculer/addon-deploy-source.js';
|
|
30
31
|
export { createAddonService, validateProviderRegistrations, } from './moleculer/addon-service-factory.js';
|
|
31
32
|
export { createCoreCapService, CORE_CAP_SERVICE_NAME } from './moleculer/core-cap-service.js';
|
|
32
33
|
export type { CoreCapAction, CoreCapServiceOptions } from './moleculer/core-cap-service.js';
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where an agent should fetch an addon's bytes from for a `$agent.deploy`.
|
|
3
|
+
* `npm`: the agent runs its own AddonInstaller pull. `hub-http`: the agent
|
|
4
|
+
* streams the staged tgz from the hub's one-time-token bundle route.
|
|
5
|
+
*/
|
|
6
|
+
export type AddonDeploySource = {
|
|
7
|
+
readonly kind: 'npm';
|
|
8
|
+
readonly version: string;
|
|
9
|
+
} | {
|
|
10
|
+
readonly kind: 'hub-http';
|
|
11
|
+
readonly url: string;
|
|
12
|
+
readonly token: string;
|
|
13
|
+
readonly sha256: string;
|
|
14
|
+
readonly bytes: number;
|
|
15
|
+
};
|
|
16
|
+
export declare function isAddonDeploySource(v: unknown): v is AddonDeploySource;
|
|
@@ -19,6 +19,17 @@ import { LocalChildRegistry, CapCallInput } from '../transport/index.js';
|
|
|
19
19
|
export interface LocalProviderResolver {
|
|
20
20
|
/** First matching provider (active singleton or first collection member). */
|
|
21
21
|
getByName(capabilityName: string): unknown | null;
|
|
22
|
+
/**
|
|
23
|
+
* The node this resolver's providers run on (e.g. `'hub'`, `'hub/model-studio'`,
|
|
24
|
+
* `'little-unraid/model-studio'`). When set, {@link localProviderLink} defers a
|
|
25
|
+
* call carrying an explicit `nodePin` to a DIFFERENT node instead of
|
|
26
|
+
* short-circuiting to a co-located provider — so the pin routes to the
|
|
27
|
+
* intended node (parent → `onUnownedCall` → CapRouteResolver). Symmetric to
|
|
28
|
+
* the LocalChildRegistry sibling-skip: an explicit pin must win over a
|
|
29
|
+
* same-named co-located provider. Omit for legacy/in-process callers that
|
|
30
|
+
* never pin a cap they themselves host.
|
|
31
|
+
*/
|
|
32
|
+
localNodeId?: string;
|
|
22
33
|
}
|
|
23
34
|
/**
|
|
24
35
|
* A single tRPC cap call observation. Recorded by `localProviderLink`
|
|
@@ -105,6 +105,21 @@ export declare const AGENT_CAP_FWD_SERVICE: "$agent-cap-fwd";
|
|
|
105
105
|
/** The Moleculer action (service.action) for agent cap forwarding. */
|
|
106
106
|
export declare const AGENT_CAP_FWD_ACTION: "$agent-cap-fwd.forward";
|
|
107
107
|
export declare function extractDeviceId(args: unknown): number | undefined;
|
|
108
|
+
/**
|
|
109
|
+
* Extract an inline `nodeId` string from a cap call's args — the per-call node
|
|
110
|
+
* pin a forked addon expresses by carrying `nodeId` in the input (the SAME way
|
|
111
|
+
* device-scoped caps carry `deviceId`, and the way the generated cap-router on
|
|
112
|
+
* the hub already honours an inline pin). Mirrors {@link extractDeviceId}.
|
|
113
|
+
*
|
|
114
|
+
* This is the routing source for hub→remote-node EXECUTION pinning (e.g. the
|
|
115
|
+
* benchmark addon running a synthetic/decoder workload ON a chosen agent). The
|
|
116
|
+
* out-of-band `nodePin(op.context)` path does NOT survive the forked addon →
|
|
117
|
+
* hub UDS link chain, so `onUnownedCall` reads the inline pin instead. Provider
|
|
118
|
+
* methods that don't declare `nodeId` simply ignore the extra field (they
|
|
119
|
+
* destructure the fields they need); methods that DO declare it (e.g.
|
|
120
|
+
* `getEngineProvisioning({nodeId})`) still receive it unchanged.
|
|
121
|
+
*/
|
|
122
|
+
export declare function extractNodeId(args: unknown): string | undefined;
|
|
108
123
|
export declare class CapRouteResolver {
|
|
109
124
|
private readonly hubNodeId;
|
|
110
125
|
private readonly broker;
|