@alfe.ai/integrations 0.6.0 → 0.6.2

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/README.md CHANGED
@@ -45,7 +45,8 @@ import {
45
45
  } from '@alfe.ai/integrations';
46
46
 
47
47
  // ── Registry + Resolution ──────────────────────────────────
48
- const registry = new Registry();
48
+ const registryFetcher = () => apiClient.integrations.getRegistry();
49
+ const registry = new Registry(registryFetcher);
49
50
  const results = await registry.search('discord');
50
51
 
51
52
  const resolver = new Resolver(registry);
@@ -56,8 +57,9 @@ const installPath = await installer.install(resolved);
56
57
 
57
58
  // ── Lifecycle Management ───────────────────────────────────
58
59
  const manager = new IntegrationManager({
59
- logger,
60
- skillsDir: '~/.alfe/skills/', // default
60
+ registryFetcher,
61
+ runtimeAppliers: new Map([['openclaw', openClawApplier]]),
62
+ mcpApplier,
61
63
  });
62
64
 
63
65
  await manager.install({ name: 'discord', version: '1.0.0' });
@@ -73,20 +75,20 @@ cloudClient.setIntegrationManager(adapter);
73
75
 
74
76
  ## Configuration
75
77
 
76
- The `Registry` class resolves the API URL in this order:
77
-
78
- 1. Explicit `apiUrl` constructor argument
79
- 2. `ALFE_API_URL` environment variable
80
- 3. `https://api.alfe.ai` (production default)
78
+ `Registry` is transport-independent. Callers provide a `RegistryFetcher`,
79
+ normally backed by `@alfe.ai/api-client`; the package does not read an API URL
80
+ or perform HTTP directly.
81
81
 
82
82
  The `IntegrationManager` accepts an options object:
83
83
 
84
84
  ```typescript
85
85
  interface IntegrationManagerOptions {
86
- logger?: Logger;
87
- statePath?: string; // default: ~/.alfe/integrations.json
86
+ registryFetcher: RegistryFetcher;
87
+ statePath?: string; // default: ~/.alfe/integrations.json
88
88
  integrationsDir?: string; // default: ~/.alfe/integrations/
89
- skillsDir?: string; // default: ~/.alfe/skills/
89
+ lockPath?: string; // default: ~/.alfe/runtime-lock.json
90
+ runtimeAppliers?: Map<string, RuntimeApplier>;
91
+ mcpApplier?: McpApplier; // required when manifests declare mcp_servers
90
92
  }
91
93
  ```
92
94
 
