@camstack/system 1.2.61 → 1.2.63
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/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.js +1 -1
- package/dist/builtins/addon-pages-aggregator/addon-pages-aggregator.addon.mjs +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.js +1 -1
- package/dist/builtins/addon-widgets-aggregator/addon-widgets-aggregator.addon.mjs +1 -1
- package/dist/builtins/alerts/alerts.addon.js +1 -1
- package/dist/builtins/alerts/alerts.addon.mjs +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.js +1 -1
- package/dist/builtins/backup-orchestrator/backup-orchestrator.addon.mjs +1 -1
- package/dist/builtins/console-logging/index.js +1 -1
- package/dist/builtins/console-logging/index.mjs +1 -1
- package/dist/builtins/core-blocks/block-package.d.ts +9 -27
- package/dist/builtins/core-blocks/block-supervisor.d.ts +15 -0
- package/dist/builtins/core-blocks/core-blocks.addon.js +91 -24
- package/dist/builtins/core-blocks/core-blocks.addon.mjs +91 -25
- package/dist/builtins/device-manager/device-aggregation.d.ts +49 -2
- package/dist/builtins/device-manager/device-bindings-store.d.ts +28 -5
- package/dist/builtins/device-manager/device-linked-devices.d.ts +2 -2
- package/dist/builtins/device-manager/device-manager.addon.d.ts +3 -45
- package/dist/builtins/device-manager/device-manager.addon.js +182 -888
- package/dist/builtins/device-manager/device-manager.addon.mjs +182 -888
- package/dist/builtins/device-manager/device-meta-actions.d.ts +0 -8
- package/dist/builtins/device-manager/device-meta-store.d.ts +1 -28
- package/dist/builtins/device-manager/device-meta-types.d.ts +2 -6
- package/dist/builtins/device-manager/device-projection.d.ts +1 -2
- package/dist/builtins/device-manager/device-provider-context.d.ts +2 -13
- package/dist/builtins/device-manager/device-queries.d.ts +1 -1
- package/dist/builtins/device-manager/device-state-mirror.d.ts +8 -49
- package/dist/builtins/device-manager/device-sync-resolvers.d.ts +0 -11
- package/dist/builtins/doorbell/virtual-doorbell.addon.js +1 -1
- package/dist/builtins/doorbell/virtual-doorbell.addon.mjs +1 -1
- package/dist/builtins/hub-forwarder/index.js +1 -1
- package/dist/builtins/hub-forwarder/index.mjs +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.js +1 -1
- package/dist/builtins/liveness-monitor/liveness-monitor.addon.mjs +1 -1
- package/dist/builtins/local-auth/homeassistant-integration.d.ts +7 -0
- package/dist/builtins/local-auth/index.d.ts +1 -0
- package/dist/builtins/local-auth/index.js +1 -0
- package/dist/builtins/local-auth/index.mjs +2 -2
- package/dist/builtins/local-auth/local-auth.addon.js +561 -497
- package/dist/builtins/local-auth/local-auth.addon.mjs +580 -517
- package/dist/builtins/local-auth/oauth-grants.d.ts +11 -0
- package/dist/builtins/local-auth/oauth-session-manager.d.ts +1 -1
- package/dist/builtins/local-network/local-network.addon.js +1 -1
- package/dist/builtins/local-network/local-network.addon.mjs +1 -1
- package/dist/builtins/loki-logging/index.js +1 -1
- package/dist/builtins/loki-logging/index.mjs +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.js +1 -1
- package/dist/builtins/native-metrics/native-metrics.addon.mjs +1 -1
- package/dist/builtins/platform-probe/index.js +1 -1
- package/dist/builtins/platform-probe/index.mjs +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.js +1 -1
- package/dist/builtins/remote-access-orchestrator/remote-access-orchestrator.addon.mjs +1 -1
- package/dist/builtins/snapshot/index.js +319 -20
- package/dist/builtins/snapshot/index.mjs +319 -20
- package/dist/builtins/snapshot/snapshot-keep-warm.d.ts +86 -0
- package/dist/builtins/snapshot/snapshot-media-handler.d.ts +48 -1
- package/dist/builtins/snapshot/snapshot.addon.d.ts +65 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.js +1 -1
- package/dist/builtins/sqlite-storage/filesystem-storage.addon.mjs +1 -1
- package/dist/builtins/sqlite-storage/retired-settings-keys.d.ts +15 -0
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.js +45 -5
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +45 -5
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +1 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +1 -1
- package/dist/builtins/system-config/system-config.addon.js +1 -1
- package/dist/builtins/system-config/system-config.addon.mjs +1 -1
- package/dist/builtins/winston-logging/index.js +1 -1
- package/dist/builtins/winston-logging/index.mjs +1 -1
- package/dist/{dist-L6QPJWsr.mjs → dist-BJ2w3uoN.mjs} +1307 -1313
- package/dist/{dist-BN681PgO.js → dist-LDrOFrd-.js} +1255 -1273
- package/dist/index.d.ts +1 -1
- package/dist/index.js +209 -5
- package/dist/index.mjs +206 -7
- package/dist/kernel/addon-residue.d.ts +49 -0
- package/dist/kernel/index.d.ts +2 -0
- package/dist/kernel/moleculer/device-cap-proxy.d.ts +2 -2
- package/dist/{manifest-python-deps-CX5tGRqf.mjs → manifest-python-deps-CopL1_5C.mjs} +20 -14
- package/dist/{manifest-python-deps-d4acYXVV.js → manifest-python-deps-DNfmBu20.js} +20 -14
- package/package.json +5 -1
- package/dist/builtins/device-manager/device-link-cycle.d.ts +0 -18
- package/dist/builtins/device-manager/device-link-overlay.d.ts +0 -33
- package/dist/builtins/device-manager/device-link-resolver.d.ts +0 -44
- package/dist/main-CVLq8IzA.js +0 -2356
- package/dist/main-CzH19GkU.mjs +0 -2353
package/dist/index.d.ts
CHANGED
|
@@ -51,7 +51,7 @@ export { LivenessMonitorAddon } from './builtins/liveness-monitor/index.js';
|
|
|
51
51
|
export { NativeMetricsProvider } from './builtins/native-metrics/native-metrics-provider.js';
|
|
52
52
|
export { SystemConfigAddon } from './builtins/system-config/index.js';
|
|
53
53
|
export { CoreBlocksAddon } from './builtins/core-blocks/index.js';
|
|
54
|
-
export { LocalAuthAddon } from './builtins/local-auth/index.js';
|
|
54
|
+
export { LocalAuthAddon, HOME_ASSISTANT_OAUTH_INTEGRATION } from './builtins/local-auth/index.js';
|
|
55
55
|
export { DeviceManagerAddon } from './builtins/device-manager/index.js';
|
|
56
56
|
export { LifecycleStateMachine } from './lifecycle/lifecycle-state-machine.js';
|
|
57
57
|
export type { ElementState, ElementStatus, } from './lifecycle/lifecycle-state-machine.js';
|
package/dist/index.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
2
|
const require_chunk = require("./chunk-Cek0wNdY.js");
|
|
3
|
-
const require_dist = require("./dist-
|
|
3
|
+
const require_dist = require("./dist-LDrOFrd-.js");
|
|
4
4
|
const require_model_download_service = require("./model-download-service-Cpc6SWzV.js");
|
|
5
|
-
const require_manifest_python_deps = require("./manifest-python-deps-
|
|
5
|
+
const require_manifest_python_deps = require("./manifest-python-deps-DNfmBu20.js");
|
|
6
6
|
const require_resource_monitor = require("./resource-monitor-DNNomR-i.js");
|
|
7
7
|
const require_builtins_sqlite_storage_filesystem_storage_addon = require("./builtins/sqlite-storage/filesystem-storage.addon.js");
|
|
8
8
|
const require_builtins_sqlite_storage_sqlite_settings_addon = require("./builtins/sqlite-storage/sqlite-settings.addon.js");
|
|
@@ -3356,7 +3356,12 @@ var MANIFEST_VERSION = 1;
|
|
|
3356
3356
|
* workspace source. Trusting the dirent there finds zero addons on disk, which
|
|
3357
3357
|
* with pruning would empty a developer's manifest on the next boot.
|
|
3358
3358
|
*/
|
|
3359
|
-
|
|
3359
|
+
/** `@camstack/system` → `system`: the directory a package must live in. */
|
|
3360
|
+
function packageDirName$1(declaredName) {
|
|
3361
|
+
const slash = declaredName.lastIndexOf("/");
|
|
3362
|
+
return slash === -1 ? declaredName : declaredName.slice(slash + 1);
|
|
3363
|
+
}
|
|
3364
|
+
function isDirLike$1(p) {
|
|
3360
3365
|
try {
|
|
3361
3366
|
return node_fs.statSync(p).isDirectory();
|
|
3362
3367
|
} catch {
|
|
@@ -3490,10 +3495,10 @@ var AddonManifest = class {
|
|
|
3490
3495
|
/** Package name → version, for every ADDON package actually installed. */
|
|
3491
3496
|
const onDisk = /* @__PURE__ */ new Map();
|
|
3492
3497
|
for (const scope of node_fs.readdirSync(addonsDir, { withFileTypes: true })) {
|
|
3493
|
-
if (!scope.name.startsWith("@") || !isDirLike(node_path.join(addonsDir, scope.name))) continue;
|
|
3498
|
+
if (!scope.name.startsWith("@") || !isDirLike$1(node_path.join(addonsDir, scope.name))) continue;
|
|
3494
3499
|
const scopeDir = node_path.join(addonsDir, scope.name);
|
|
3495
3500
|
for (const pkg of node_fs.readdirSync(scopeDir, { withFileTypes: true })) {
|
|
3496
|
-
if (!isDirLike(node_path.join(scopeDir, pkg.name))) continue;
|
|
3501
|
+
if (!isDirLike$1(node_path.join(scopeDir, pkg.name))) continue;
|
|
3497
3502
|
const pkgJsonPath = node_path.join(scopeDir, pkg.name, "package.json");
|
|
3498
3503
|
if (!node_fs.existsSync(pkgJsonPath)) continue;
|
|
3499
3504
|
let pkgJson;
|
|
@@ -3507,6 +3512,7 @@ var AddonManifest = class {
|
|
|
3507
3512
|
if (!pkgJson.name || !pkgJson.version) continue;
|
|
3508
3513
|
const addons = pkgJson.camstack?.addons;
|
|
3509
3514
|
if (!Array.isArray(addons) || addons.length === 0) continue;
|
|
3515
|
+
if (packageDirName$1(pkgJson.name) !== pkg.name) continue;
|
|
3510
3516
|
onDisk.set(pkgJson.name, pkgJson.version);
|
|
3511
3517
|
}
|
|
3512
3518
|
}
|
|
@@ -5024,6 +5030,199 @@ function isDirEntry(entry, parentDir) {
|
|
|
5024
5030
|
return false;
|
|
5025
5031
|
}
|
|
5026
5032
|
//#endregion
|
|
5033
|
+
//#region src/kernel/addon-residue.ts
|
|
5034
|
+
/**
|
|
5035
|
+
* Residue quarantine for the ONE addon root.
|
|
5036
|
+
*
|
|
5037
|
+
* ── The bug class this closes ──────────────────────────────────────────────
|
|
5038
|
+
* `<dataDir>/addons/@camstack/<pkg>` is the only directory a node loads addons
|
|
5039
|
+
* from. Renaming a package inside it does NOT archive it — the directory name
|
|
5040
|
+
* is decoration, and three independent consumers key on the `package.json`
|
|
5041
|
+
* they find inside:
|
|
5042
|
+
*
|
|
5043
|
+
* - `AddonLoader.loadFromDirectory` walks `@scope/*` and loads anything with
|
|
5044
|
+
* a `package.json`; its skip list is keyed on the PACKAGE name, so
|
|
5045
|
+
* `@camstack/system.bak-pre-unshadow` was never skipped;
|
|
5046
|
+
* - the agent's `resolveAddonPackageDirs` scans on the same rule;
|
|
5047
|
+
* - `AddonManifest.reconcileFromDisk` does `onDisk.set(pkgJson.name, …)`,
|
|
5048
|
+
* so the backup's version becomes the version the manifest — and every
|
|
5049
|
+
* surface reading it — REPORTS.
|
|
5050
|
+
*
|
|
5051
|
+
* Measured 2026-08-07/08 on the live cluster:
|
|
5052
|
+
*
|
|
5053
|
+
* little-unraid `@camstack/system.bak-pre-unshadow` — no package.json left,
|
|
5054
|
+
* so not loadable, but the agent main process held five live
|
|
5055
|
+
* mappings of `better_sqlite3.node` out of it (D87).
|
|
5056
|
+
* macmini `@camstack/system.bak-pre-1272-1786129145` — still declares
|
|
5057
|
+
* `@camstack/system@1.2.3` (17 days old, 97 MB). The data root
|
|
5058
|
+
* has NO `addons/@camstack/system` directory at all, yet the
|
|
5059
|
+
* manifest reports `@camstack/system 1.2.3` — read straight
|
|
5060
|
+
* out of the backup. The live process still had the backup's
|
|
5061
|
+
* `better_sqlite3.node` mapped `txt` against a 1.2.61 closure.
|
|
5062
|
+
*
|
|
5063
|
+
* ── Why quarantine and not delete ─────────────────────────────────────────
|
|
5064
|
+
* The same evening, a cleanup that DELETED what it judged to be a shadow copy
|
|
5065
|
+
* removed the only builtins an agent had and gutted the node. So this never
|
|
5066
|
+
* deletes: it renames out of the scanned root into `.quarantine/`, logs every
|
|
5067
|
+
* move by name, reason and both paths, and leaves the bytes recoverable. A
|
|
5068
|
+
* silent sweep reads as "nothing was here", which is the failure mode that
|
|
5069
|
+
* made the original one unobservable.
|
|
5070
|
+
*
|
|
5071
|
+
* `.quarantine` sits beside `.staging` / `.backups` / `.evicting` under the
|
|
5072
|
+
* addon root: dot-prefixed, carrying no `package.json` at its top level, so no
|
|
5073
|
+
* scanner descends into it.
|
|
5074
|
+
*/
|
|
5075
|
+
/** Where residue is moved to, relative to the addon root. */
|
|
5076
|
+
var QUARANTINE_DIRNAME = ".quarantine";
|
|
5077
|
+
/**
|
|
5078
|
+
* A backup marker: a `.bak` / `.old` / `.orig` segment, not merely those
|
|
5079
|
+
* letters. `.backups` — the installer's own rollback store — must NOT match,
|
|
5080
|
+
* and it is also excluded by the dot rule below; the pattern stays tight
|
|
5081
|
+
* anyway so the two guards are independent.
|
|
5082
|
+
*/
|
|
5083
|
+
var BACKUP_MARKER = /\.(bak|old|orig)(?![a-z])/i;
|
|
5084
|
+
/** Installer-owned stores and node's own tree. Never candidates. */
|
|
5085
|
+
function isReservedDir(dirName) {
|
|
5086
|
+
return dirName.startsWith(".") || dirName === "node_modules";
|
|
5087
|
+
}
|
|
5088
|
+
/** `@camstack/system` → `system`; `foo` → `foo`. */
|
|
5089
|
+
function packageDirName(declaredName) {
|
|
5090
|
+
const slash = declaredName.lastIndexOf("/");
|
|
5091
|
+
return slash === -1 ? declaredName : declaredName.slice(slash + 1);
|
|
5092
|
+
}
|
|
5093
|
+
/**
|
|
5094
|
+
* Is this directory residue? Returns the verdict, or null to keep it.
|
|
5095
|
+
*
|
|
5096
|
+
* A directory with no `package.json` is NOT residue on that ground alone —
|
|
5097
|
+
* `models/`, a half-finished copy, a scratch dir are all absence, not a
|
|
5098
|
+
* rename. Only a backup marker condemns one.
|
|
5099
|
+
*/
|
|
5100
|
+
function classifyAddonDir(candidate) {
|
|
5101
|
+
const { dirName, declaredName } = candidate;
|
|
5102
|
+
if (isReservedDir(dirName)) return null;
|
|
5103
|
+
if (BACKUP_MARKER.test(dirName)) return {
|
|
5104
|
+
dirName,
|
|
5105
|
+
declaredName,
|
|
5106
|
+
reason: "backup-residue"
|
|
5107
|
+
};
|
|
5108
|
+
if (declaredName !== null && packageDirName(declaredName) !== dirName) return {
|
|
5109
|
+
dirName,
|
|
5110
|
+
declaredName,
|
|
5111
|
+
reason: "name-mismatch"
|
|
5112
|
+
};
|
|
5113
|
+
return null;
|
|
5114
|
+
}
|
|
5115
|
+
function selectAddonResidue(candidates) {
|
|
5116
|
+
const out = [];
|
|
5117
|
+
for (const candidate of candidates) {
|
|
5118
|
+
const verdict = classifyAddonDir(candidate);
|
|
5119
|
+
if (verdict !== null) out.push(verdict);
|
|
5120
|
+
}
|
|
5121
|
+
return out;
|
|
5122
|
+
}
|
|
5123
|
+
function readDeclaredName(dir) {
|
|
5124
|
+
try {
|
|
5125
|
+
const parsed = JSON.parse(node_fs.readFileSync(node_path.join(dir, "package.json"), "utf-8"));
|
|
5126
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
5127
|
+
const name = parsed.name;
|
|
5128
|
+
return typeof name === "string" ? name : null;
|
|
5129
|
+
} catch {
|
|
5130
|
+
return null;
|
|
5131
|
+
}
|
|
5132
|
+
}
|
|
5133
|
+
function readDeclaredVersion(dir) {
|
|
5134
|
+
try {
|
|
5135
|
+
const parsed = JSON.parse(node_fs.readFileSync(node_path.join(dir, "package.json"), "utf-8"));
|
|
5136
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
5137
|
+
const version = parsed.version;
|
|
5138
|
+
return typeof version === "string" ? version : null;
|
|
5139
|
+
} catch {
|
|
5140
|
+
return null;
|
|
5141
|
+
}
|
|
5142
|
+
}
|
|
5143
|
+
function isDirLike(p) {
|
|
5144
|
+
try {
|
|
5145
|
+
return node_fs.statSync(p).isDirectory();
|
|
5146
|
+
} catch {
|
|
5147
|
+
return false;
|
|
5148
|
+
}
|
|
5149
|
+
}
|
|
5150
|
+
/** `<scope>__<dir>`, suffixed until free — two sweeps must not overwrite. */
|
|
5151
|
+
function freeQuarantineTarget(store, scope, dirName) {
|
|
5152
|
+
const base = scope === null ? dirName : `${scope.replace("@", "")}__${dirName}`;
|
|
5153
|
+
let candidate = node_path.join(store, base);
|
|
5154
|
+
let n = 1;
|
|
5155
|
+
while (node_fs.existsSync(candidate)) {
|
|
5156
|
+
candidate = node_path.join(store, `${base}-${n}`);
|
|
5157
|
+
n += 1;
|
|
5158
|
+
}
|
|
5159
|
+
return candidate;
|
|
5160
|
+
}
|
|
5161
|
+
/**
|
|
5162
|
+
* Move every residue directory under `addonsDir` into `addonsDir/.quarantine`.
|
|
5163
|
+
*
|
|
5164
|
+
* Runs at boot, BEFORE anything scans the root or seeds into it — a residue
|
|
5165
|
+
* that is still present when the loader walks has already been installed.
|
|
5166
|
+
* Never throws, never deletes; a move that fails is reported and left in
|
|
5167
|
+
* place, which is strictly better than a half-moved package.
|
|
5168
|
+
*/
|
|
5169
|
+
function quarantineAddonResidue(addonsDir, log) {
|
|
5170
|
+
const quarantined = [];
|
|
5171
|
+
const failed = [];
|
|
5172
|
+
if (!node_fs.existsSync(addonsDir)) return {
|
|
5173
|
+
quarantined,
|
|
5174
|
+
failed
|
|
5175
|
+
};
|
|
5176
|
+
const store = node_path.join(addonsDir, QUARANTINE_DIRNAME);
|
|
5177
|
+
const scan = (parent, scope) => {
|
|
5178
|
+
let names;
|
|
5179
|
+
try {
|
|
5180
|
+
names = node_fs.readdirSync(parent);
|
|
5181
|
+
} catch {
|
|
5182
|
+
return;
|
|
5183
|
+
}
|
|
5184
|
+
for (const dirName of names) {
|
|
5185
|
+
const full = node_path.join(parent, dirName);
|
|
5186
|
+
if (!isDirLike(full)) continue;
|
|
5187
|
+
if (scope === null && dirName.startsWith("@")) {
|
|
5188
|
+
scan(full, dirName);
|
|
5189
|
+
continue;
|
|
5190
|
+
}
|
|
5191
|
+
const verdict = classifyAddonDir({
|
|
5192
|
+
dirName,
|
|
5193
|
+
declaredName: readDeclaredName(full)
|
|
5194
|
+
});
|
|
5195
|
+
if (verdict === null) continue;
|
|
5196
|
+
const version = readDeclaredVersion(full);
|
|
5197
|
+
const to = freeQuarantineTarget(store, scope, dirName);
|
|
5198
|
+
try {
|
|
5199
|
+
node_fs.mkdirSync(store, { recursive: true });
|
|
5200
|
+
node_fs.renameSync(full, to);
|
|
5201
|
+
} catch (err) {
|
|
5202
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
5203
|
+
failed.push({
|
|
5204
|
+
...verdict,
|
|
5205
|
+
from: full,
|
|
5206
|
+
error
|
|
5207
|
+
});
|
|
5208
|
+
log(`[addons] residue quarantine FAILED for ${full} (${verdict.reason}) — ${error}. It stays on the addon root and will be loaded, scanned and reported as an install.`);
|
|
5209
|
+
continue;
|
|
5210
|
+
}
|
|
5211
|
+
quarantined.push({
|
|
5212
|
+
...verdict,
|
|
5213
|
+
from: full,
|
|
5214
|
+
to
|
|
5215
|
+
});
|
|
5216
|
+
log(`[addons] quarantined ${verdict.reason} ${full} → ${to} (declares ${verdict.declaredName ?? "no package name"}${version === null ? "" : `@${version}`}). A renamed package dir is INSTALLED, not archived — nothing was deleted.`);
|
|
5217
|
+
}
|
|
5218
|
+
};
|
|
5219
|
+
scan(addonsDir, null);
|
|
5220
|
+
return {
|
|
5221
|
+
quarantined,
|
|
5222
|
+
failed
|
|
5223
|
+
};
|
|
5224
|
+
}
|
|
5225
|
+
//#endregion
|
|
5027
5226
|
//#region src/kernel/workspace-detect.ts
|
|
5028
5227
|
/**
|
|
5029
5228
|
* Detect whether we're running inside the monorepo workspace.
|
|
@@ -94687,6 +94886,7 @@ exports.HEAP_RECLAIM_MIN_INTERVAL_MS = HEAP_RECLAIM_MIN_INTERVAL_MS;
|
|
|
94687
94886
|
exports.HEAP_RECLAIM_TRIGGER_MB = HEAP_RECLAIM_TRIGGER_MB;
|
|
94688
94887
|
exports.HEAP_WATCH_INTERVAL_MS = HEAP_WATCH_INTERVAL_MS;
|
|
94689
94888
|
exports.HEAP_WATCH_WARN_RATIO = HEAP_WATCH_WARN_RATIO;
|
|
94889
|
+
exports.HOME_ASSISTANT_OAUTH_INTEGRATION = require_builtins_local_auth_local_auth_addon.HOME_ASSISTANT_OAUTH_INTEGRATION;
|
|
94690
94890
|
exports.HUB_CAP_FWD_ACTION = require_manifest_python_deps.HUB_CAP_FWD_ACTION;
|
|
94691
94891
|
exports.HUB_CAP_FWD_SERVICE = require_manifest_python_deps.HUB_CAP_FWD_SERVICE;
|
|
94692
94892
|
exports.HubForwarderAddon = require_builtins_hub_forwarder_index.HubForwarderAddon$1;
|
|
@@ -94723,6 +94923,7 @@ Object.defineProperty(exports, "PYTHON_VERSION", {
|
|
|
94723
94923
|
exports.PipelineRunner = PipelineRunner;
|
|
94724
94924
|
exports.PipelineValidator = PipelineValidator;
|
|
94725
94925
|
exports.PythonEnvManager = PythonEnvManager;
|
|
94926
|
+
exports.QUARANTINE_DIRNAME = QUARANTINE_DIRNAME;
|
|
94726
94927
|
exports.RESTART_MARKER_FILE = RESTART_MARKER_FILE;
|
|
94727
94928
|
exports.RUNTIME_DEFAULTS = require_dist.RUNTIME_DEFAULTS;
|
|
94728
94929
|
exports.ReadinessRegistry = require_dist.ReadinessRegistry;
|
|
@@ -94775,6 +94976,7 @@ exports.capActionName = require_manifest_python_deps.capActionName;
|
|
|
94775
94976
|
exports.capActionSuffix = require_manifest_python_deps.capActionSuffix;
|
|
94776
94977
|
exports.capBareAction = require_manifest_python_deps.capBareAction;
|
|
94777
94978
|
exports.capServiceName = require_manifest_python_deps.capServiceName;
|
|
94979
|
+
exports.classifyAddonDir = classifyAddonDir;
|
|
94778
94980
|
exports.classifyCapRoute = require_manifest_python_deps.classifyCapRoute;
|
|
94779
94981
|
exports.clearPendingRestart = clearPendingRestart;
|
|
94780
94982
|
exports.clusterEventTopic = require_manifest_python_deps.clusterEventTopic;
|
|
@@ -94916,6 +95118,7 @@ exports.parseCapAction = require_manifest_python_deps.parseCapAction;
|
|
|
94916
95118
|
exports.parseRangeHeader = require_model_download_service.parseRangeHeader;
|
|
94917
95119
|
exports.parseTokenizedUrl = require_model_download_service.parseTokenizedUrl;
|
|
94918
95120
|
exports.proxyToUpstream = proxyToUpstream;
|
|
95121
|
+
exports.quarantineAddonResidue = quarantineAddonResidue;
|
|
94919
95122
|
exports.readPendingRestart = readPendingRestart;
|
|
94920
95123
|
exports.readinessKey = require_dist.readinessKey;
|
|
94921
95124
|
exports.registerEventBusService = require_manifest_python_deps.registerEventBusService;
|
|
@@ -94926,6 +95129,7 @@ exports.runNpm = require_manifest_python_deps.runNpm;
|
|
|
94926
95129
|
exports.scheduleSelfRestart = scheduleSelfRestart;
|
|
94927
95130
|
exports.scopeKey = require_dist.scopeKey;
|
|
94928
95131
|
exports.scopesAllowDeviceCap = scopesAllowDeviceCap;
|
|
95132
|
+
exports.selectAddonResidue = selectAddonResidue;
|
|
94929
95133
|
exports.serializeTypedArrays = require_manifest_python_deps.serializeTypedArrays;
|
|
94930
95134
|
exports.setHubConnected = require_manifest_python_deps.setHubConnected;
|
|
94931
95135
|
exports.setNodeEventInterest = require_manifest_python_deps.setNodeEventInterest;
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
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 {
|
|
2
|
+
import { Ct as asString$1, Dt as expandCapMethods, F as extractNestedAddonId, Ft as scopeKey, H as kebabToCamel, K as logLevelAtMost, L as isArrayOutputSchema, Lt as EventCategory$1, Mt as parseJsonUnknown$1, Nt as readinessKey, Pt as resolveCapMount, R as isCollectionArrayMethod, St as asNumber, Tt as emitDownForOwnedCaps, U as lifecycleJobSchema, V as isVoidInput, Z as objectInputDeclaresAddonId, et as procedureAuthKey, ft as DATAPLANE_SECRET_HEADER$1, g as addonSettingsCapability, jt as parseJsonObject, l as METHOD_ACCESS_MAP, mt as DEVICE_STATUS_METHOD, pt as DEVICE_SETTINGS_CONTRIBUTION_METHODS, q as looseSchema, t as ALL_CAPABILITY_DEFINITIONS, u as RUNTIME_DEFAULTS, ut as errMsg$1, vt as ReadinessRegistry, wt as createEvent, xt as asJsonObject$1, yt as ReadinessTimeoutError, z as isObjectInput } from "./dist-BJ2w3uoN.mjs";
|
|
3
3
|
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-D8B-4ktF.mjs";
|
|
4
|
-
import { $ as buildNativeCapProxy, A as createHubCapForwardService, B as AGENT_CAP_FWD_ACTION, C as brokerTransportLink, Ct as resolveAddonClass, D as localProviderLink, E as ipcParentLink, F as createUdsLogger, G as callWithServiceDiscovery, H as CapRouteResolver, I as createUdsLoggerWithControl, J as UdsLocalTransportServer, K as createLocalTransport, L as LocalChildClient, M as createUdsEventBridge, N as createUdsEventBus, O as HUB_CAP_FWD_ACTION, P as udsChildLogToWorkerEntry, Q as encodeFrame, R as LocalChildRegistry, S as brokerCallForCap, St as runNpm, T as ipcChildLink, U as CapRouteError, V as AGENT_CAP_FWD_SERVICE, W as classifyCapRoute, X as localEndpointPath, Y as SocketChannel, Z as FrameDecoder, _ as createKernelHwAccel, _t as CapabilityHandle, a as getWorkerDeviceRegistry, b as __resetCapUsageRegistryForTests, bt as installManifestNativeDeps, c as setHubConnected, ct as NATIVE_PROVIDER_SERVICE_INFIX, d as getBrokerEventBus, dt as capBareAction, et as buildUdsNativeCapProxy, f as getMoleculerEventStats, ft as capServiceName, g as AddonDepsManager, gt as DeviceRegistry, h as subscribePassthrough, ht as serializeTypedArrays, i as createUdsAddonContext, it as mountNativeCapService, j as createParentUnownedCallHandler, k as HUB_CAP_FWD_SERVICE, l as EVENT_TOPIC_PREFIX, lt as capActionName, m as setNodeEventInterest, mt as deserializeTypedArrays, n as adaptBrokerToCluster, o as getOrInitReadinessRegistry, ot as createAddonService, p as registerEventBusService, pt as parseCapAction, q as UdsLocalTransportClient, r as createAddonContext, s as getOrInitReadinessRegistryForClient, st as validateProviderRegistrations, t as installManifestPythonDeps, tt as createBrokerDeviceManagerApi, u as clusterEventTopic, ut as capActionSuffix, v as resolveHwAccel, vt as CapabilityUnavailableError, w as buildLinkChain, wt as createAddonDataPlaneFacility, x as getCapUsageRegistry, xt as resolveNpmInvocation, y as CapUsageRegistry, yt as copyBundledNativeModules, z as UDS_NO_ROUTE_PREFIX } from "./manifest-python-deps-
|
|
4
|
+
import { $ as buildNativeCapProxy, A as createHubCapForwardService, B as AGENT_CAP_FWD_ACTION, C as brokerTransportLink, Ct as resolveAddonClass, D as localProviderLink, E as ipcParentLink, F as createUdsLogger, G as callWithServiceDiscovery, H as CapRouteResolver, I as createUdsLoggerWithControl, J as UdsLocalTransportServer, K as createLocalTransport, L as LocalChildClient, M as createUdsEventBridge, N as createUdsEventBus, O as HUB_CAP_FWD_ACTION, P as udsChildLogToWorkerEntry, Q as encodeFrame, R as LocalChildRegistry, S as brokerCallForCap, St as runNpm, T as ipcChildLink, U as CapRouteError, V as AGENT_CAP_FWD_SERVICE, W as classifyCapRoute, X as localEndpointPath, Y as SocketChannel, Z as FrameDecoder, _ as createKernelHwAccel, _t as CapabilityHandle, a as getWorkerDeviceRegistry, b as __resetCapUsageRegistryForTests, bt as installManifestNativeDeps, c as setHubConnected, ct as NATIVE_PROVIDER_SERVICE_INFIX, d as getBrokerEventBus, dt as capBareAction, et as buildUdsNativeCapProxy, f as getMoleculerEventStats, ft as capServiceName, g as AddonDepsManager, gt as DeviceRegistry, h as subscribePassthrough, ht as serializeTypedArrays, i as createUdsAddonContext, it as mountNativeCapService, j as createParentUnownedCallHandler, k as HUB_CAP_FWD_SERVICE, l as EVENT_TOPIC_PREFIX, lt as capActionName, m as setNodeEventInterest, mt as deserializeTypedArrays, n as adaptBrokerToCluster, o as getOrInitReadinessRegistry, ot as createAddonService, p as registerEventBusService, pt as parseCapAction, q as UdsLocalTransportClient, r as createAddonContext, s as getOrInitReadinessRegistryForClient, st as validateProviderRegistrations, t as installManifestPythonDeps, tt as createBrokerDeviceManagerApi, u as clusterEventTopic, ut as capActionSuffix, v as resolveHwAccel, vt as CapabilityUnavailableError, w as buildLinkChain, wt as createAddonDataPlaneFacility, x as getCapUsageRegistry, xt as resolveNpmInvocation, y as CapUsageRegistry, yt as copyBundledNativeModules, z as UDS_NO_ROUTE_PREFIX } from "./manifest-python-deps-CopL1_5C.mjs";
|
|
5
5
|
import { n as getSinglePidStats, t as getPidStats } from "./resource-monitor-BkP504Vq.mjs";
|
|
6
6
|
import { FilesystemStorageAddon, t as FilesystemStorageProvider } from "./builtins/sqlite-storage/filesystem-storage.addon.mjs";
|
|
7
7
|
import { SqliteSettingsAddon, o as SqliteSettingsBackend } from "./builtins/sqlite-storage/sqlite-settings.addon.mjs";
|
|
@@ -22,7 +22,7 @@ import { SystemConfigAddon } from "./builtins/system-config/system-config.addon.
|
|
|
22
22
|
import "./builtins/system-config/index.mjs";
|
|
23
23
|
import { CoreBlocksAddon } from "./builtins/core-blocks/core-blocks.addon.mjs";
|
|
24
24
|
import "./builtins/core-blocks/index.mjs";
|
|
25
|
-
import { LocalAuthAddon, a as
|
|
25
|
+
import { LocalAuthAddon, a as require_safe_buffer, i as require_ms, n as ScopedTokenManager, o as ApiKeyManager, r as AuthManager, s as HOME_ASSISTANT_OAUTH_INTEGRATION, t as UserManager } from "./builtins/local-auth/local-auth.addon.mjs";
|
|
26
26
|
import "./builtins/local-auth/index.mjs";
|
|
27
27
|
import { DeviceManagerAddon } from "./builtins/device-manager/device-manager.addon.mjs";
|
|
28
28
|
import "./builtins/device-manager/index.mjs";
|
|
@@ -3348,7 +3348,12 @@ var MANIFEST_VERSION = 1;
|
|
|
3348
3348
|
* workspace source. Trusting the dirent there finds zero addons on disk, which
|
|
3349
3349
|
* with pruning would empty a developer's manifest on the next boot.
|
|
3350
3350
|
*/
|
|
3351
|
-
|
|
3351
|
+
/** `@camstack/system` → `system`: the directory a package must live in. */
|
|
3352
|
+
function packageDirName$1(declaredName) {
|
|
3353
|
+
const slash = declaredName.lastIndexOf("/");
|
|
3354
|
+
return slash === -1 ? declaredName : declaredName.slice(slash + 1);
|
|
3355
|
+
}
|
|
3356
|
+
function isDirLike$1(p) {
|
|
3352
3357
|
try {
|
|
3353
3358
|
return fs$17.statSync(p).isDirectory();
|
|
3354
3359
|
} catch {
|
|
@@ -3482,10 +3487,10 @@ var AddonManifest = class {
|
|
|
3482
3487
|
/** Package name → version, for every ADDON package actually installed. */
|
|
3483
3488
|
const onDisk = /* @__PURE__ */ new Map();
|
|
3484
3489
|
for (const scope of fs$17.readdirSync(addonsDir, { withFileTypes: true })) {
|
|
3485
|
-
if (!scope.name.startsWith("@") || !isDirLike(path$39.join(addonsDir, scope.name))) continue;
|
|
3490
|
+
if (!scope.name.startsWith("@") || !isDirLike$1(path$39.join(addonsDir, scope.name))) continue;
|
|
3486
3491
|
const scopeDir = path$39.join(addonsDir, scope.name);
|
|
3487
3492
|
for (const pkg of fs$17.readdirSync(scopeDir, { withFileTypes: true })) {
|
|
3488
|
-
if (!isDirLike(path$39.join(scopeDir, pkg.name))) continue;
|
|
3493
|
+
if (!isDirLike$1(path$39.join(scopeDir, pkg.name))) continue;
|
|
3489
3494
|
const pkgJsonPath = path$39.join(scopeDir, pkg.name, "package.json");
|
|
3490
3495
|
if (!fs$17.existsSync(pkgJsonPath)) continue;
|
|
3491
3496
|
let pkgJson;
|
|
@@ -3499,6 +3504,7 @@ var AddonManifest = class {
|
|
|
3499
3504
|
if (!pkgJson.name || !pkgJson.version) continue;
|
|
3500
3505
|
const addons = pkgJson.camstack?.addons;
|
|
3501
3506
|
if (!Array.isArray(addons) || addons.length === 0) continue;
|
|
3507
|
+
if (packageDirName$1(pkgJson.name) !== pkg.name) continue;
|
|
3502
3508
|
onDisk.set(pkgJson.name, pkgJson.version);
|
|
3503
3509
|
}
|
|
3504
3510
|
}
|
|
@@ -5016,6 +5022,199 @@ function isDirEntry(entry, parentDir) {
|
|
|
5016
5022
|
return false;
|
|
5017
5023
|
}
|
|
5018
5024
|
//#endregion
|
|
5025
|
+
//#region src/kernel/addon-residue.ts
|
|
5026
|
+
/**
|
|
5027
|
+
* Residue quarantine for the ONE addon root.
|
|
5028
|
+
*
|
|
5029
|
+
* ── The bug class this closes ──────────────────────────────────────────────
|
|
5030
|
+
* `<dataDir>/addons/@camstack/<pkg>` is the only directory a node loads addons
|
|
5031
|
+
* from. Renaming a package inside it does NOT archive it — the directory name
|
|
5032
|
+
* is decoration, and three independent consumers key on the `package.json`
|
|
5033
|
+
* they find inside:
|
|
5034
|
+
*
|
|
5035
|
+
* - `AddonLoader.loadFromDirectory` walks `@scope/*` and loads anything with
|
|
5036
|
+
* a `package.json`; its skip list is keyed on the PACKAGE name, so
|
|
5037
|
+
* `@camstack/system.bak-pre-unshadow` was never skipped;
|
|
5038
|
+
* - the agent's `resolveAddonPackageDirs` scans on the same rule;
|
|
5039
|
+
* - `AddonManifest.reconcileFromDisk` does `onDisk.set(pkgJson.name, …)`,
|
|
5040
|
+
* so the backup's version becomes the version the manifest — and every
|
|
5041
|
+
* surface reading it — REPORTS.
|
|
5042
|
+
*
|
|
5043
|
+
* Measured 2026-08-07/08 on the live cluster:
|
|
5044
|
+
*
|
|
5045
|
+
* little-unraid `@camstack/system.bak-pre-unshadow` — no package.json left,
|
|
5046
|
+
* so not loadable, but the agent main process held five live
|
|
5047
|
+
* mappings of `better_sqlite3.node` out of it (D87).
|
|
5048
|
+
* macmini `@camstack/system.bak-pre-1272-1786129145` — still declares
|
|
5049
|
+
* `@camstack/system@1.2.3` (17 days old, 97 MB). The data root
|
|
5050
|
+
* has NO `addons/@camstack/system` directory at all, yet the
|
|
5051
|
+
* manifest reports `@camstack/system 1.2.3` — read straight
|
|
5052
|
+
* out of the backup. The live process still had the backup's
|
|
5053
|
+
* `better_sqlite3.node` mapped `txt` against a 1.2.61 closure.
|
|
5054
|
+
*
|
|
5055
|
+
* ── Why quarantine and not delete ─────────────────────────────────────────
|
|
5056
|
+
* The same evening, a cleanup that DELETED what it judged to be a shadow copy
|
|
5057
|
+
* removed the only builtins an agent had and gutted the node. So this never
|
|
5058
|
+
* deletes: it renames out of the scanned root into `.quarantine/`, logs every
|
|
5059
|
+
* move by name, reason and both paths, and leaves the bytes recoverable. A
|
|
5060
|
+
* silent sweep reads as "nothing was here", which is the failure mode that
|
|
5061
|
+
* made the original one unobservable.
|
|
5062
|
+
*
|
|
5063
|
+
* `.quarantine` sits beside `.staging` / `.backups` / `.evicting` under the
|
|
5064
|
+
* addon root: dot-prefixed, carrying no `package.json` at its top level, so no
|
|
5065
|
+
* scanner descends into it.
|
|
5066
|
+
*/
|
|
5067
|
+
/** Where residue is moved to, relative to the addon root. */
|
|
5068
|
+
var QUARANTINE_DIRNAME = ".quarantine";
|
|
5069
|
+
/**
|
|
5070
|
+
* A backup marker: a `.bak` / `.old` / `.orig` segment, not merely those
|
|
5071
|
+
* letters. `.backups` — the installer's own rollback store — must NOT match,
|
|
5072
|
+
* and it is also excluded by the dot rule below; the pattern stays tight
|
|
5073
|
+
* anyway so the two guards are independent.
|
|
5074
|
+
*/
|
|
5075
|
+
var BACKUP_MARKER = /\.(bak|old|orig)(?![a-z])/i;
|
|
5076
|
+
/** Installer-owned stores and node's own tree. Never candidates. */
|
|
5077
|
+
function isReservedDir(dirName) {
|
|
5078
|
+
return dirName.startsWith(".") || dirName === "node_modules";
|
|
5079
|
+
}
|
|
5080
|
+
/** `@camstack/system` → `system`; `foo` → `foo`. */
|
|
5081
|
+
function packageDirName(declaredName) {
|
|
5082
|
+
const slash = declaredName.lastIndexOf("/");
|
|
5083
|
+
return slash === -1 ? declaredName : declaredName.slice(slash + 1);
|
|
5084
|
+
}
|
|
5085
|
+
/**
|
|
5086
|
+
* Is this directory residue? Returns the verdict, or null to keep it.
|
|
5087
|
+
*
|
|
5088
|
+
* A directory with no `package.json` is NOT residue on that ground alone —
|
|
5089
|
+
* `models/`, a half-finished copy, a scratch dir are all absence, not a
|
|
5090
|
+
* rename. Only a backup marker condemns one.
|
|
5091
|
+
*/
|
|
5092
|
+
function classifyAddonDir(candidate) {
|
|
5093
|
+
const { dirName, declaredName } = candidate;
|
|
5094
|
+
if (isReservedDir(dirName)) return null;
|
|
5095
|
+
if (BACKUP_MARKER.test(dirName)) return {
|
|
5096
|
+
dirName,
|
|
5097
|
+
declaredName,
|
|
5098
|
+
reason: "backup-residue"
|
|
5099
|
+
};
|
|
5100
|
+
if (declaredName !== null && packageDirName(declaredName) !== dirName) return {
|
|
5101
|
+
dirName,
|
|
5102
|
+
declaredName,
|
|
5103
|
+
reason: "name-mismatch"
|
|
5104
|
+
};
|
|
5105
|
+
return null;
|
|
5106
|
+
}
|
|
5107
|
+
function selectAddonResidue(candidates) {
|
|
5108
|
+
const out = [];
|
|
5109
|
+
for (const candidate of candidates) {
|
|
5110
|
+
const verdict = classifyAddonDir(candidate);
|
|
5111
|
+
if (verdict !== null) out.push(verdict);
|
|
5112
|
+
}
|
|
5113
|
+
return out;
|
|
5114
|
+
}
|
|
5115
|
+
function readDeclaredName(dir) {
|
|
5116
|
+
try {
|
|
5117
|
+
const parsed = JSON.parse(fs$17.readFileSync(path$39.join(dir, "package.json"), "utf-8"));
|
|
5118
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
5119
|
+
const name = parsed.name;
|
|
5120
|
+
return typeof name === "string" ? name : null;
|
|
5121
|
+
} catch {
|
|
5122
|
+
return null;
|
|
5123
|
+
}
|
|
5124
|
+
}
|
|
5125
|
+
function readDeclaredVersion(dir) {
|
|
5126
|
+
try {
|
|
5127
|
+
const parsed = JSON.parse(fs$17.readFileSync(path$39.join(dir, "package.json"), "utf-8"));
|
|
5128
|
+
if (typeof parsed !== "object" || parsed === null) return null;
|
|
5129
|
+
const version = parsed.version;
|
|
5130
|
+
return typeof version === "string" ? version : null;
|
|
5131
|
+
} catch {
|
|
5132
|
+
return null;
|
|
5133
|
+
}
|
|
5134
|
+
}
|
|
5135
|
+
function isDirLike(p) {
|
|
5136
|
+
try {
|
|
5137
|
+
return fs$17.statSync(p).isDirectory();
|
|
5138
|
+
} catch {
|
|
5139
|
+
return false;
|
|
5140
|
+
}
|
|
5141
|
+
}
|
|
5142
|
+
/** `<scope>__<dir>`, suffixed until free — two sweeps must not overwrite. */
|
|
5143
|
+
function freeQuarantineTarget(store, scope, dirName) {
|
|
5144
|
+
const base = scope === null ? dirName : `${scope.replace("@", "")}__${dirName}`;
|
|
5145
|
+
let candidate = path$39.join(store, base);
|
|
5146
|
+
let n = 1;
|
|
5147
|
+
while (fs$17.existsSync(candidate)) {
|
|
5148
|
+
candidate = path$39.join(store, `${base}-${n}`);
|
|
5149
|
+
n += 1;
|
|
5150
|
+
}
|
|
5151
|
+
return candidate;
|
|
5152
|
+
}
|
|
5153
|
+
/**
|
|
5154
|
+
* Move every residue directory under `addonsDir` into `addonsDir/.quarantine`.
|
|
5155
|
+
*
|
|
5156
|
+
* Runs at boot, BEFORE anything scans the root or seeds into it — a residue
|
|
5157
|
+
* that is still present when the loader walks has already been installed.
|
|
5158
|
+
* Never throws, never deletes; a move that fails is reported and left in
|
|
5159
|
+
* place, which is strictly better than a half-moved package.
|
|
5160
|
+
*/
|
|
5161
|
+
function quarantineAddonResidue(addonsDir, log) {
|
|
5162
|
+
const quarantined = [];
|
|
5163
|
+
const failed = [];
|
|
5164
|
+
if (!fs$17.existsSync(addonsDir)) return {
|
|
5165
|
+
quarantined,
|
|
5166
|
+
failed
|
|
5167
|
+
};
|
|
5168
|
+
const store = path$39.join(addonsDir, QUARANTINE_DIRNAME);
|
|
5169
|
+
const scan = (parent, scope) => {
|
|
5170
|
+
let names;
|
|
5171
|
+
try {
|
|
5172
|
+
names = fs$17.readdirSync(parent);
|
|
5173
|
+
} catch {
|
|
5174
|
+
return;
|
|
5175
|
+
}
|
|
5176
|
+
for (const dirName of names) {
|
|
5177
|
+
const full = path$39.join(parent, dirName);
|
|
5178
|
+
if (!isDirLike(full)) continue;
|
|
5179
|
+
if (scope === null && dirName.startsWith("@")) {
|
|
5180
|
+
scan(full, dirName);
|
|
5181
|
+
continue;
|
|
5182
|
+
}
|
|
5183
|
+
const verdict = classifyAddonDir({
|
|
5184
|
+
dirName,
|
|
5185
|
+
declaredName: readDeclaredName(full)
|
|
5186
|
+
});
|
|
5187
|
+
if (verdict === null) continue;
|
|
5188
|
+
const version = readDeclaredVersion(full);
|
|
5189
|
+
const to = freeQuarantineTarget(store, scope, dirName);
|
|
5190
|
+
try {
|
|
5191
|
+
fs$17.mkdirSync(store, { recursive: true });
|
|
5192
|
+
fs$17.renameSync(full, to);
|
|
5193
|
+
} catch (err) {
|
|
5194
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
5195
|
+
failed.push({
|
|
5196
|
+
...verdict,
|
|
5197
|
+
from: full,
|
|
5198
|
+
error
|
|
5199
|
+
});
|
|
5200
|
+
log(`[addons] residue quarantine FAILED for ${full} (${verdict.reason}) — ${error}. It stays on the addon root and will be loaded, scanned and reported as an install.`);
|
|
5201
|
+
continue;
|
|
5202
|
+
}
|
|
5203
|
+
quarantined.push({
|
|
5204
|
+
...verdict,
|
|
5205
|
+
from: full,
|
|
5206
|
+
to
|
|
5207
|
+
});
|
|
5208
|
+
log(`[addons] quarantined ${verdict.reason} ${full} → ${to} (declares ${verdict.declaredName ?? "no package name"}${version === null ? "" : `@${version}`}). A renamed package dir is INSTALLED, not archived — nothing was deleted.`);
|
|
5209
|
+
}
|
|
5210
|
+
};
|
|
5211
|
+
scan(addonsDir, null);
|
|
5212
|
+
return {
|
|
5213
|
+
quarantined,
|
|
5214
|
+
failed
|
|
5215
|
+
};
|
|
5216
|
+
}
|
|
5217
|
+
//#endregion
|
|
5019
5218
|
//#region src/kernel/workspace-detect.ts
|
|
5020
5219
|
/**
|
|
5021
5220
|
* Detect whether we're running inside the monorepo workspace.
|
|
@@ -94627,4 +94826,4 @@ var LifecycleJobEngine = class {
|
|
|
94627
94826
|
}
|
|
94628
94827
|
};
|
|
94629
94828
|
//#endregion
|
|
94630
|
-
export { AGENT_CAP_FWD_ACTION, AGENT_CAP_FWD_SERVICE, AGENT_READINESS_SERVICE_NAME, ALL_CAPABILITY_DEFINITIONS, 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, CoreBlocksAddon, CustomActionRegistry, DEFAULT_DATA_PATH, DEVICE_SETTINGS_CONTRIBUTION_METHODS, DEVICE_STATUS_METHOD, DataPlaneRegistry, DeviceManagerAddon, DeviceRegistry, DeviceStore, EVENT_TOPIC_PREFIX, EngineManagerResolver, EventBus, FeatureManager, FilesystemStorageAddon, FilesystemStorageProvider, FrameDecoder, FsStorageBackend, HEALTH_MONITOR_GRACE_PERIOD_MS, HEALTH_MONITOR_RETRY_INTERVALS_MS, HEALTH_MONITOR_TICK_MS, HEAP_RECLAIM_MIN_INTERVAL_MS, HEAP_RECLAIM_TRIGGER_MB, HEAP_WATCH_INTERVAL_MS, HEAP_WATCH_WARN_RATIO, HUB_CAP_FWD_ACTION, HUB_CAP_FWD_SERVICE, HubForwarderAddon, HubForwarderDestination, HubLogForwarder, HubNodeRegistry, INFRA_CAPABILITIES, IntegrationRegistry, JobJournal, LifecycleJobEngine, LifecycleStateMachine, LivenessMonitorAddon, LocalAuthAddon, LocalChildClient, LocalChildRegistry, LogManager, LogRingBuffer, LokiDestination, LokiLoggingAddon, METHOD_ACCESS_MAP, ModelDownloadService, NATIVE_PROVIDER_SERVICE_INFIX, NATIVE_SCAN_DEPTH, 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, TRADITIONAL_NATIVE_PACKAGES, ToastService, UDS_NO_ROUTE_PREFIX, UdsLocalTransportClient, UdsLocalTransportServer, UserManager, WinstonDestination, WinstonLoggingAddon, __resetCapUsageRegistryForTests, adaptBrokerToCluster, addonSettingsCapability, bootstrapSchema, brokerCallForCap, brokerTransportLink, buildBinaryPath, buildCapRouters, buildHeapSample, buildLinkChain, buildNativeCapProxy, buildNodeManifest, buildStorageLocationRegistry, buildUdsNativeCapProxy, builderMountedCapNames, callRegisterNodeWithRetry, callWithServiceDiscovery, capActionName, capActionSuffix, capBareAction, capServiceName, classifyCapRoute, clearPendingRestart, clusterEventTopic, clusterSecretMatches, collectModelFiles, contentTypeFor, copyDirRecursive, copyExtraFileDirs, createAddonContext, createAddonDataPlaneFacility, createAddonService, createAuthenticatedFileServer, createBroker, createBrokerDeviceManagerApi, createCoreCapService, createFileDataPlaneHandler, createHubCapForwardService, createHubService, createKernelHwAccel, createLocalTransport, createParentUnownedCallHandler, createProcessService, createReadinessService, createReadinessServiceForRegistry, createScopedProcessManager, createStreamProbeBrokerService, createUdsAddonContext, createUdsEventBridge, createUdsEventBus, createUdsLogger, createUdsLoggerWithControl, createV8Reclaimer, deleteModelFromDisk, deriveAgentListenPort, describeProviderKindDrift, detectWorkspacePackagesDir, downloadBinary, downloadFile, downloadModel, emitDownForOwnedCaps, encodeFrame, ensureAddonNativePrebuilds, ensureBinary, ensureDir, ensureFfmpeg, ensureLibraryBuilt, ensureModel, ensureNativePrebuilds, ensurePython, ensureTlsCert, expandCapMethods, fetchJson, findInPath, formatLogLine, getBrokerEventBus, getCapUsageRegistry, getFfmpegDownloadUrl, getModelFilePath, getMoleculerEventStats, getOrInitReadinessRegistry, getOrInitReadinessRegistryForClient, getPidStats, getPlatformInfo, getPythonDownloadUrl, getRestartMarkerPath, getSinglePidStats, getWorkerDeviceRegistry, hasDotNode, hashClusterSecret, installManifestNativeDeps, installManifestPythonDeps, installPackageFromNpm, installPythonPackages, installPythonRequirements, ipcChildLink, ipcParentLink, isAddonDeploySource, isArrayOutputSchema, isClusterSecretMismatchError, isCollectionArrayMethod, isInfraCapability, isModelDownloaded, isSourceNewer, loadTlsCert, localEndpointPath, localProviderLink, mountNativeCapService, parseCapAction, parseRangeHeader, parseTokenizedUrl, proxyToUpstream, readPendingRestart, readinessKey, registerEventBusService, resolveFilePath, resolveHwAccel, resolveNpmInvocation, runNpm, scheduleSelfRestart, scopeKey, scopesAllowDeviceCap, serializeTypedArrays, setHubConnected, setNodeEventInterest, shouldReclaim, startHeapWatch, strandedMb, stripCamstackDeps, subscribePassthrough, udsChildLogToWorkerEntry, validateProviderRegistrations, writePendingRestart };
|
|
94829
|
+
export { AGENT_CAP_FWD_ACTION, AGENT_CAP_FWD_SERVICE, AGENT_READINESS_SERVICE_NAME, ALL_CAPABILITY_DEFINITIONS, 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, CoreBlocksAddon, CustomActionRegistry, DEFAULT_DATA_PATH, DEVICE_SETTINGS_CONTRIBUTION_METHODS, DEVICE_STATUS_METHOD, DataPlaneRegistry, DeviceManagerAddon, DeviceRegistry, DeviceStore, EVENT_TOPIC_PREFIX, EngineManagerResolver, EventBus, FeatureManager, FilesystemStorageAddon, FilesystemStorageProvider, FrameDecoder, FsStorageBackend, HEALTH_MONITOR_GRACE_PERIOD_MS, HEALTH_MONITOR_RETRY_INTERVALS_MS, HEALTH_MONITOR_TICK_MS, HEAP_RECLAIM_MIN_INTERVAL_MS, HEAP_RECLAIM_TRIGGER_MB, HEAP_WATCH_INTERVAL_MS, HEAP_WATCH_WARN_RATIO, HOME_ASSISTANT_OAUTH_INTEGRATION, HUB_CAP_FWD_ACTION, HUB_CAP_FWD_SERVICE, HubForwarderAddon, HubForwarderDestination, HubLogForwarder, HubNodeRegistry, INFRA_CAPABILITIES, IntegrationRegistry, JobJournal, LifecycleJobEngine, LifecycleStateMachine, LivenessMonitorAddon, LocalAuthAddon, LocalChildClient, LocalChildRegistry, LogManager, LogRingBuffer, LokiDestination, LokiLoggingAddon, METHOD_ACCESS_MAP, ModelDownloadService, NATIVE_PROVIDER_SERVICE_INFIX, NATIVE_SCAN_DEPTH, NativeMetricsAddon, NativeMetricsProvider, NetworkQualityTracker, NotificationService, PYTHON_VERSION, PipelineRunner, PipelineValidator, PythonEnvManager, QUARANTINE_DIRNAME, RESTART_MARKER_FILE, RUNTIME_DEFAULTS, ReadinessRegistry, ReadinessTimeoutError, ReplEngine, RingBuffer, ScopedLogger, ScopedTokenManager, SocketChannel, SqliteSettingsAddon, SqliteSettingsBackend, StagingArea, StorageLocationManager, StorageManager, StorageOrchestratorAddon, StorageOrchestratorService, SystemConfigAddon, SystemEventBus, TRADITIONAL_NATIVE_PACKAGES, ToastService, UDS_NO_ROUTE_PREFIX, UdsLocalTransportClient, UdsLocalTransportServer, UserManager, WinstonDestination, WinstonLoggingAddon, __resetCapUsageRegistryForTests, adaptBrokerToCluster, addonSettingsCapability, bootstrapSchema, brokerCallForCap, brokerTransportLink, buildBinaryPath, buildCapRouters, buildHeapSample, buildLinkChain, buildNativeCapProxy, buildNodeManifest, buildStorageLocationRegistry, buildUdsNativeCapProxy, builderMountedCapNames, callRegisterNodeWithRetry, callWithServiceDiscovery, capActionName, capActionSuffix, capBareAction, capServiceName, classifyAddonDir, classifyCapRoute, clearPendingRestart, clusterEventTopic, clusterSecretMatches, collectModelFiles, contentTypeFor, copyDirRecursive, copyExtraFileDirs, createAddonContext, createAddonDataPlaneFacility, createAddonService, createAuthenticatedFileServer, createBroker, createBrokerDeviceManagerApi, createCoreCapService, createFileDataPlaneHandler, createHubCapForwardService, createHubService, createKernelHwAccel, createLocalTransport, createParentUnownedCallHandler, createProcessService, createReadinessService, createReadinessServiceForRegistry, createScopedProcessManager, createStreamProbeBrokerService, createUdsAddonContext, createUdsEventBridge, createUdsEventBus, createUdsLogger, createUdsLoggerWithControl, createV8Reclaimer, deleteModelFromDisk, deriveAgentListenPort, describeProviderKindDrift, detectWorkspacePackagesDir, downloadBinary, downloadFile, downloadModel, emitDownForOwnedCaps, encodeFrame, ensureAddonNativePrebuilds, ensureBinary, ensureDir, ensureFfmpeg, ensureLibraryBuilt, ensureModel, ensureNativePrebuilds, ensurePython, ensureTlsCert, expandCapMethods, fetchJson, findInPath, formatLogLine, getBrokerEventBus, getCapUsageRegistry, getFfmpegDownloadUrl, getModelFilePath, getMoleculerEventStats, getOrInitReadinessRegistry, getOrInitReadinessRegistryForClient, getPidStats, getPlatformInfo, getPythonDownloadUrl, getRestartMarkerPath, getSinglePidStats, getWorkerDeviceRegistry, hasDotNode, hashClusterSecret, installManifestNativeDeps, installManifestPythonDeps, installPackageFromNpm, installPythonPackages, installPythonRequirements, ipcChildLink, ipcParentLink, isAddonDeploySource, isArrayOutputSchema, isClusterSecretMismatchError, isCollectionArrayMethod, isInfraCapability, isModelDownloaded, isSourceNewer, loadTlsCert, localEndpointPath, localProviderLink, mountNativeCapService, parseCapAction, parseRangeHeader, parseTokenizedUrl, proxyToUpstream, quarantineAddonResidue, readPendingRestart, readinessKey, registerEventBusService, resolveFilePath, resolveHwAccel, resolveNpmInvocation, runNpm, scheduleSelfRestart, scopeKey, scopesAllowDeviceCap, selectAddonResidue, serializeTypedArrays, setHubConnected, setNodeEventInterest, shouldReclaim, startHeapWatch, strandedMb, stripCamstackDeps, subscribePassthrough, udsChildLogToWorkerEntry, validateProviderRegistrations, writePendingRestart };
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/** Where residue is moved to, relative to the addon root. */
|
|
2
|
+
export declare const QUARANTINE_DIRNAME = ".quarantine";
|
|
3
|
+
export type ResidueReason =
|
|
4
|
+
/** The directory name carries a backup marker — `.bak…`, `.old…`, `.orig…`. */
|
|
5
|
+
'backup-residue'
|
|
6
|
+
/** The directory declares a package whose name is not this directory. */
|
|
7
|
+
| 'name-mismatch';
|
|
8
|
+
export interface AddonDirCandidate {
|
|
9
|
+
/** Directory basename under the addon root (or under its `@scope`). */
|
|
10
|
+
readonly dirName: string;
|
|
11
|
+
/** `name` from the directory's `package.json`; null when absent/unreadable. */
|
|
12
|
+
readonly declaredName: string | null;
|
|
13
|
+
}
|
|
14
|
+
export interface ResidueVerdict extends AddonDirCandidate {
|
|
15
|
+
readonly reason: ResidueReason;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Is this directory residue? Returns the verdict, or null to keep it.
|
|
19
|
+
*
|
|
20
|
+
* A directory with no `package.json` is NOT residue on that ground alone —
|
|
21
|
+
* `models/`, a half-finished copy, a scratch dir are all absence, not a
|
|
22
|
+
* rename. Only a backup marker condemns one.
|
|
23
|
+
*/
|
|
24
|
+
export declare function classifyAddonDir(candidate: AddonDirCandidate): ResidueVerdict | null;
|
|
25
|
+
export declare function selectAddonResidue(candidates: readonly AddonDirCandidate[]): readonly ResidueVerdict[];
|
|
26
|
+
export interface QuarantinedEntry extends ResidueVerdict {
|
|
27
|
+
/** Absolute path the residue occupied under the addon root. */
|
|
28
|
+
readonly from: string;
|
|
29
|
+
/** Absolute path it now occupies under `.quarantine/`. */
|
|
30
|
+
readonly to: string;
|
|
31
|
+
}
|
|
32
|
+
export interface ResidueQuarantineFailure extends ResidueVerdict {
|
|
33
|
+
readonly from: string;
|
|
34
|
+
readonly error: string;
|
|
35
|
+
}
|
|
36
|
+
export interface ResidueQuarantineResult {
|
|
37
|
+
readonly quarantined: readonly QuarantinedEntry[];
|
|
38
|
+
/** Residue that could not be moved. Reported, never fatal. */
|
|
39
|
+
readonly failed: readonly ResidueQuarantineFailure[];
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Move every residue directory under `addonsDir` into `addonsDir/.quarantine`.
|
|
43
|
+
*
|
|
44
|
+
* Runs at boot, BEFORE anything scans the root or seeds into it — a residue
|
|
45
|
+
* that is still present when the loader walks has already been installed.
|
|
46
|
+
* Never throws, never deletes; a move that fails is reported and left in
|
|
47
|
+
* place, which is strictly better than a half-moved package.
|
|
48
|
+
*/
|
|
49
|
+
export declare function quarantineAddonResidue(addonsDir: string, log: (message: string) => void): ResidueQuarantineResult;
|
package/dist/kernel/index.d.ts
CHANGED
|
@@ -5,6 +5,8 @@ export type { AddonInstallerConfig, InstallSource, InstalledPackage } from './ad
|
|
|
5
5
|
export { AddonInstaller } from './addon-installer.js';
|
|
6
6
|
export type { AddonInstallSource, AddonManifestEntry, AddonManifestFile } from './addon-manifest.js';
|
|
7
7
|
export { AddonManifest } from './addon-manifest.js';
|
|
8
|
+
export type { AddonDirCandidate, QuarantinedEntry, ResidueQuarantineResult, ResidueReason, ResidueVerdict, } from './addon-residue.js';
|
|
9
|
+
export { QUARANTINE_DIRNAME, classifyAddonDir, quarantineAddonResidue, selectAddonResidue, } from './addon-residue.js';
|
|
8
10
|
export { copyDirRecursive, ensureDir, stripCamstackDeps, copyExtraFileDirs, ensureLibraryBuilt, installPackageFromNpm, isSourceNewer, } from './fs-utils.js';
|
|
9
11
|
export { detectWorkspacePackagesDir } from './workspace-detect.js';
|
|
10
12
|
export { RESTART_MARKER_FILE, clearPendingRestart, getRestartMarkerPath, readPendingRestart, scheduleSelfRestart, writePendingRestart, } from './restart-coordinator.js';
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { AddonApi, CapabilityDefinition, DeviceManagerApi, IDevice, IEventBus, IKernelStreamProbe, InferProvider, IScopedLogger } from '@camstack/types';
|
|
1
2
|
import { deviceOpsCapability } from '@camstack/types/addon';
|
|
2
|
-
import { AddonApi, CapabilityDefinition, DeviceManagerApi, IDevice, IEventBus, IKernelStreamProbe, IScopedLogger, InferProvider } from '@camstack/types';
|
|
3
3
|
import { ServiceBroker } from 'moleculer';
|
|
4
|
-
import { DeviceRegistry } from '../device-registry.js';
|
|
5
4
|
import { CapabilityRegistry } from '../capability-registry.js';
|
|
5
|
+
import { DeviceRegistry } from '../device-registry.js';
|
|
6
6
|
import { LocalChildRegistry } from '../transport/local-child-registry.js';
|
|
7
7
|
export { NATIVE_PROVIDER_SERVICE_INFIX } from '../transport/cap-action-name.js';
|
|
8
8
|
export interface WorkerDeviceManagerOptions {
|