camstack 1.2.106 → 1.2.107
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/cli.js
CHANGED
|
@@ -38,7 +38,7 @@ async function runServe(args) {
|
|
|
38
38
|
...typeof values.data === "string" ? { data: values.data } : {}
|
|
39
39
|
};
|
|
40
40
|
Object.assign(process.env, buildServeEnv(opts));
|
|
41
|
-
await import("./launcher-
|
|
41
|
+
await import("./launcher-HX7B54EQ.js");
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
// src/commands/agent.ts
|
|
@@ -83,7 +83,7 @@ async function runAgent(args) {
|
|
|
83
83
|
...typeof values.port === "string" ? { port: values.port } : {}
|
|
84
84
|
};
|
|
85
85
|
Object.assign(process.env, buildAgentEnv(opts));
|
|
86
|
-
await import("./launcher-
|
|
86
|
+
await import("./launcher-HX7B54EQ.js");
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
// src/commands/setup.ts
|
|
@@ -65727,21 +65727,46 @@ var require_device_manager_addon = __commonJS({
|
|
|
65727
65727
|
device
|
|
65728
65728
|
};
|
|
65729
65729
|
}
|
|
65730
|
+
function persistedOnlineFallback(input) {
|
|
65731
|
+
if (!input.registryPresent) return false;
|
|
65732
|
+
if (input.parentDeviceId !== null) return true;
|
|
65733
|
+
return Object.keys(input.config ?? {}).length > 0;
|
|
65734
|
+
}
|
|
65730
65735
|
function extractCallerAddonId(input) {
|
|
65731
65736
|
const raw = Reflect.get(input, "callerAddonId");
|
|
65732
65737
|
return typeof raw === "string" && raw.length > 0 ? raw : void 0;
|
|
65733
65738
|
}
|
|
65734
65739
|
async function listPersistedByAddon(pctx, input) {
|
|
65735
65740
|
const { addonId } = input;
|
|
65736
|
-
|
|
65737
|
-
|
|
65738
|
-
|
|
65739
|
-
|
|
65740
|
-
|
|
65741
|
-
|
|
65742
|
-
|
|
65743
|
-
|
|
65744
|
-
|
|
65741
|
+
const rows = await pctx.metaStore.rows.listByAddon(addonId);
|
|
65742
|
+
const saved = [];
|
|
65743
|
+
for (const row of rows) {
|
|
65744
|
+
const m = row.meta;
|
|
65745
|
+
if (!row.registered) {
|
|
65746
|
+
const reserved = await pctx.settings.readDeviceStore(m.id);
|
|
65747
|
+
if (Object.keys(reserved ?? {}).length === 0) {
|
|
65748
|
+
pctx.host.ctx.logger.warn("Skipping an unregistered device reservation with no configuration \u2014 not a device, not restored", {
|
|
65749
|
+
tags: { deviceId: m.id },
|
|
65750
|
+
meta: {
|
|
65751
|
+
addonId,
|
|
65752
|
+
stableId: m.stableId,
|
|
65753
|
+
type: m.type
|
|
65754
|
+
}
|
|
65755
|
+
});
|
|
65756
|
+
continue;
|
|
65757
|
+
}
|
|
65758
|
+
}
|
|
65759
|
+
saved.push({
|
|
65760
|
+
id: m.id,
|
|
65761
|
+
stableId: m.stableId,
|
|
65762
|
+
type: m.type,
|
|
65763
|
+
name: m.name,
|
|
65764
|
+
location: m.location ?? null,
|
|
65765
|
+
disabled: m.disabled ?? false,
|
|
65766
|
+
parentDeviceId: m.parentDeviceId
|
|
65767
|
+
});
|
|
65768
|
+
}
|
|
65769
|
+
return saved;
|
|
65745
65770
|
}
|
|
65746
65771
|
async function readDeviceConfigs(pctx, deviceIds) {
|
|
65747
65772
|
if (deviceIds.length === 0) return /* @__PURE__ */ new Map();
|
|
@@ -65811,7 +65836,11 @@ var require_device_manager_addon = __commonJS({
|
|
|
65811
65836
|
disabled: m.disabled ?? false,
|
|
65812
65837
|
parentDeviceId: m.parentDeviceId ?? null,
|
|
65813
65838
|
role: toDeviceRole(m.role),
|
|
65814
|
-
online: pctx.host.resolveDeviceOnline(m.id,
|
|
65839
|
+
online: pctx.host.resolveDeviceOnline(m.id, persistedOnlineFallback({
|
|
65840
|
+
registryPresent: pctx.registry !== null,
|
|
65841
|
+
parentDeviceId: m.parentDeviceId ?? null,
|
|
65842
|
+
config: persistedConfig
|
|
65843
|
+
})),
|
|
65815
65844
|
probed: pctx.host.resolveDeviceProbed(m.id),
|
|
65816
65845
|
features: persistedFeatures(m.features),
|
|
65817
65846
|
isCamera: persistedIsCamera(persistedType),
|
|
@@ -65856,7 +65885,11 @@ var require_device_manager_addon = __commonJS({
|
|
|
65856
65885
|
disabled: m.disabled ?? false,
|
|
65857
65886
|
parentDeviceId: m.parentDeviceId,
|
|
65858
65887
|
role: toDeviceRole(m.role),
|
|
65859
|
-
online: pctx.host.resolveDeviceOnline(deviceId,
|
|
65888
|
+
online: pctx.host.resolveDeviceOnline(deviceId, persistedOnlineFallback({
|
|
65889
|
+
registryPresent: true,
|
|
65890
|
+
parentDeviceId: m.parentDeviceId ?? null,
|
|
65891
|
+
config: persistedConfig
|
|
65892
|
+
})),
|
|
65860
65893
|
probed: pctx.host.resolveDeviceProbed(deviceId),
|
|
65861
65894
|
features: persistedFeatures(m.features),
|
|
65862
65895
|
isCamera: persistedIsCamera(m.type),
|
|
@@ -66172,7 +66205,7 @@ var require_device_manager_addon = __commonJS({
|
|
|
66172
66205
|
async function adoptDevice(pctx, input) {
|
|
66173
66206
|
const dp = await pctx.host.requireDeviceProvider(input.addonId);
|
|
66174
66207
|
if (!await dp.supportsDiscovery({})) throw new Error(`Addon "${input.addonId}" does not support device adoption`);
|
|
66175
|
-
const summary = await dp.adoptDiscoveredDevice({ candidate: input.candidate });
|
|
66208
|
+
const summary = await pctx.removalFence.runUnfenced(() => dp.adoptDiscoveredDevice({ candidate: input.candidate }));
|
|
66176
66209
|
if (input.integrationId !== void 0) try {
|
|
66177
66210
|
await pctx.stampIntegrationId(summary.id, input.integrationId);
|
|
66178
66211
|
} catch (err) {
|
|
@@ -66194,10 +66227,10 @@ var require_device_manager_addon = __commonJS({
|
|
|
66194
66227
|
async function createDevice(pctx, input) {
|
|
66195
66228
|
const dp = await pctx.host.requireDeviceProvider(input.addonId);
|
|
66196
66229
|
if (!await dp.supportsManualCreation({})) throw new Error(`Addon "${input.addonId}" does not support manual device creation`);
|
|
66197
|
-
const summary = await dp.createDevice({
|
|
66230
|
+
const summary = await pctx.removalFence.runUnfenced(() => dp.createDevice({
|
|
66198
66231
|
type: input.type,
|
|
66199
66232
|
config: input.config
|
|
66200
|
-
});
|
|
66233
|
+
}));
|
|
66201
66234
|
if (input.integrationId !== void 0) try {
|
|
66202
66235
|
await pctx.stampIntegrationId(summary.id, input.integrationId);
|
|
66203
66236
|
} catch (err) {
|
|
@@ -67487,6 +67520,17 @@ var require_device_manager_addon = __commonJS({
|
|
|
67487
67520
|
return await pctx.metaStore.withMetaWriteLock(async () => {
|
|
67488
67521
|
const existing = await pctx.metaStore.rows.findByStableId(addonId, stableId);
|
|
67489
67522
|
if (existing) return { id: existing.meta.id };
|
|
67523
|
+
const fenced = pctx.removalFence.isPairFenced(addonId, stableId);
|
|
67524
|
+
if (fenced) {
|
|
67525
|
+
pctx.host.ctx.logger.warn("allocateDeviceId refused \u2014 this device was just removed, NOT re-created", {
|
|
67526
|
+
tags: { deviceId: fenced.deviceId },
|
|
67527
|
+
meta: {
|
|
67528
|
+
addonId,
|
|
67529
|
+
stableId
|
|
67530
|
+
}
|
|
67531
|
+
});
|
|
67532
|
+
throw new Error(`[device-manager] allocateDeviceId: device ${fenced.deviceId} (${addonId}/${stableId}) was removed \u2014 refusing to re-create it`);
|
|
67533
|
+
}
|
|
67490
67534
|
const id = await pctx.metaStore.allocateNextDeviceId();
|
|
67491
67535
|
await pctx.metaStore.rows.upsert({
|
|
67492
67536
|
addonId,
|
|
@@ -67615,6 +67659,16 @@ var require_device_manager_addon = __commonJS({
|
|
|
67615
67659
|
async function registerDevice(pctx, input) {
|
|
67616
67660
|
const { addonId, stableId, id, type, name, parentDeviceId, features, config } = input;
|
|
67617
67661
|
const featuresArr = Array.isArray(features) ? [...features] : [];
|
|
67662
|
+
if (pctx.removalFence.isIdFenced(id) ?? pctx.removalFence.isPairFenced(addonId, stableId)) {
|
|
67663
|
+
pctx.host.ctx.logger.warn("registerDevice refused \u2014 this device was removed, NOT resurrected", {
|
|
67664
|
+
tags: { deviceId: id },
|
|
67665
|
+
meta: {
|
|
67666
|
+
addonId,
|
|
67667
|
+
stableId
|
|
67668
|
+
}
|
|
67669
|
+
});
|
|
67670
|
+
throw new Error(`[device-manager] registerDevice: device ${id} (${addonId}/${stableId}) was removed \u2014 refusing to resurrect it`);
|
|
67671
|
+
}
|
|
67618
67672
|
const { isFirstRegistration, exportFingerprint, fingerprintChanged, inheritedLocation, inheritedDescendantIds, inheritFailed } = await pctx.metaStore.withMetaWriteLock(async () => {
|
|
67619
67673
|
const existingRow = await pctx.metaStore.rows.get(id);
|
|
67620
67674
|
const existingMeta = existingRow?.meta;
|
|
@@ -67755,6 +67809,11 @@ var require_device_manager_addon = __commonJS({
|
|
|
67755
67809
|
const deviceName = persistedMeta.name;
|
|
67756
67810
|
await pctx.metaStore.withMetaWriteLock(async () => {
|
|
67757
67811
|
await pctx.metaStore.rows.remove(deviceId);
|
|
67812
|
+
pctx.removalFence.record({
|
|
67813
|
+
deviceId,
|
|
67814
|
+
addonId,
|
|
67815
|
+
stableId
|
|
67816
|
+
});
|
|
67758
67817
|
});
|
|
67759
67818
|
await pctx.settings.clearDeviceStore(deviceId);
|
|
67760
67819
|
await pctx.settings.clearDeviceRuntimeState(deviceId);
|
|
@@ -69681,7 +69740,7 @@ var require_device_manager_addon = __commonJS({
|
|
|
69681
69740
|
function createMigrationGuard(now = Date.now) {
|
|
69682
69741
|
const inFlight = /* @__PURE__ */ new Set();
|
|
69683
69742
|
const completed = /* @__PURE__ */ new Map();
|
|
69684
|
-
const
|
|
69743
|
+
const pairKey2 = (sourceId, targetId) => `${sourceId}->${targetId}`;
|
|
69685
69744
|
return {
|
|
69686
69745
|
begin(sourceId, targetId) {
|
|
69687
69746
|
for (const id of [sourceId, targetId]) if (inFlight.has(id)) throw new Error(`[device-manager] migrateDevice: a migration involving device ${id} is already in flight \u2014 this call was refused, not queued. The running migration's report stands; re-read the device rows before retrying.`);
|
|
@@ -69693,13 +69752,13 @@ var require_device_manager_addon = __commonJS({
|
|
|
69693
69752
|
inFlight.delete(targetId);
|
|
69694
69753
|
},
|
|
69695
69754
|
recordCompleted(record) {
|
|
69696
|
-
completed.set(
|
|
69755
|
+
completed.set(pairKey2(record.sourceId, record.targetId), {
|
|
69697
69756
|
...record,
|
|
69698
69757
|
completedAt: now()
|
|
69699
69758
|
});
|
|
69700
69759
|
},
|
|
69701
69760
|
matchReplay(sourceId, targetId, atSource, atTarget) {
|
|
69702
|
-
const key =
|
|
69761
|
+
const key = pairKey2(sourceId, targetId);
|
|
69703
69762
|
const record = completed.get(key);
|
|
69704
69763
|
if (record === void 0) return null;
|
|
69705
69764
|
if (now() - record.completedAt > 9e5) {
|
|
@@ -69711,6 +69770,56 @@ var require_device_manager_addon = __commonJS({
|
|
|
69711
69770
|
}
|
|
69712
69771
|
};
|
|
69713
69772
|
}
|
|
69773
|
+
function pairKey(addonId, stableId) {
|
|
69774
|
+
return `${addonId}\0${stableId}`;
|
|
69775
|
+
}
|
|
69776
|
+
function createRemovalFence(options = {}) {
|
|
69777
|
+
const ttlMs = options.ttlMs ?? 3e5;
|
|
69778
|
+
const now = options.now ?? (() => Date.now());
|
|
69779
|
+
const byId = /* @__PURE__ */ new Map();
|
|
69780
|
+
const byPair = /* @__PURE__ */ new Map();
|
|
69781
|
+
let suspensions = 0;
|
|
69782
|
+
const fresh = (entry) => {
|
|
69783
|
+
if (entry === void 0) return null;
|
|
69784
|
+
if (suspensions > 0) return null;
|
|
69785
|
+
if (now() - entry.removedAt >= ttlMs) {
|
|
69786
|
+
byId.delete(entry.deviceId);
|
|
69787
|
+
byPair.delete(pairKey(entry.addonId, entry.stableId));
|
|
69788
|
+
return null;
|
|
69789
|
+
}
|
|
69790
|
+
return entry;
|
|
69791
|
+
};
|
|
69792
|
+
return {
|
|
69793
|
+
record(entry) {
|
|
69794
|
+
const full = {
|
|
69795
|
+
...entry,
|
|
69796
|
+
removedAt: now()
|
|
69797
|
+
};
|
|
69798
|
+
byId.set(full.deviceId, full);
|
|
69799
|
+
byPair.set(pairKey(full.addonId, full.stableId), full);
|
|
69800
|
+
},
|
|
69801
|
+
isIdFenced(deviceId) {
|
|
69802
|
+
return fresh(byId.get(deviceId));
|
|
69803
|
+
},
|
|
69804
|
+
isPairFenced(addonId, stableId) {
|
|
69805
|
+
return fresh(byPair.get(pairKey(addonId, stableId)));
|
|
69806
|
+
},
|
|
69807
|
+
lift(addonId, stableId) {
|
|
69808
|
+
const entry = byPair.get(pairKey(addonId, stableId));
|
|
69809
|
+
if (entry === void 0) return;
|
|
69810
|
+
byPair.delete(pairKey(addonId, stableId));
|
|
69811
|
+
byId.delete(entry.deviceId);
|
|
69812
|
+
},
|
|
69813
|
+
async runUnfenced(fn) {
|
|
69814
|
+
suspensions += 1;
|
|
69815
|
+
try {
|
|
69816
|
+
return await fn();
|
|
69817
|
+
} finally {
|
|
69818
|
+
suspensions -= 1;
|
|
69819
|
+
}
|
|
69820
|
+
}
|
|
69821
|
+
};
|
|
69822
|
+
}
|
|
69714
69823
|
var DeviceManagerAddon = class extends require_dist10.BaseAddon {
|
|
69715
69824
|
constructor() {
|
|
69716
69825
|
super({});
|
|
@@ -69951,6 +70060,7 @@ var require_device_manager_addon = __commonJS({
|
|
|
69951
70060
|
registry,
|
|
69952
70061
|
bindingsDeps: this.bindingsDeps,
|
|
69953
70062
|
migrationGuard: createMigrationGuard(),
|
|
70063
|
+
removalFence: createRemovalFence(),
|
|
69954
70064
|
requireDeviceOps,
|
|
69955
70065
|
stampIntegrationId: stampIntegrationId$1,
|
|
69956
70066
|
get provider() {
|
|
@@ -241146,6 +241256,53 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
241146
241256
|
const ids = [...this._permanentRestoreFailures.keys()].join(", ");
|
|
241147
241257
|
return `${this._permanentRestoreFailures.size} device(s) permanently failed restore (deviceIds: ${ids}) \u2014 restart the ${this.providerName} provider to retry`;
|
|
241148
241258
|
}
|
|
241259
|
+
/**
|
|
241260
|
+
* A top-level persisted row with NO configuration is a leftover, not a
|
|
241261
|
+
* device — quarantine it instead of dialling it.
|
|
241262
|
+
*
|
|
241263
|
+
* Device 614 was deleted, resurrected as a config-less shell by a write
|
|
241264
|
+
* already in flight (D376), and then dialled at every single boot: eight
|
|
241265
|
+
* `Failed to restore device — bounded retry scheduled` lines in one day,
|
|
241266
|
+
* each one the same Zod refusal on `host` and `password`. The bound was
|
|
241267
|
+
* real, but it is a bound PER PROCESS, and the runner restarts — so nothing
|
|
241268
|
+
* about the row ever stopped costing attempts and log lines.
|
|
241269
|
+
*
|
|
241270
|
+
* No retry can add a configuration that is not there. The row is reported
|
|
241271
|
+
* once, with its `deviceId`, and set aside.
|
|
241272
|
+
*
|
|
241273
|
+
* Top-level only: an accessory child legitimately carries an empty blob (it
|
|
241274
|
+
* lives off its parent), and a hub-adopted child with an emptied blob is
|
|
241275
|
+
* repaired by `healSavedConfig` from its parent instead. Providers that
|
|
241276
|
+
* genuinely create configuration-free top-level devices turn this off.
|
|
241277
|
+
*/
|
|
241278
|
+
quarantineConfiglessTopLevelRestores = true;
|
|
241279
|
+
isUnrestorableShell(saved) {
|
|
241280
|
+
if (!this.quarantineConfiglessTopLevelRestores) return false;
|
|
241281
|
+
if (saved.parentDeviceId !== null) return false;
|
|
241282
|
+
const config = saved.config;
|
|
241283
|
+
if (config === void 0) return false;
|
|
241284
|
+
return Object.keys(config).length === 0;
|
|
241285
|
+
}
|
|
241286
|
+
quarantineShell(saved) {
|
|
241287
|
+
this._permanentRestoreFailures.set(saved.id, {
|
|
241288
|
+
deviceId: saved.id,
|
|
241289
|
+
stableId: saved.stableId,
|
|
241290
|
+
type: saved.type,
|
|
241291
|
+
attempts: 0,
|
|
241292
|
+
lastError: "persisted row has no configuration",
|
|
241293
|
+
failedAt: Date.now()
|
|
241294
|
+
});
|
|
241295
|
+
this.ctx.logger.error("Persisted device row has NO configuration \u2014 quarantined: not restored, not retried. Remove it (deviceManager.removeDevice) or re-add the device.", {
|
|
241296
|
+
tags: {
|
|
241297
|
+
deviceId: saved.id,
|
|
241298
|
+
stableId: saved.stableId
|
|
241299
|
+
},
|
|
241300
|
+
meta: {
|
|
241301
|
+
type: saved.type,
|
|
241302
|
+
provider: this.providerName
|
|
241303
|
+
}
|
|
241304
|
+
});
|
|
241305
|
+
}
|
|
241149
241306
|
cancelRestoreRetries() {
|
|
241150
241307
|
this._restoreRetryScheduler?.cancel();
|
|
241151
241308
|
this._restoreRetryScheduler = null;
|
|
@@ -241331,6 +241488,10 @@ ${recipe.triggers.map((t, i) => emitTrigger(t, i)).join("\n\n")}
|
|
|
241331
241488
|
});
|
|
241332
241489
|
return;
|
|
241333
241490
|
}
|
|
241491
|
+
if (this.isUnrestorableShell(saved)) {
|
|
241492
|
+
this.quarantineShell(saved);
|
|
241493
|
+
return;
|
|
241494
|
+
}
|
|
241334
241495
|
try {
|
|
241335
241496
|
await attemptRestore(saved);
|
|
241336
241497
|
} catch (err) {
|