package/dist/index.d.ts CHANGED
@@ -95,6 +95,7 @@ interface LoadOptions {
95
95
  declare class Registry {
96
96
  private index;
97
97
  private loadedAt;
98
+ private loadPromise;
98
99
  private fetcher;
99
100
  private ttlMs;
100
101
  /**
@@ -249,12 +250,14 @@ declare class Installer {
249
250
  */
250
251
  stage(resolved: ResolvedIntegration): Promise<string>;
251
252
  /**
252
- * Commit a previously-staged clone: remove the live install dir and rename
253
- * the staged dir into its place. Same-filesystem `renameSync` makes the swap
254
- * near-atomic (no window where the install dir is half-populated). Called
255
- * inside the runtime-suspension window during a diff-based upgrade.
253
+ * Commit a previously-staged clone with a recoverable same-filesystem swap.
254
+ * The live dir first moves to a deterministic backup, then the staged dir
255
+ * moves into place. A failed second rename restores the prior install; a
256
+ * daemon crash is recovered on the next `getInstallPath()` call.
256
257
  */
257
258
  commitStaged(name: string, stagedPath: string): void;
259
+ /** Restore a live install left in the deterministic backup by a killed swap. */
260
+ private recoverInterruptedSwap;
258
261
  /**
259
262
  * Remove any orphaned `.staging-*` directories under the base path. Called at
260
263
  * the start of `stage()` so a daemon killed mid-upgrade doesn't accumulate
@@ -313,6 +316,7 @@ declare class Installer {
313
316
  * Check if an integration is installed.
314
317
  */
315
318
  isInstalled(name: string): boolean;
319
+ private validateResolved;
316
320
  }
317
321
  //#endregion
318
322
  //#region src/runtime-applier.d.ts
@@ -525,6 +529,7 @@ interface IntegrationManagerOptions {
525
529
  logger?: unknown;
526
530
  statePath?: string;
527
531
  integrationsDir?: string;
532
+ /** @deprecated Skills directories are owned by each RuntimeApplier. */
528
533
  skillsDir?: string;
529
534
  /** Runtime appliers keyed by runtime name */
530
535
  runtimeAppliers?: Map<string, RuntimeApplier>;
@@ -536,7 +541,8 @@ interface IntegrationManagerOptions {
536
541
  * Runtime-agnostic MCP server applier. The manager routes
537
542
  * `mcp_servers` declarations through it once per integration (the
538
543
  * applier writes to the bundler store + openclaw.json mirror).
539
- * Required there is no fallback path; integrations with
544
+ * Operationally required when manifests declare MCP servers. There is no
545
+ * fallback path; integrations with
540
546
  * `mcp_servers` will skip MCP registration silently if this is
541
547
  * omitted (the warn log makes this visible).
542
548
  */
@@ -731,8 +737,10 @@ declare class IntegrationManager {
731
737
  /**
732
738
  * Build a hook-failure message. A hook we SIGKILLed at its timeout is rendered
733
739
  * as `(timed out after <ms>ms)` so it is visually distinguishable in Sentry
734
- * from a genuine non-zero exit `(exit <code>)` the two have very different
735
- * root causes (Sentry AGENT-DAEMON-6).
740
+ * from a genuine non-zero exit `(exit <code>)`. Captured output is deliberately
741
+ * excluded: hooks inherit daemon credentials and receive integration secrets,
742
+ * so stdout/stderr is a secret-bearing channel that must not enter durable
743
+ * state, logs, or user-facing error messages.
736
744
  */
737
745
  private hookFailureMessage;
738
746
  /**
@@ -754,9 +762,9 @@ declare class IntegrationManager {
754
762
  * (and everything a sibling integration still claims) in place. Phase 4's
755
763
  * `activate` re-applies and re-locks the new set immediately after.
756
764
  *
757
- * `removeEntries` clears this integration's lock rows and returns what it had;
758
- * a candidate removal is skipped when it is EITHER (a) still declared by the
759
- * new manifest, or (b) still claimed by another integration.
765
+ * The existing lock remains intact until every physical removal and MCP prune
766
+ * succeeds. This makes partial cleanup retryable; only then are the old rows
767
+ * cleared so activate() can record the new manifest's contributions.
760
768
  */
761
769
  private applyUpgradeDiffRemovals;
762
770
  /**
@@ -773,7 +781,6 @@ declare class IntegrationManager {
773
781
  //#region src/state.d.ts
774
782
  declare class StateManager {
775
783
  private filePath;
776
- private lockHeld;
777
784
  constructor(filePath?: string);
778
785
  /**
779
786
  * Read the current state file. Returns empty state if file doesn't exist.
@@ -1230,8 +1237,9 @@ declare class OpenClawApplier implements RuntimeApplier {
1230
1237
  * Drop a set of dotted keys from a dot-free parent object via read-drop-write,
1231
1238
  * UNLOCKED. If the parent becomes empty, `config unset` it; otherwise
1232
1239
  * `--replace` the shrunk map (siblings survive because they remain in
1233
- * `remaining`). Warn-tolerant a failed drop of an already-gone key must not
1234
- * fail the caller. Shared by `removeConfig` (whole-integration teardown) and
1240
+ * `remaining`). A proven already-absent path is idempotent success; every
1241
+ * other write failure propagates so callers retain the ownership record for a
1242
+ * later retry. Shared by `removeConfig` (whole-integration teardown) and
1235
1243
  * `applyConfig`'s stale-key diff (per-key removal between manifest versions).
1236
1244
  *
1237
1245
  * Assumes the shared CLI lock is already held by the calling public method —
@@ -1255,10 +1263,11 @@ declare class OpenClawApplier implements RuntimeApplier {
1255
1263
  * the provider subtree is unset for `models.providers.zhipu.baseUrl`, the
1256
1264
  * follow-up `.apiKey` / `.models` leaves skip re-issuing the parent unset.
1257
1265
  *
1258
- * Warn-tolerant (a failed unset of an already-gone key must never fail the
1259
- * caller) and assumes the shared CLI lock is held stays an `*Unlocked`
1260
- * internal since the lock is NOT re-entrant. Shared by `removeConfig`
1261
- * (whole-integration teardown) and `applyConfig`'s stale-leaf diff.
1266
+ * A proven already-absent path is idempotent success; every other failure
1267
+ * propagates so the ownership ledger remains a retry record. Assumes the
1268
+ * shared CLI lock is held stays an `*Unlocked` internal since the lock is
1269
+ * NOT re-entrant. Shared by `removeConfig` (whole-integration teardown) and
1270
+ * `applyConfig`'s stale-leaf diff.
1262
1271
  */
1263
1272
  private unsetLeafPathUnlocked;
1264
1273
  /**
@@ -1768,6 +1777,11 @@ interface RuntimeLockFile {
1768
1777
  runtimes: Record<string, RuntimeDesiredState>;
1769
1778
  updatedAt: string;
1770
1779
  }
1780
+ interface IntegrationRuntimeEntries {
1781
+ plugins: RuntimePluginEntry[];
1782
+ skills: RuntimeSkillEntry[];
1783
+ config: RuntimeConfigEntry[];
1784
+ }
1771
1785
  declare class LockManager {
1772
1786
  private filePath;
1773
1787
  constructor(filePath?: string);
@@ -1789,6 +1803,12 @@ declare class LockManager {
1789
1803
  addEntries(runtime: string, integrationId: string, version: string, plugins: PluginInstall[], skills: SkillInstall[], installPath: string, opts?: {
1790
1804
  configApplied?: boolean;
1791
1805
  }): void;
1806
+ /**
1807
+ * Snapshot an integration's entries without changing the lock. Teardown uses
1808
+ * this first and only clears the entries after every physical removal
1809
+ * succeeds, making a failed cleanup retryable.
1810
+ */
1811
+ getEntriesForIntegration(integrationId: string): Record<string, IntegrationRuntimeEntries>;
1792
1812
  /**
1793
1813
  * Remove all entries for a given integration across all runtimes.
1794
1814
  * Returns what was removed, keyed by runtime.
@@ -1797,11 +1817,7 @@ declare class LockManager {
1797
1817
  * plugins, skills, OR config there — so `deactivate` drives
1798
1818
  * `applier.removeConfig` even for a config-only integration.
1799
1819
  */
1800
- removeEntries(integrationId: string): Record<string, {
1801
- plugins: RuntimePluginEntry[];
1802
- skills: RuntimeSkillEntry[];
1803
- config: RuntimeConfigEntry[];
1804
- }>;
1820
+ removeEntries(integrationId: string): Record<string, IntegrationRuntimeEntries>;
1805
1821
  /**
1806
1822
  * Get the full desired state for a specific runtime.
1807
1823
  */
@@ -1889,4 +1905,4 @@ declare class IntegrationManagerAdapter implements IIntegrationManager {
1889
1905
  resetReinstallAttempts(integrationId: string): void;
1890
1906
  }
1891
1907
  //#endregion
1892
- export { ClaudeCodeApplier, type ClaudeCodeApplierOptions, ClaudeCodeMcpSync, type ClaudeCodeMcpSyncOptions, type CredentialsResolver, DEFAULT_REGISTRY_TTL_MS, HermesApplier, type HermesApplierOptions, HermesMcpSync, type HermesMcpSyncOptions, type HookEnvOptions, type HookResult, type IIntegrationManager, type InstalledInfo, Installer, InstallerError, type IntegrationConfigureParams, type IntegrationHealthParams, type IntegrationInfo, type IntegrationInstallParams, IntegrationManager, IntegrationManagerAdapter, type IntegrationManagerOptions, type IntegrationRemoveParams, type LoadOptions, LockManager, McpApplier, type McpApplierOptions, type McpStoreReader, NoopOpenClawCliLock, OpenClawApplier, type OpenClawApplierOptions, type OpenClawCliLock, type PlatformContext, Registry, type RegistryEntry, type RegistryFetcher, type RegistryIndex, type RegistryOptions, RegistryResolveError, type ResolveOptions, type ResolvedIntegration, Resolver, type RuntimeApplier, type RuntimeConfigEntry, type RuntimeDesiredState, type RuntimeLockFile, type RuntimePluginEntry, type RuntimeSkillEntry, SerialOpenClawCliLock, StateManager, buildHookEnv, resolveInstallsForRuntime, runHook, runHookWithContext, upsertEnvVar };
1908
+ export { ClaudeCodeApplier, type ClaudeCodeApplierOptions, ClaudeCodeMcpSync, type ClaudeCodeMcpSyncOptions, type CredentialsResolver, DEFAULT_REGISTRY_TTL_MS, HermesApplier, type HermesApplierOptions, HermesMcpSync, type HermesMcpSyncOptions, type HookEnvOptions, type HookResult, type IIntegrationManager, type InstalledInfo, Installer, InstallerError, type IntegrationConfigureParams, type IntegrationHealthParams, type IntegrationInfo, type IntegrationInstallParams, IntegrationManager, IntegrationManagerAdapter, type IntegrationManagerOptions, type IntegrationRemoveParams, type IntegrationRuntimeEntries, type LoadOptions, LockManager, McpApplier, type McpApplierOptions, type McpStoreReader, NoopOpenClawCliLock, OpenClawApplier, type OpenClawApplierOptions, type OpenClawCliLock, type PlatformContext, Registry, type RegistryEntry, type RegistryFetcher, type RegistryIndex, type RegistryOptions, RegistryResolveError, type ResolveOptions, type ResolvedIntegration, Resolver, type RuntimeApplier, type RuntimeConfigEntry, type RuntimeDesiredState, type RuntimeLockFile, type RuntimePluginEntry, type RuntimeSkillEntry, SerialOpenClawCliLock, StateManager, buildHookEnv, resolveInstallsForRuntime, runHook, runHookWithContext, upsertEnvVar };