@camstack/system 1.2.24 → 1.2.26
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/device-manager/device-aggregation.d.ts +18 -0
- package/dist/builtins/device-manager/device-manager.addon.js +47 -1
- package/dist/builtins/device-manager/device-manager.addon.mjs +47 -1
- 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/local-auth/local-auth.addon.js +1 -1
- package/dist/builtins/local-auth/local-auth.addon.mjs +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 +1 -1
- package/dist/builtins/snapshot/index.mjs +1 -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/sqlite-settings.addon.js +1 -1
- package/dist/builtins/sqlite-storage/sqlite-settings.addon.mjs +1 -1
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.d.ts +0 -19
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.js +143 -67
- package/dist/builtins/storage-orchestrator/storage-orchestrator.addon.mjs +143 -67
- package/dist/builtins/storage-orchestrator/storage-orchestrator.service.d.ts +67 -7
- 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-CS0pdC8l.js → dist-Cu72wsz5.js} +132 -3
- package/dist/{dist-CIek6F9X.mjs → dist-DimX9eZy.mjs} +132 -3
- package/dist/index.js +2 -2
- package/dist/index.mjs +2 -2
- package/dist/kernel/moleculer/settings-read-guard.d.ts +49 -0
- package/dist/{manifest-python-deps-BbOZZbVt.js → manifest-python-deps-COgal7Jy.js} +68 -17
- package/dist/{manifest-python-deps-C9qhvMUi.mjs → manifest-python-deps-julrfvyf.mjs} +68 -17
- package/package.json +1 -1
|
@@ -1188,6 +1188,27 @@ function addSettingsActions(addon, actions) {
|
|
|
1188
1188
|
}
|
|
1189
1189
|
}
|
|
1190
1190
|
//#endregion
|
|
1191
|
+
//#region src/kernel/moleculer/settings-read-guard.ts
|
|
1192
|
+
/**
|
|
1193
|
+
* Merge `patch` over the current value and write it — unless the read failed,
|
|
1194
|
+
* in which case write NOTHING.
|
|
1195
|
+
*
|
|
1196
|
+
* Returns whether the write was attempted, so a caller can surface the refusal.
|
|
1197
|
+
*/
|
|
1198
|
+
async function readModifyWrite(input) {
|
|
1199
|
+
const current = await input.read();
|
|
1200
|
+
if (current.kind === "failed") {
|
|
1201
|
+
input.onRefused(current.error);
|
|
1202
|
+
return false;
|
|
1203
|
+
}
|
|
1204
|
+
const base = current.kind === "value" ? current.value : {};
|
|
1205
|
+
await input.write({
|
|
1206
|
+
...base,
|
|
1207
|
+
...input.patch
|
|
1208
|
+
});
|
|
1209
|
+
return true;
|
|
1210
|
+
}
|
|
1211
|
+
//#endregion
|
|
1191
1212
|
//#region src/kernel/moleculer/device-cap-proxy.ts
|
|
1192
1213
|
/**
|
|
1193
1214
|
* Max number of accessory children spawned concurrently during a single
|
|
@@ -7171,8 +7192,18 @@ async function buildAddonContext(runtime, declaration, dataDir, options) {
|
|
|
7171
7192
|
bindingCache.clear();
|
|
7172
7193
|
});
|
|
7173
7194
|
const settingsApi = Reflect.get(api, "settingsStore");
|
|
7174
|
-
|
|
7175
|
-
|
|
7195
|
+
/**
|
|
7196
|
+
* Read a settings blob, distinguishing a MISS from a FAILURE.
|
|
7197
|
+
*
|
|
7198
|
+
* Collapsing the two is a data-loss bug, not a style choice: every mutation
|
|
7199
|
+
* is read-modify-write, so a failed read that answers "empty" makes the write
|
|
7200
|
+
* persist the patch ALONE and destroy every field the patch does not mention.
|
|
7201
|
+
* That is what emptied the recording configuration on 2026-07-31 when the UDS
|
|
7202
|
+
* transport stalled — `{"enabled": false, "bands": []}`, two and a half hours
|
|
7203
|
+
* of footage never written, and nothing that looked like a fault.
|
|
7204
|
+
*/
|
|
7205
|
+
const readBlobResult = async (coll, key, namespace) => {
|
|
7206
|
+
if (!settingsApi) return { kind: "missing" };
|
|
7176
7207
|
try {
|
|
7177
7208
|
const v = await settingsApi.get.query(namespace !== void 0 ? {
|
|
7178
7209
|
namespace,
|
|
@@ -7182,11 +7213,40 @@ async function buildAddonContext(runtime, declaration, dataDir, options) {
|
|
|
7182
7213
|
collection: coll,
|
|
7183
7214
|
key
|
|
7184
7215
|
});
|
|
7185
|
-
return v && typeof v === "object" ?
|
|
7186
|
-
|
|
7187
|
-
|
|
7216
|
+
return v && typeof v === "object" ? {
|
|
7217
|
+
kind: "value",
|
|
7218
|
+
value: v
|
|
7219
|
+
} : { kind: "missing" };
|
|
7220
|
+
} catch (err) {
|
|
7221
|
+
return {
|
|
7222
|
+
kind: "failed",
|
|
7223
|
+
error: (0, _camstack_types_addon.errMsg)(err)
|
|
7224
|
+
};
|
|
7188
7225
|
}
|
|
7189
7226
|
};
|
|
7227
|
+
/**
|
|
7228
|
+
* Soft read for CONSUMERS: a failure reads as empty so a boot path degrades
|
|
7229
|
+
* to defaults instead of throwing. Safe here precisely because the result is
|
|
7230
|
+
* never written back — {@link readBlobResult} is what the write paths use.
|
|
7231
|
+
*/
|
|
7232
|
+
const readBlob = async (coll, key, namespace) => {
|
|
7233
|
+
const r = await readBlobResult(coll, key, namespace);
|
|
7234
|
+
return r.kind === "value" ? r.value : {};
|
|
7235
|
+
};
|
|
7236
|
+
/** Read-modify-write that REFUSES to write when the read failed. */
|
|
7237
|
+
const mergeBlob = async (coll, key, patch, namespace) => {
|
|
7238
|
+
await readModifyWrite({
|
|
7239
|
+
read: () => readBlobResult(coll, key, namespace),
|
|
7240
|
+
write: (next) => writeBlob(coll, key, next, namespace),
|
|
7241
|
+
patch,
|
|
7242
|
+
onRefused: (error) => {
|
|
7243
|
+
scopedLogger.error(`settings merge REFUSED (${coll}/${key}) — read failed, refusing to overwrite with a partial blob`, { meta: {
|
|
7244
|
+
error,
|
|
7245
|
+
patchKeys: Object.keys(patch)
|
|
7246
|
+
} });
|
|
7247
|
+
}
|
|
7248
|
+
});
|
|
7249
|
+
};
|
|
7190
7250
|
const writeBlob = async (coll, key, value, namespace) => {
|
|
7191
7251
|
if (!settingsApi) return;
|
|
7192
7252
|
try {
|
|
@@ -7225,10 +7285,7 @@ async function buildAddonContext(runtime, declaration, dataDir, options) {
|
|
|
7225
7285
|
},
|
|
7226
7286
|
async writeAddonStore(patch) {
|
|
7227
7287
|
return serializeRmw("addon-settings", "root", async () => {
|
|
7228
|
-
await
|
|
7229
|
-
...await readBlob("addon-settings", "root", addonId),
|
|
7230
|
-
...patch
|
|
7231
|
-
}, addonId);
|
|
7288
|
+
await mergeBlob("addon-settings", "root", patch, addonId);
|
|
7232
7289
|
});
|
|
7233
7290
|
},
|
|
7234
7291
|
async readDeviceStore(deviceId) {
|
|
@@ -7237,10 +7294,7 @@ async function buildAddonContext(runtime, declaration, dataDir, options) {
|
|
|
7237
7294
|
async writeDeviceStore(deviceId, patch) {
|
|
7238
7295
|
const key = String(deviceId);
|
|
7239
7296
|
return serializeRmw("addon-devices", key, async () => {
|
|
7240
|
-
await
|
|
7241
|
-
...await readBlob("addon-devices", key, addonId),
|
|
7242
|
-
...patch
|
|
7243
|
-
}, addonId);
|
|
7297
|
+
await mergeBlob("addon-devices", key, patch, addonId);
|
|
7244
7298
|
});
|
|
7245
7299
|
},
|
|
7246
7300
|
async clearDeviceStore(deviceId) {
|
|
@@ -7260,10 +7314,7 @@ async function buildAddonContext(runtime, declaration, dataDir, options) {
|
|
|
7260
7314
|
},
|
|
7261
7315
|
async setSection(section, patch) {
|
|
7262
7316
|
return serializeRmw("sections", section, async () => {
|
|
7263
|
-
await
|
|
7264
|
-
...await readBlob("sections", section),
|
|
7265
|
-
...patch
|
|
7266
|
-
});
|
|
7317
|
+
await mergeBlob("sections", section, patch);
|
|
7267
7318
|
});
|
|
7268
7319
|
}
|
|
7269
7320
|
};
|
|
@@ -1186,6 +1186,27 @@ function addSettingsActions(addon, actions) {
|
|
|
1186
1186
|
}
|
|
1187
1187
|
}
|
|
1188
1188
|
//#endregion
|
|
1189
|
+
//#region src/kernel/moleculer/settings-read-guard.ts
|
|
1190
|
+
/**
|
|
1191
|
+
* Merge `patch` over the current value and write it — unless the read failed,
|
|
1192
|
+
* in which case write NOTHING.
|
|
1193
|
+
*
|
|
1194
|
+
* Returns whether the write was attempted, so a caller can surface the refusal.
|
|
1195
|
+
*/
|
|
1196
|
+
async function readModifyWrite(input) {
|
|
1197
|
+
const current = await input.read();
|
|
1198
|
+
if (current.kind === "failed") {
|
|
1199
|
+
input.onRefused(current.error);
|
|
1200
|
+
return false;
|
|
1201
|
+
}
|
|
1202
|
+
const base = current.kind === "value" ? current.value : {};
|
|
1203
|
+
await input.write({
|
|
1204
|
+
...base,
|
|
1205
|
+
...input.patch
|
|
1206
|
+
});
|
|
1207
|
+
return true;
|
|
1208
|
+
}
|
|
1209
|
+
//#endregion
|
|
1189
1210
|
//#region src/kernel/moleculer/device-cap-proxy.ts
|
|
1190
1211
|
/**
|
|
1191
1212
|
* Max number of accessory children spawned concurrently during a single
|
|
@@ -7169,8 +7190,18 @@ async function buildAddonContext(runtime, declaration, dataDir, options) {
|
|
|
7169
7190
|
bindingCache.clear();
|
|
7170
7191
|
});
|
|
7171
7192
|
const settingsApi = Reflect.get(api, "settingsStore");
|
|
7172
|
-
|
|
7173
|
-
|
|
7193
|
+
/**
|
|
7194
|
+
* Read a settings blob, distinguishing a MISS from a FAILURE.
|
|
7195
|
+
*
|
|
7196
|
+
* Collapsing the two is a data-loss bug, not a style choice: every mutation
|
|
7197
|
+
* is read-modify-write, so a failed read that answers "empty" makes the write
|
|
7198
|
+
* persist the patch ALONE and destroy every field the patch does not mention.
|
|
7199
|
+
* That is what emptied the recording configuration on 2026-07-31 when the UDS
|
|
7200
|
+
* transport stalled — `{"enabled": false, "bands": []}`, two and a half hours
|
|
7201
|
+
* of footage never written, and nothing that looked like a fault.
|
|
7202
|
+
*/
|
|
7203
|
+
const readBlobResult = async (coll, key, namespace) => {
|
|
7204
|
+
if (!settingsApi) return { kind: "missing" };
|
|
7174
7205
|
try {
|
|
7175
7206
|
const v = await settingsApi.get.query(namespace !== void 0 ? {
|
|
7176
7207
|
namespace,
|
|
@@ -7180,11 +7211,40 @@ async function buildAddonContext(runtime, declaration, dataDir, options) {
|
|
|
7180
7211
|
collection: coll,
|
|
7181
7212
|
key
|
|
7182
7213
|
});
|
|
7183
|
-
return v && typeof v === "object" ?
|
|
7184
|
-
|
|
7185
|
-
|
|
7214
|
+
return v && typeof v === "object" ? {
|
|
7215
|
+
kind: "value",
|
|
7216
|
+
value: v
|
|
7217
|
+
} : { kind: "missing" };
|
|
7218
|
+
} catch (err) {
|
|
7219
|
+
return {
|
|
7220
|
+
kind: "failed",
|
|
7221
|
+
error: errMsg(err)
|
|
7222
|
+
};
|
|
7186
7223
|
}
|
|
7187
7224
|
};
|
|
7225
|
+
/**
|
|
7226
|
+
* Soft read for CONSUMERS: a failure reads as empty so a boot path degrades
|
|
7227
|
+
* to defaults instead of throwing. Safe here precisely because the result is
|
|
7228
|
+
* never written back — {@link readBlobResult} is what the write paths use.
|
|
7229
|
+
*/
|
|
7230
|
+
const readBlob = async (coll, key, namespace) => {
|
|
7231
|
+
const r = await readBlobResult(coll, key, namespace);
|
|
7232
|
+
return r.kind === "value" ? r.value : {};
|
|
7233
|
+
};
|
|
7234
|
+
/** Read-modify-write that REFUSES to write when the read failed. */
|
|
7235
|
+
const mergeBlob = async (coll, key, patch, namespace) => {
|
|
7236
|
+
await readModifyWrite({
|
|
7237
|
+
read: () => readBlobResult(coll, key, namespace),
|
|
7238
|
+
write: (next) => writeBlob(coll, key, next, namespace),
|
|
7239
|
+
patch,
|
|
7240
|
+
onRefused: (error) => {
|
|
7241
|
+
scopedLogger.error(`settings merge REFUSED (${coll}/${key}) — read failed, refusing to overwrite with a partial blob`, { meta: {
|
|
7242
|
+
error,
|
|
7243
|
+
patchKeys: Object.keys(patch)
|
|
7244
|
+
} });
|
|
7245
|
+
}
|
|
7246
|
+
});
|
|
7247
|
+
};
|
|
7188
7248
|
const writeBlob = async (coll, key, value, namespace) => {
|
|
7189
7249
|
if (!settingsApi) return;
|
|
7190
7250
|
try {
|
|
@@ -7223,10 +7283,7 @@ async function buildAddonContext(runtime, declaration, dataDir, options) {
|
|
|
7223
7283
|
},
|
|
7224
7284
|
async writeAddonStore(patch) {
|
|
7225
7285
|
return serializeRmw("addon-settings", "root", async () => {
|
|
7226
|
-
await
|
|
7227
|
-
...await readBlob("addon-settings", "root", addonId),
|
|
7228
|
-
...patch
|
|
7229
|
-
}, addonId);
|
|
7286
|
+
await mergeBlob("addon-settings", "root", patch, addonId);
|
|
7230
7287
|
});
|
|
7231
7288
|
},
|
|
7232
7289
|
async readDeviceStore(deviceId) {
|
|
@@ -7235,10 +7292,7 @@ async function buildAddonContext(runtime, declaration, dataDir, options) {
|
|
|
7235
7292
|
async writeDeviceStore(deviceId, patch) {
|
|
7236
7293
|
const key = String(deviceId);
|
|
7237
7294
|
return serializeRmw("addon-devices", key, async () => {
|
|
7238
|
-
await
|
|
7239
|
-
...await readBlob("addon-devices", key, addonId),
|
|
7240
|
-
...patch
|
|
7241
|
-
}, addonId);
|
|
7295
|
+
await mergeBlob("addon-devices", key, patch, addonId);
|
|
7242
7296
|
});
|
|
7243
7297
|
},
|
|
7244
7298
|
async clearDeviceStore(deviceId) {
|
|
@@ -7258,10 +7312,7 @@ async function buildAddonContext(runtime, declaration, dataDir, options) {
|
|
|
7258
7312
|
},
|
|
7259
7313
|
async setSection(section, patch) {
|
|
7260
7314
|
return serializeRmw("sections", section, async () => {
|
|
7261
|
-
await
|
|
7262
|
-
...await readBlob("sections", section),
|
|
7263
|
-
...patch
|
|
7264
|
-
});
|
|
7315
|
+
await mergeBlob("sections", section, patch);
|
|
7265
7316
|
});
|
|
7266
7317
|
}
|
|
7267
7318
|
};
|