@alfe.ai/integrations 0.3.2 → 0.4.0

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/index.d.ts CHANGED
@@ -359,6 +359,22 @@ interface RuntimeApplier {
359
359
  * its absence and degrade gracefully.
360
360
  */
361
361
  setConfigRaw?(key: string, value: string): Promise<void>;
362
+ /**
363
+ * Optional: read a single raw config key back from the runtime, returning the
364
+ * scalar value as a string (or `undefined` when the key is unset / can't be
365
+ * read). Used by the daemon's config-reconcile pass (plan A4) to value-diff
366
+ * before writing (skip when equal) and to verify a write landed.
367
+ *
368
+ * MUST normalize the runtime's `config get` output to the same scalar string
369
+ * that `setConfigRaw` was called with (e.g. unwrap the JSON quoting OpenClaw's
370
+ * `config get` adds), so a `getConfigRaw(k) === want` compare is exact.
371
+ *
372
+ * Read-back can be a false negative under load (a 2-vCPU box running a
373
+ * `config get` while the runtime hot-reloads) — callers MUST retry with the
374
+ * same cadence as a write verify, never one-shot. Appliers that can't read
375
+ * config omit this method; callers degrade gracefully (blind set, no diff).
376
+ */
377
+ getConfigRaw?(key: string): Promise<string | undefined>;
362
378
  /** Check if this runtime is available (e.g. workspace directory exists) */
363
379
  isAvailable(): Promise<boolean>;
364
380
  }
