@alfe.ai/integrations 0.1.4 → 0.1.5
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 +56 -7
- package/dist/index.js +32 -12
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -8,6 +8,14 @@ import { Manager } from "@alfe.ai/mcp-bundler";
|
|
|
8
8
|
*
|
|
9
9
|
* Calls GET /integrations/registry (public, no auth required) and caches the result.
|
|
10
10
|
* The API URL can be passed explicitly or set via ALFE_API_URL env var.
|
|
11
|
+
*
|
|
12
|
+
* The cache is time-bounded by a TTL (default 60s). Long-running consumers — chiefly
|
|
13
|
+
* the agent daemon, which constructs the Registry once at startup and never restarts —
|
|
14
|
+
* would otherwise be pinned to the registry snapshot taken at process boot, and could
|
|
15
|
+
* never resolve a version published after the daemon came up (the "stale cache" bug).
|
|
16
|
+
* With a TTL, every consumer self-heals within a bounded window. The resolve-for-install
|
|
17
|
+
* path additionally forces a fresh read (see `load({ fresh: true })`) because installing
|
|
18
|
+
* a specific version is a rare, correctness-critical action that must never race the TTL.
|
|
11
19
|
*/
|
|
12
20
|
interface RegistryEntry {
|
|
13
21
|
/** Human-readable display name (e.g. 'Alfe Voice') */
|
|
@@ -69,26 +77,51 @@ interface RegistryIndex {
|
|
|
69
77
|
type RegistryFetcher = () => Promise<(RegistryEntry & {
|
|
70
78
|
id: string;
|
|
71
79
|
})[]>;
|
|
80
|
+
/** Default cache TTL — refetch the registry index after this many ms. */
|
|
81
|
+
declare const DEFAULT_REGISTRY_TTL_MS = 60000;
|
|
82
|
+
interface RegistryOptions {
|
|
83
|
+
/**
|
|
84
|
+
* How long (ms) a loaded index is considered fresh before `load()` refetches.
|
|
85
|
+
* Defaults to {@link DEFAULT_REGISTRY_TTL_MS} (60s). A non-positive value
|
|
86
|
+
* disables time-based caching (every `load()` refetches).
|
|
87
|
+
*/
|
|
88
|
+
ttlMs?: number;
|
|
89
|
+
}
|
|
90
|
+
/** Options for a single `load()` call. */
|
|
91
|
+
interface LoadOptions {
|
|
92
|
+
/** Bypass the cache and refetch the index even if it's still within TTL. */
|
|
93
|
+
fresh?: boolean;
|
|
94
|
+
}
|
|
72
95
|
declare class Registry {
|
|
73
96
|
private index;
|
|
97
|
+
private loadedAt;
|
|
74
98
|
private fetcher;
|
|
99
|
+
private ttlMs;
|
|
75
100
|
/**
|
|
76
101
|
* @param fetcher - Function that fetches the integrations array from the registry API.
|
|
77
102
|
* Typically backed by api-client's IntegrationsService.getRegistry().
|
|
103
|
+
* @param options - Optional cache configuration (TTL).
|
|
78
104
|
*/
|
|
79
|
-
constructor(fetcher: RegistryFetcher);
|
|
105
|
+
constructor(fetcher: RegistryFetcher, options?: RegistryOptions);
|
|
106
|
+
/** True when the cached index is absent or older than the TTL. */
|
|
107
|
+
private isStale;
|
|
80
108
|
/**
|
|
81
|
-
* Load the registry index.
|
|
109
|
+
* Load the registry index. Returns the cached index while it's still within
|
|
110
|
+
* TTL; refetches once the cache is stale (or when `{ fresh: true }` is passed).
|
|
82
111
|
*/
|
|
83
|
-
load(): Promise<RegistryIndex>;
|
|
112
|
+
load(options?: LoadOptions): Promise<RegistryIndex>;
|
|
84
113
|
/**
|
|
85
|
-
* Force reload the index (bypass cache).
|
|
114
|
+
* Force reload the index (bypass cache). Equivalent to `load({ fresh: true })`.
|
|
86
115
|
*/
|
|
87
116
|
reload(): Promise<RegistryIndex>;
|
|
88
117
|
/**
|
|
89
118
|
* Get a specific integration entry by name.
|
|
119
|
+
*
|
|
120
|
+
* @param fresh - When true, force a fresh fetch before reading (used by the
|
|
121
|
+
* resolve-for-install path so a stale version list never blocks
|
|
122
|
+
* a just-published version).
|
|
90
123
|
*/
|
|
91
|
-
get(id: string): Promise<RegistryEntry | undefined>;
|
|
124
|
+
get(id: string, fresh?: boolean): Promise<RegistryEntry | undefined>;
|
|
92
125
|
/**
|
|
93
126
|
* List all integrations in the registry.
|
|
94
127
|
*/
|
|
@@ -115,6 +148,20 @@ interface ResolvedIntegration {
|
|
|
115
148
|
subdir?: string;
|
|
116
149
|
description: string;
|
|
117
150
|
}
|
|
151
|
+
/** Options for a single `resolve()` call. */
|
|
152
|
+
interface ResolveOptions {
|
|
153
|
+
/**
|
|
154
|
+
* Bypass the registry cache and read a fresh index before resolving.
|
|
155
|
+
*
|
|
156
|
+
* The install/reconcile path sets this: installing a specific version is a
|
|
157
|
+
* rare, user-triggered, correctness-critical action that must never resolve
|
|
158
|
+
* against a stale cached version list (the cause of the
|
|
159
|
+
* `Version "x" not found … Available versions: …` install failure on a
|
|
160
|
+
* long-running daemon). High-frequency read-only uses (marketplace
|
|
161
|
+
* listing/search) leave it unset and keep the TTL cache.
|
|
162
|
+
*/
|
|
163
|
+
fresh?: boolean;
|
|
164
|
+
}
|
|
118
165
|
declare class RegistryResolveError extends Error {
|
|
119
166
|
constructor(message: string);
|
|
120
167
|
}
|
|
@@ -126,9 +173,11 @@ declare class Resolver {
|
|
|
126
173
|
*
|
|
127
174
|
* @param name - Integration name (e.g. "discord")
|
|
128
175
|
* @param version - Specific version (e.g. "1.0.0") or undefined for latest
|
|
176
|
+
* @param options - Pass `{ fresh: true }` to bypass the registry cache
|
|
177
|
+
* (used by the install/reconcile path).
|
|
129
178
|
* @returns Resolved integration with repo URL and commit hash
|
|
130
179
|
*/
|
|
131
|
-
resolve(id: string, version?: string): Promise<ResolvedIntegration>;
|
|
180
|
+
resolve(id: string, version?: string, options?: ResolveOptions): Promise<ResolvedIntegration>;
|
|
132
181
|
/**
|
|
133
182
|
* Check if an integration exists in the registry.
|
|
134
183
|
*/
|
|
@@ -875,4 +924,4 @@ declare class IntegrationManagerAdapter implements IIntegrationManager {
|
|
|
875
924
|
resetReinstallAttempts(integrationId: string): void;
|
|
876
925
|
}
|
|
877
926
|
//#endregion
|
|
878
|
-
export { type CredentialsResolver, type HookEnvOptions, type HookResult, type IIntegrationManager, type InstalledInfo, Installer, InstallerError, type IntegrationConfigureParams, type IntegrationHealthParams, type IntegrationInfo, type IntegrationInstallParams, IntegrationManager, IntegrationManagerAdapter, type IntegrationManagerOptions, type IntegrationRemoveParams, LockManager, McpApplier, type McpApplierOptions, OpenClawApplier, type OpenClawApplierOptions, type PlatformContext, Registry, type RegistryEntry, type RegistryFetcher, type RegistryIndex, RegistryResolveError, type ResolvedIntegration, Resolver, type RuntimeApplier, type RuntimeDesiredState, type RuntimeLockFile, type RuntimePluginEntry, type RuntimeSkillEntry, StateManager, buildHookEnv, resolveInstallsForRuntime, runHook, runHookWithContext };
|
|
927
|
+
export { type CredentialsResolver, DEFAULT_REGISTRY_TTL_MS, 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, OpenClawApplier, type OpenClawApplierOptions, type PlatformContext, Registry, type RegistryEntry, type RegistryFetcher, type RegistryIndex, type RegistryOptions, RegistryResolveError, type ResolveOptions, type ResolvedIntegration, Resolver, type RuntimeApplier, type RuntimeDesiredState, type RuntimeLockFile, type RuntimePluginEntry, type RuntimeSkillEntry, StateManager, buildHookEnv, resolveInstallsForRuntime, runHook, runHookWithContext };
|
package/dist/index.js
CHANGED
|
@@ -6,21 +6,34 @@ import { closeSync, copyFileSync, cpSync, existsSync, mkdirSync, mkdtempSync, op
|
|
|
6
6
|
import { buildConfigValidationSchema, parseManifestFile } from "@alfe.ai/integration-manifest";
|
|
7
7
|
import { createLogger } from "@auriclabs/logger";
|
|
8
8
|
//#region src/registry.ts
|
|
9
|
+
/** Default cache TTL — refetch the registry index after this many ms. */
|
|
10
|
+
const DEFAULT_REGISTRY_TTL_MS = 6e4;
|
|
9
11
|
var Registry = class {
|
|
10
12
|
index = null;
|
|
13
|
+
loadedAt = 0;
|
|
11
14
|
fetcher;
|
|
15
|
+
ttlMs;
|
|
12
16
|
/**
|
|
13
17
|
* @param fetcher - Function that fetches the integrations array from the registry API.
|
|
14
18
|
* Typically backed by api-client's IntegrationsService.getRegistry().
|
|
19
|
+
* @param options - Optional cache configuration (TTL).
|
|
15
20
|
*/
|
|
16
|
-
constructor(fetcher) {
|
|
21
|
+
constructor(fetcher, options = {}) {
|
|
17
22
|
this.fetcher = fetcher;
|
|
23
|
+
this.ttlMs = options.ttlMs ?? 6e4;
|
|
24
|
+
}
|
|
25
|
+
/** True when the cached index is absent or older than the TTL. */
|
|
26
|
+
isStale() {
|
|
27
|
+
if (!this.index) return true;
|
|
28
|
+
if (this.ttlMs <= 0) return true;
|
|
29
|
+
return Date.now() - this.loadedAt >= this.ttlMs;
|
|
18
30
|
}
|
|
19
31
|
/**
|
|
20
|
-
* Load the registry index.
|
|
32
|
+
* Load the registry index. Returns the cached index while it's still within
|
|
33
|
+
* TTL; refetches once the cache is stale (or when `{ fresh: true }` is passed).
|
|
21
34
|
*/
|
|
22
|
-
async load() {
|
|
23
|
-
if (this.index) return this.index;
|
|
35
|
+
async load(options = {}) {
|
|
36
|
+
if (!options.fresh && this.index && !this.isStale()) return this.index;
|
|
24
37
|
const raw = await this.fetcher();
|
|
25
38
|
const integrations = {};
|
|
26
39
|
for (const entry of raw) {
|
|
@@ -31,20 +44,25 @@ var Registry = class {
|
|
|
31
44
|
version: 1,
|
|
32
45
|
integrations
|
|
33
46
|
};
|
|
47
|
+
this.loadedAt = Date.now();
|
|
34
48
|
return this.index;
|
|
35
49
|
}
|
|
36
50
|
/**
|
|
37
|
-
* Force reload the index (bypass cache).
|
|
51
|
+
* Force reload the index (bypass cache). Equivalent to `load({ fresh: true })`.
|
|
38
52
|
*/
|
|
39
53
|
async reload() {
|
|
40
54
|
this.index = null;
|
|
41
|
-
return this.load();
|
|
55
|
+
return this.load({ fresh: true });
|
|
42
56
|
}
|
|
43
57
|
/**
|
|
44
58
|
* Get a specific integration entry by name.
|
|
59
|
+
*
|
|
60
|
+
* @param fresh - When true, force a fresh fetch before reading (used by the
|
|
61
|
+
* resolve-for-install path so a stale version list never blocks
|
|
62
|
+
* a just-published version).
|
|
45
63
|
*/
|
|
46
|
-
async get(id) {
|
|
47
|
-
return (await this.load()).integrations[id];
|
|
64
|
+
async get(id, fresh = false) {
|
|
65
|
+
return (await this.load({ fresh })).integrations[id];
|
|
48
66
|
}
|
|
49
67
|
/**
|
|
50
68
|
* List all integrations in the registry.
|
|
@@ -83,10 +101,12 @@ var Resolver = class {
|
|
|
83
101
|
*
|
|
84
102
|
* @param name - Integration name (e.g. "discord")
|
|
85
103
|
* @param version - Specific version (e.g. "1.0.0") or undefined for latest
|
|
104
|
+
* @param options - Pass `{ fresh: true }` to bypass the registry cache
|
|
105
|
+
* (used by the install/reconcile path).
|
|
86
106
|
* @returns Resolved integration with repo URL and commit hash
|
|
87
107
|
*/
|
|
88
|
-
async resolve(id, version) {
|
|
89
|
-
const entry = await this.registry.get(id);
|
|
108
|
+
async resolve(id, version, options = {}) {
|
|
109
|
+
const entry = await this.registry.get(id, options.fresh);
|
|
90
110
|
if (!entry) throw new RegistryResolveError(`Integration "${id}" not found in registry`);
|
|
91
111
|
const resolvedVersion = version && version.length > 0 ? version : entry.latest;
|
|
92
112
|
if (!entry.versions.includes(resolvedVersion)) throw new RegistryResolveError(`Version "${resolvedVersion}" not found for integration "${id}". Available versions: ${entry.versions.join(", ")}`);
|
|
@@ -966,7 +986,7 @@ var IntegrationManager = class {
|
|
|
966
986
|
resolved = buildCustomResolved(name, customSource);
|
|
967
987
|
this.log.info(`Custom Connection install: ${name} from ${resolved.repository}@${resolved.commit}`);
|
|
968
988
|
} else {
|
|
969
|
-
resolved = await this.resolver.resolve(name, version);
|
|
989
|
+
resolved = await this.resolver.resolve(name, version, { fresh: true });
|
|
970
990
|
this.log.info(`Resolved ${name}@${resolved.version} from ${resolved.repository}`);
|
|
971
991
|
}
|
|
972
992
|
const installPath = await this.installer.install(resolved);
|
|
@@ -2157,4 +2177,4 @@ var IntegrationManagerAdapter = class {
|
|
|
2157
2177
|
}
|
|
2158
2178
|
};
|
|
2159
2179
|
//#endregion
|
|
2160
|
-
export { Installer, InstallerError, IntegrationManager, IntegrationManagerAdapter, LockManager, McpApplier, OpenClawApplier, Registry, RegistryResolveError, Resolver, StateManager, buildHookEnv, resolveInstallsForRuntime, runHook, runHookWithContext };
|
|
2180
|
+
export { DEFAULT_REGISTRY_TTL_MS, Installer, InstallerError, IntegrationManager, IntegrationManagerAdapter, LockManager, McpApplier, OpenClawApplier, Registry, RegistryResolveError, Resolver, StateManager, buildHookEnv, resolveInstallsForRuntime, runHook, runHookWithContext };
|
package/package.json
CHANGED