@camstack/addon-import-alexa 0.2.184 → 0.2.185
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/CHANGELOG.md +7 -0
- package/dist/addon.js +38 -10
- package/dist/addon.mjs +38 -10
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.185] - 2026-11-04
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Alexa starts without waiting for the first appliance list and state poll, so a large account no longer exceeds the runner init budget during an update
|
|
15
|
+
- Alexa broker migrations no longer read as 'skipped-newer' (and no false 'Database is newer' alert) after the addon boots
|
|
16
|
+
|
|
10
17
|
## [0.2.184] - 2026-11-04
|
|
11
18
|
|
|
12
19
|
### Changed
|
package/dist/addon.js
CHANGED
|
@@ -6044,7 +6044,7 @@ function number(params) {
|
|
|
6044
6044
|
return /* @__PURE__ */ _coercedNumber(ZodNumber, params);
|
|
6045
6045
|
}
|
|
6046
6046
|
//#endregion
|
|
6047
|
-
//#region ../types/dist/sleep-
|
|
6047
|
+
//#region ../types/dist/sleep-Cv4Zmie9.mjs
|
|
6048
6048
|
/**
|
|
6049
6049
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
6050
6050
|
* window to float samples (D455).
|
|
@@ -6644,7 +6644,7 @@ var NOT_RUN = {
|
|
|
6644
6644
|
* throws: an unreachable or refusing engine is logged and reported `declared: false`,
|
|
6645
6645
|
* because the hub (and this addon's boot) must not hang on a migration.
|
|
6646
6646
|
*/
|
|
6647
|
-
async function runAddonMigrations(host, migrations, logger) {
|
|
6647
|
+
async function runAddonMigrations(host, migrations, logger, options = {}) {
|
|
6648
6648
|
const first = migrations[0];
|
|
6649
6649
|
if (first === void 0) return NOT_RUN;
|
|
6650
6650
|
const owner = first.meta.owner;
|
|
@@ -6673,7 +6673,9 @@ async function runAddonMigrations(host, migrations, logger) {
|
|
|
6673
6673
|
else if (status === "blocked") blocked.push(id);
|
|
6674
6674
|
else skipped.push(id);
|
|
6675
6675
|
};
|
|
6676
|
+
const selected = options.only === void 0 ? null : new Set(options.only);
|
|
6676
6677
|
for (const step of declaration.steps) {
|
|
6678
|
+
if (selected !== null && !selected.has(step.id)) continue;
|
|
6677
6679
|
const bound = byId.get(step.id);
|
|
6678
6680
|
if (bound === void 0 || step.action === "skip") {
|
|
6679
6681
|
skipped.push(step.id);
|
|
@@ -7833,14 +7835,18 @@ var BaseAddon = class {
|
|
|
7833
7835
|
* nothing; the boot continues. A migration that FAILED (or was blocked by one)
|
|
7834
7836
|
* and declares `onFailure: 'block-owner'` makes this throw, so the addon does not
|
|
7835
7837
|
* serve data whose shape it cannot trust; `tolerate` migrations never do.
|
|
7838
|
+
*
|
|
7839
|
+
* ALWAYS pass the whole catalogue; a phase that must run only some of it passes
|
|
7840
|
+
* `{ only: [ids] }` — never a filtered catalogue (the engine would judge the
|
|
7841
|
+
* omitted applied ids "newer").
|
|
7836
7842
|
*/
|
|
7837
|
-
async runMigrations(migrations) {
|
|
7843
|
+
async runMigrations(migrations, options) {
|
|
7838
7844
|
const api = this.ctx.api.migrations;
|
|
7839
7845
|
const report = await runAddonMigrations({
|
|
7840
7846
|
declareOwner: (input) => api.declareOwner.mutate(input),
|
|
7841
7847
|
startMigration: (input) => api.startMigration.mutate(input),
|
|
7842
7848
|
finishMigration: (input) => api.finishMigration.mutate(input)
|
|
7843
|
-
}, migrations, this.ctx.logger);
|
|
7849
|
+
}, migrations, this.ctx.logger, options);
|
|
7844
7850
|
if (report.blockOwner.length > 0) throw new AddonMigrationsBlockedError(report.blockOwner);
|
|
7845
7851
|
return report;
|
|
7846
7852
|
}
|
|
@@ -14672,7 +14678,13 @@ var MigrationLedgerEntrySchema = object({
|
|
|
14672
14678
|
* joined by the engine from the catalogues it knows this boot. Absent when no
|
|
14673
14679
|
* catalogue lists the id (a newer database).
|
|
14674
14680
|
*/
|
|
14675
|
-
meta: MigrationMetaSchema.optional()
|
|
14681
|
+
meta: MigrationMetaSchema.optional(),
|
|
14682
|
+
/**
|
|
14683
|
+
* Set when this is an applied row of an addon owner whose catalogue the answering
|
|
14684
|
+
* process has not loaded (the owner has not declared yet): the row is applied, the
|
|
14685
|
+
* process just cannot judge it. Never `skipped-newer` — that needs the owner loaded.
|
|
14686
|
+
*/
|
|
14687
|
+
ownerNotLoaded: boolean().optional()
|
|
14676
14688
|
});
|
|
14677
14689
|
/** A catalogued migration that is not applied, with where it stands. */
|
|
14678
14690
|
var MigrationPendingEntrySchema = object({
|
|
@@ -141638,10 +141650,8 @@ var AlexaIntegrationManager = class {
|
|
|
141638
141650
|
this.#status = "connected";
|
|
141639
141651
|
this.#lastCheckedAt = Date.now();
|
|
141640
141652
|
this.#onConnected(this.#id);
|
|
141641
|
-
await this.refreshAppliances();
|
|
141642
|
-
await this.poll();
|
|
141643
|
-
this.#schedulePoll();
|
|
141644
141653
|
this.#logger.info("AlexaIntegrationManager: started", { tags: { brokerId: this.#id } });
|
|
141654
|
+
this.#firstSync(client);
|
|
141645
141655
|
} catch (err) {
|
|
141646
141656
|
if (err instanceof AlexaAuthError) {
|
|
141647
141657
|
this.#status = "auth-failed";
|
|
@@ -141662,6 +141672,24 @@ var AlexaIntegrationManager = class {
|
|
|
141662
141672
|
});
|
|
141663
141673
|
}
|
|
141664
141674
|
}
|
|
141675
|
+
/** Background first refresh + poll, then the poll loop. Does nothing once `client` is stale. */
|
|
141676
|
+
async #firstSync(client) {
|
|
141677
|
+
let refreshed = false;
|
|
141678
|
+
try {
|
|
141679
|
+
await this.refreshAppliances();
|
|
141680
|
+
refreshed = true;
|
|
141681
|
+
} catch (err) {
|
|
141682
|
+
this.#error = errMsg(err);
|
|
141683
|
+
this.#logger.warn("AlexaIntegrationManager: first appliance refresh failed", {
|
|
141684
|
+
tags: { brokerId: this.#id },
|
|
141685
|
+
meta: { error: errMsg(err) }
|
|
141686
|
+
});
|
|
141687
|
+
}
|
|
141688
|
+
if (this.#client !== client) return;
|
|
141689
|
+
if (refreshed) await this.poll();
|
|
141690
|
+
if (this.#client !== client) return;
|
|
141691
|
+
this.#schedulePoll();
|
|
141692
|
+
}
|
|
141665
141693
|
/** List the account's appliances + publish them to the resolver. */
|
|
141666
141694
|
async refreshAppliances() {
|
|
141667
141695
|
const client = this.#client;
|
|
@@ -145116,7 +145144,7 @@ var AlexaImportAddon = class extends BaseDeviceProvider {
|
|
|
145116
145144
|
infoOf: (id) => this.registry?.get(id) ?? null,
|
|
145117
145145
|
onChanged: (id) => this.syncBrokerRuntime(id)
|
|
145118
145146
|
});
|
|
145119
|
-
await this.runMigrations(defineAddonMigrations(ADDON_MIGRATIONS
|
|
145147
|
+
await this.runMigrations(defineAddonMigrations(ADDON_MIGRATIONS, this.migrationPorts(records)), { only: [FLATTEN_BROKER_RECORDS_ID] });
|
|
145120
145148
|
this.activeBrokers = await this.loadPersistedBrokers();
|
|
145121
145149
|
this.registry = new AlexaBrokerRegistry(this.ctx.logger, {
|
|
145122
145150
|
dataDir: this.ctx.dataDir,
|
|
@@ -145161,7 +145189,7 @@ var AlexaImportAddon = class extends BaseDeviceProvider {
|
|
|
145161
145189
|
deviceCode: this.deviceCodeLogin,
|
|
145162
145190
|
commitCookie: (input) => this.commitCapturedCookie(input)
|
|
145163
145191
|
}), ...speakIconRoutes()];
|
|
145164
|
-
await this.runMigrations(defineAddonMigrations(ADDON_MIGRATIONS.
|
|
145192
|
+
await this.runMigrations(defineAddonMigrations(ADDON_MIGRATIONS, this.migrationPorts(records)), { only: ADDON_MIGRATIONS.map((m) => m.meta.id).filter((id) => id !== FLATTEN_BROKER_RECORDS_ID) });
|
|
145165
145193
|
const speak = new AlexaSpeakController({
|
|
145166
145194
|
logger: this.ctx.logger.child("speak"),
|
|
145167
145195
|
readConfig: () => this.config,
|
package/dist/addon.mjs
CHANGED
|
@@ -6042,7 +6042,7 @@ function number(params) {
|
|
|
6042
6042
|
return /* @__PURE__ */ _coercedNumber(ZodNumber, params);
|
|
6043
6043
|
}
|
|
6044
6044
|
//#endregion
|
|
6045
|
-
//#region ../types/dist/sleep-
|
|
6045
|
+
//#region ../types/dist/sleep-Cv4Zmie9.mjs
|
|
6046
6046
|
/**
|
|
6047
6047
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
6048
6048
|
* window to float samples (D455).
|
|
@@ -6642,7 +6642,7 @@ var NOT_RUN = {
|
|
|
6642
6642
|
* throws: an unreachable or refusing engine is logged and reported `declared: false`,
|
|
6643
6643
|
* because the hub (and this addon's boot) must not hang on a migration.
|
|
6644
6644
|
*/
|
|
6645
|
-
async function runAddonMigrations(host, migrations, logger) {
|
|
6645
|
+
async function runAddonMigrations(host, migrations, logger, options = {}) {
|
|
6646
6646
|
const first = migrations[0];
|
|
6647
6647
|
if (first === void 0) return NOT_RUN;
|
|
6648
6648
|
const owner = first.meta.owner;
|
|
@@ -6671,7 +6671,9 @@ async function runAddonMigrations(host, migrations, logger) {
|
|
|
6671
6671
|
else if (status === "blocked") blocked.push(id);
|
|
6672
6672
|
else skipped.push(id);
|
|
6673
6673
|
};
|
|
6674
|
+
const selected = options.only === void 0 ? null : new Set(options.only);
|
|
6674
6675
|
for (const step of declaration.steps) {
|
|
6676
|
+
if (selected !== null && !selected.has(step.id)) continue;
|
|
6675
6677
|
const bound = byId.get(step.id);
|
|
6676
6678
|
if (bound === void 0 || step.action === "skip") {
|
|
6677
6679
|
skipped.push(step.id);
|
|
@@ -7831,14 +7833,18 @@ var BaseAddon = class {
|
|
|
7831
7833
|
* nothing; the boot continues. A migration that FAILED (or was blocked by one)
|
|
7832
7834
|
* and declares `onFailure: 'block-owner'` makes this throw, so the addon does not
|
|
7833
7835
|
* serve data whose shape it cannot trust; `tolerate` migrations never do.
|
|
7836
|
+
*
|
|
7837
|
+
* ALWAYS pass the whole catalogue; a phase that must run only some of it passes
|
|
7838
|
+
* `{ only: [ids] }` — never a filtered catalogue (the engine would judge the
|
|
7839
|
+
* omitted applied ids "newer").
|
|
7834
7840
|
*/
|
|
7835
|
-
async runMigrations(migrations) {
|
|
7841
|
+
async runMigrations(migrations, options) {
|
|
7836
7842
|
const api = this.ctx.api.migrations;
|
|
7837
7843
|
const report = await runAddonMigrations({
|
|
7838
7844
|
declareOwner: (input) => api.declareOwner.mutate(input),
|
|
7839
7845
|
startMigration: (input) => api.startMigration.mutate(input),
|
|
7840
7846
|
finishMigration: (input) => api.finishMigration.mutate(input)
|
|
7841
|
-
}, migrations, this.ctx.logger);
|
|
7847
|
+
}, migrations, this.ctx.logger, options);
|
|
7842
7848
|
if (report.blockOwner.length > 0) throw new AddonMigrationsBlockedError(report.blockOwner);
|
|
7843
7849
|
return report;
|
|
7844
7850
|
}
|
|
@@ -14670,7 +14676,13 @@ var MigrationLedgerEntrySchema = object({
|
|
|
14670
14676
|
* joined by the engine from the catalogues it knows this boot. Absent when no
|
|
14671
14677
|
* catalogue lists the id (a newer database).
|
|
14672
14678
|
*/
|
|
14673
|
-
meta: MigrationMetaSchema.optional()
|
|
14679
|
+
meta: MigrationMetaSchema.optional(),
|
|
14680
|
+
/**
|
|
14681
|
+
* Set when this is an applied row of an addon owner whose catalogue the answering
|
|
14682
|
+
* process has not loaded (the owner has not declared yet): the row is applied, the
|
|
14683
|
+
* process just cannot judge it. Never `skipped-newer` — that needs the owner loaded.
|
|
14684
|
+
*/
|
|
14685
|
+
ownerNotLoaded: boolean().optional()
|
|
14674
14686
|
});
|
|
14675
14687
|
/** A catalogued migration that is not applied, with where it stands. */
|
|
14676
14688
|
var MigrationPendingEntrySchema = object({
|
|
@@ -141636,10 +141648,8 @@ var AlexaIntegrationManager = class {
|
|
|
141636
141648
|
this.#status = "connected";
|
|
141637
141649
|
this.#lastCheckedAt = Date.now();
|
|
141638
141650
|
this.#onConnected(this.#id);
|
|
141639
|
-
await this.refreshAppliances();
|
|
141640
|
-
await this.poll();
|
|
141641
|
-
this.#schedulePoll();
|
|
141642
141651
|
this.#logger.info("AlexaIntegrationManager: started", { tags: { brokerId: this.#id } });
|
|
141652
|
+
this.#firstSync(client);
|
|
141643
141653
|
} catch (err) {
|
|
141644
141654
|
if (err instanceof AlexaAuthError) {
|
|
141645
141655
|
this.#status = "auth-failed";
|
|
@@ -141660,6 +141670,24 @@ var AlexaIntegrationManager = class {
|
|
|
141660
141670
|
});
|
|
141661
141671
|
}
|
|
141662
141672
|
}
|
|
141673
|
+
/** Background first refresh + poll, then the poll loop. Does nothing once `client` is stale. */
|
|
141674
|
+
async #firstSync(client) {
|
|
141675
|
+
let refreshed = false;
|
|
141676
|
+
try {
|
|
141677
|
+
await this.refreshAppliances();
|
|
141678
|
+
refreshed = true;
|
|
141679
|
+
} catch (err) {
|
|
141680
|
+
this.#error = errMsg(err);
|
|
141681
|
+
this.#logger.warn("AlexaIntegrationManager: first appliance refresh failed", {
|
|
141682
|
+
tags: { brokerId: this.#id },
|
|
141683
|
+
meta: { error: errMsg(err) }
|
|
141684
|
+
});
|
|
141685
|
+
}
|
|
141686
|
+
if (this.#client !== client) return;
|
|
141687
|
+
if (refreshed) await this.poll();
|
|
141688
|
+
if (this.#client !== client) return;
|
|
141689
|
+
this.#schedulePoll();
|
|
141690
|
+
}
|
|
141663
141691
|
/** List the account's appliances + publish them to the resolver. */
|
|
141664
141692
|
async refreshAppliances() {
|
|
141665
141693
|
const client = this.#client;
|
|
@@ -145114,7 +145142,7 @@ var AlexaImportAddon = class extends BaseDeviceProvider {
|
|
|
145114
145142
|
infoOf: (id) => this.registry?.get(id) ?? null,
|
|
145115
145143
|
onChanged: (id) => this.syncBrokerRuntime(id)
|
|
145116
145144
|
});
|
|
145117
|
-
await this.runMigrations(defineAddonMigrations(ADDON_MIGRATIONS
|
|
145145
|
+
await this.runMigrations(defineAddonMigrations(ADDON_MIGRATIONS, this.migrationPorts(records)), { only: [FLATTEN_BROKER_RECORDS_ID] });
|
|
145118
145146
|
this.activeBrokers = await this.loadPersistedBrokers();
|
|
145119
145147
|
this.registry = new AlexaBrokerRegistry(this.ctx.logger, {
|
|
145120
145148
|
dataDir: this.ctx.dataDir,
|
|
@@ -145159,7 +145187,7 @@ var AlexaImportAddon = class extends BaseDeviceProvider {
|
|
|
145159
145187
|
deviceCode: this.deviceCodeLogin,
|
|
145160
145188
|
commitCookie: (input) => this.commitCapturedCookie(input)
|
|
145161
145189
|
}), ...speakIconRoutes()];
|
|
145162
|
-
await this.runMigrations(defineAddonMigrations(ADDON_MIGRATIONS.
|
|
145190
|
+
await this.runMigrations(defineAddonMigrations(ADDON_MIGRATIONS, this.migrationPorts(records)), { only: ADDON_MIGRATIONS.map((m) => m.meta.id).filter((id) => id !== FLATTEN_BROKER_RECORDS_ID) });
|
|
145163
145191
|
const speak = new AlexaSpeakController({
|
|
145164
145192
|
logger: this.ctx.logger.child("speak"),
|
|
145165
145193
|
readConfig: () => this.config,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@camstack/addon-import-alexa",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.185",
|
|
4
4
|
"description": "Alexa device-import provider for CamStack — imports the smart-home devices in a user's Alexa account via the unofficial alexa-remote2 cookie/token client (the inverse of the Alexa exporter)",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"camstack",
|
|
@@ -72,7 +72,7 @@
|
|
|
72
72
|
}
|
|
73
73
|
],
|
|
74
74
|
"requires": {
|
|
75
|
-
"server": ">=1.2.
|
|
75
|
+
"server": ">=1.2.763"
|
|
76
76
|
}
|
|
77
77
|
},
|
|
78
78
|
"files": [
|