@@ -1218,6 +1234,21 @@ declare class OpenClawApplier implements RuntimeApplier {
1218
1234
  * config writes trigger.
1219
1235
  */
1220
1236
  setConfigRaw(key: string, value: string): Promise<void>;
1237
+ /**
1238
+ * Read a single config key back — `openclaw config get <key>` — and normalize
1239
+ * to the scalar string that `setConfigRaw` would have written. `config get`
1240
+ * emits JSON, so a scalar comes back quoted (`"claude-sonnet-4-6"`); parse it
1241
+ * to the raw value so a `=== want` compare in the daemon's config-reconcile
1242
+ * pass is exact.
1243
+ *
1244
+ * Returns `undefined` when the key is unset, the command fails, or the runtime
1245
+ * REDACTS the value (a sensitive key we can't compare — treat as "unknown" so
1246
+ * the caller doesn't spuriously diff). Under the shared CLI lock so it can't
1247
+ * interleave with a concurrent mutation. NOT self-healing on a malformed state
1248
+ * DB — a read failure returns `undefined`, and the caller's set path (which IS
1249
+ * healing) recovers.
1250
+ */
1251
+ getConfigRaw(key: string): Promise<string | undefined>;
1221
1252
  isAvailable(): Promise<boolean>;
1222
1253
  private readTracking;
1223
1254
  private writeTracking;
@@ -1279,6 +1310,14 @@ declare class HermesApplier implements RuntimeApplier {
1279
1310
  * per-integration removal accounting). Reuses the serialized queue.
1280
1311
  */
1281
1312
  setConfigRaw(key: string, value: string): Promise<void>;
1313
+ /**
1314
+ * Read a single config key back — `hermes config get <key>` — normalized to
1315
+ * the scalar string `setConfigRaw` would have written (used by the daemon's
1316
+ * config-reconcile value-diff + verify, plan A4). Returns `undefined` when the
1317
+ * key is unset or the command fails. Serialized onto the same config queue so
1318
+ * it can't interleave with an in-flight `hermes config set`.
1319
+ */
1320
+ getConfigRaw(key: string): Promise<string | undefined>;
1282
1321
  /**
1283
1322
  * Apply a plugin. The `@alfe.ai/openclaw-*` npm plugins are OpenClaw-specific
1284
1323
  * (they carry the `openclaw` peer dep) and do NOT apply to Hermes — log + skip
package/dist/index.js CHANGED
@@ -2980,6 +2980,46 @@ var OpenClawApplier = class {
2980
2980
  setConfigRaw(key, value) {
2981
2981
  return this.cliLock.run(() => this.runConfigSetUnlocked([key, value]));
2982
2982
  }
2983
+ /**
2984
+ * Read a single config key back — `openclaw config get <key>` — and normalize
2985
+ * to the scalar string that `setConfigRaw` would have written. `config get`
2986
+ * emits JSON, so a scalar comes back quoted (`"claude-sonnet-4-6"`); parse it
2987
+ * to the raw value so a `=== want` compare in the daemon's config-reconcile
2988
+ * pass is exact.
2989
+ *
2990
+ * Returns `undefined` when the key is unset, the command fails, or the runtime
2991
+ * REDACTS the value (a sensitive key we can't compare — treat as "unknown" so
2992
+ * the caller doesn't spuriously diff). Under the shared CLI lock so it can't
2993
+ * interleave with a concurrent mutation. NOT self-healing on a malformed state
2994
+ * DB — a read failure returns `undefined`, and the caller's set path (which IS
2995
+ * healing) recovers.
2996
+ */
2997
+ getConfigRaw(key) {
2998
+ return this.cliLock.run(async () => {
2999
+ try {
3000
+ const { stdout } = await execFileAsync$1("openclaw", [
3001
+ "config",
3002
+ "get",
3003
+ key
3004
+ ], { timeout: 1e4 });
3005
+ const trimmed = stdout.trim();
3006
+ if (!trimmed) return void 0;
3007
+ let parsed;
3008
+ try {
3009
+ parsed = JSON.parse(trimmed);
3010
+ } catch {
3011
+ return trimmed;
3012
+ }
3013
+ if (parsed === null || parsed === void 0) return void 0;
3014
+ if (parsed === OPENCLAW_REDACTED) return void 0;
3015
+ if (typeof parsed === "string") return parsed;
3016
+ if (typeof parsed === "number" || typeof parsed === "boolean") return String(parsed);
3017
+ return JSON.stringify(parsed);
3018
+ } catch {
3019
+ return;
3020
+ }
3021
+ });
3022
+ }
2983
3023
  isAvailable() {
2984
3024
  return Promise.resolve(existsSync(this.home));
2985
3025
  }
@@ -3185,6 +3225,40 @@ var HermesApplier = class {
3185
3225
  return this.runConfigSet([key, value]);
3186
3226
  }
3187
3227
  /**
3228
+ * Read a single config key back — `hermes config get <key>` — normalized to
3229
+ * the scalar string `setConfigRaw` would have written (used by the daemon's
3230
+ * config-reconcile value-diff + verify, plan A4). Returns `undefined` when the
3231
+ * key is unset or the command fails. Serialized onto the same config queue so
3232
+ * it can't interleave with an in-flight `hermes config set`.
3233
+ */
3234
+ getConfigRaw(key) {
3235
+ const run = async () => {
3236
+ try {
3237
+ const { stdout } = await execFileAsync("hermes", [
3238
+ "config",
3239
+ "get",
3240
+ key
3241
+ ], { timeout: 1e4 });
3242
+ const trimmed = stdout.trim();
3243
+ if (!trimmed) return void 0;
3244
+ try {
3245
+ const parsed = JSON.parse(trimmed);
3246
+ if (parsed === null || parsed === void 0) return void 0;
3247
+ if (typeof parsed === "string") return parsed;
3248
+ if (typeof parsed === "number" || typeof parsed === "boolean") return String(parsed);
3249
+ return JSON.stringify(parsed);
3250
+ } catch {
3251
+ return trimmed;
3252
+ }
3253
+ } catch {
3254
+ return;
3255
+ }
3256
+ };
3257
+ const result = this.configSetQueue.then(run, run);
3258
+ this.configSetQueue = result.catch(() => void 0);
3259
+ return result;
3260
+ }
3261
+ /**
3188
3262
  * Apply a plugin. The `@alfe.ai/openclaw-*` npm plugins are OpenClaw-specific
3189
3263
  * (they carry the `openclaw` peer dep) and do NOT apply to Hermes — log + skip
3190
3264
  * them. The manager catches per-plugin, so a skip here is a correct no-op.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfe.ai/integrations",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "description": "Integration lifecycle management for Alfe — registry, resolution, installation, and state",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",