@alfe.ai/integrations 0.2.2 → 0.2.3

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.
Files changed (2) hide show
  1. package/dist/index.js +33 -22
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -1713,27 +1713,38 @@ const delay$1 = (ms) => new Promise((resolve) => {
1713
1713
  setTimeout(resolve, ms);
1714
1714
  });
1715
1715
  /**
1716
- * Structural equality for config read-back comparison. Primitives compare by
1717
- * `Object.is`; arrays are order-SENSITIVE (config arrays are positional);
1718
- * plain objects are order-INSENSITIVE (key order in JSON is not meaningful).
1719
- * Used only to confirm a value landed after a `config set` exited non-zero.
1716
+ * Asymmetric structural containment for verify-after-write: is every field we
1717
+ * INTENDED present in the ACTUAL read-back with our value? `actual` may carry
1718
+ * EXTRA keys that `intended` does not.
1719
+ *
1720
+ * Verify-after-write only needs to answer "did our write land?", and OpenClaw
1721
+ * NORMALIZES config on load — most notably it enriches every
1722
+ * `models.providers.*.models` entry with schema defaults (`reasoning`, `input`,
1723
+ * `cost`, `contextWindow`, `maxTokens`, …) that the applier never wrote. A
1724
+ * strict `deepEqual` against the enriched read-back is therefore GUARANTEED to
1725
+ * fail for model lists (extra keys → unequal key counts), which strands the
1726
+ * `alfe` activation in `error` forever even though the write succeeded. A subset
1727
+ * check tolerates enrichment while still catching a genuine failure (a value
1728
+ * OpenClaw never stored, or stored differently).
1729
+ *
1730
+ * Arrays stay positional and length-EXACT (enrichment adds object fields, never
1731
+ * elements, and never reorders); each element is compared by subset. Objects
1732
+ * require every `intended` key to be present-and-subset-matching in `actual`;
1733
+ * primitives compare by `Object.is`.
1720
1734
  */
1721
- function deepEqual(a, b) {
1722
- if (Object.is(a, b)) return true;
1723
- if (typeof a !== "object" || typeof b !== "object" || a === null || b === null) return false;
1724
- const aIsArr = Array.isArray(a);
1725
- const bIsArr = Array.isArray(b);
1726
- if (aIsArr !== bIsArr) return false;
1727
- if (aIsArr && bIsArr) {
1728
- if (a.length !== b.length) return false;
1729
- return a.every((v, i) => deepEqual(v, b[i]));
1730
- }
1731
- const ao = a;
1732
- const bo = b;
1733
- const aKeys = Object.keys(ao);
1734
- const bKeys = Object.keys(bo);
1735
- if (aKeys.length !== bKeys.length) return false;
1736
- return aKeys.every((k) => Object.prototype.hasOwnProperty.call(bo, k) && deepEqual(ao[k], bo[k]));
1735
+ function deepSubset(intended, actual) {
1736
+ if (Object.is(intended, actual)) return true;
1737
+ if (typeof intended !== "object" || typeof actual !== "object" || intended === null || actual === null) return false;
1738
+ const iIsArr = Array.isArray(intended);
1739
+ const aIsArr = Array.isArray(actual);
1740
+ if (iIsArr !== aIsArr) return false;
1741
+ if (iIsArr && aIsArr) {
1742
+ if (intended.length !== actual.length) return false;
1743
+ return intended.every((v, i) => deepSubset(v, actual[i]));
1744
+ }
1745
+ const io = intended;
1746
+ const ao = actual;
1747
+ return Object.keys(io).every((k) => Object.prototype.hasOwnProperty.call(ao, k) && deepSubset(io[k], ao[k]));
1737
1748
  }
1738
1749
  /**
1739
1750
  * Describe a `config set` target WITHOUT leaking values. The value argument is
@@ -1844,7 +1855,7 @@ var OpenClawApplier = class {
1844
1855
  ], { timeout: 1e4 });
1845
1856
  const actual = JSON.parse(stdout.trim());
1846
1857
  if (actual === OPENCLAW_REDACTED) return true;
1847
- return deepEqual(actual, expected);
1858
+ return deepSubset(expected, actual);
1848
1859
  } catch {
1849
1860
  return false;
1850
1861
  }
@@ -2094,7 +2105,7 @@ var OpenClawApplier = class {
2094
2105
  } catch (err) {
2095
2106
  if (await this.verifyApplied(async () => {
2096
2107
  const after = await readParentObject(parentPath);
2097
- return [...dottedKvs.entries()].every(([k, v]) => deepEqual(after[k], v));
2108
+ return [...dottedKvs.entries()].every(([k, v]) => deepSubset(v, after[k]));
2098
2109
  })) {
2099
2110
  log$3.warn({ parentPath }, "openclaw config set exited non-zero but config landed — continuing");
2100
2111
  continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfe.ai/integrations",
3
- "version": "0.2.2",
3
+ "version": "0.2.3",
4
4
  "description": "Integration lifecycle management for Alfe — registry, resolution, installation, and state",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",