@alfe.ai/integrations 0.0.2 → 0.0.4

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
@@ -8,11 +8,10 @@
8
8
  interface RegistryEntry {
9
9
  /** Human-readable display name (e.g. 'Alfe Voice') */
10
10
  name?: string;
11
- repo: string;
12
- /** GitHub repository URL (HTTPS, no .git suffix) for public clone */
13
- repository?: string;
14
- /** Pinned commit SHA — when present, install uses this exact commit instead of a tag */
15
- commit?: string;
11
+ /** GitHub repository URL (HTTPS) for public clone */
12
+ repository: string;
13
+ /** Git commit SHA — installer clones and checks out this exact commit */
14
+ commit: string;
16
15
  /** Subdirectory within the repo where the integration lives (for monorepos) */
17
16
  subdir?: string;
18
17
  versions: string[];
@@ -58,14 +57,21 @@ interface RegistryIndex {
58
57
  version: number;
59
58
  integrations: Record<string, RegistryEntry>;
60
59
  }
60
+ /**
61
+ * Fetcher function that returns the raw integrations array from the registry API.
62
+ * Callers wire this up using their api-client instance so Registry stays dependency-free.
63
+ */
64
+ type RegistryFetcher = () => Promise<(RegistryEntry & {
65
+ id: string;
66
+ })[]>;
61
67
  declare class Registry {
62
68
  private index;
63
- private apiUrl;
69
+ private fetcher;
64
70
  /**
65
- * @param apiUrl - Base URL for the Alfe API (e.g. "https://api.alfe.ai").
66
- * Falls back to ALFE_API_URL env var, then the production URL.
71
+ * @param fetcher - Function that fetches the integrations array from the registry API.
72
+ * Typically backed by api-client's IntegrationsService.getRegistry().
67
73
  */
68
- constructor(apiUrl?: string);
74
+ constructor(fetcher: RegistryFetcher);
69
75
  /**
70
76
  * Load the registry index. Uses cache if already loaded.
71
77
  */
@@ -96,11 +102,10 @@ declare class Registry {
96
102
  interface ResolvedIntegration {
97
103
  id: string;
98
104
  name?: string;
99
- repo: string;
105
+ repository: string;
100
106
  version: string;
101
- tag: string;
102
- /** Pinned commit SHA — when present, installer uses this instead of tag */
103
- commit?: string;
107
+ /** Git commit SHA to checkout after clone */
108
+ commit: string;
104
109
  /** Subdirectory within the repo where the integration lives (for monorepos) */
105
110
  subdir?: string;
106
111
  description: string;
@@ -116,7 +121,7 @@ declare class Resolver {
116
121
  *
117
122
  * @param name - Integration name (e.g. "discord")
118
123
  * @param version - Specific version (e.g. "1.0.0") or undefined for latest
119
- * @returns Resolved integration with repo URL and git tag
124
+ * @returns Resolved integration with repo URL and commit hash
120
125
  */
121
126
  resolve(id: string, version?: string): Promise<ResolvedIntegration>;
122
127
  /**
@@ -158,7 +163,7 @@ declare class Installer {
158
163
  */
159
164
  getClonePath(name: string): string;
160
165
  /**
161
- * Install an integration by cloning its git repo and checking out the version tag.
166
+ * Install an integration by cloning its git repo and checking out the pinned commit.
162
167
  * Returns the effective install path (with subdir if present).
163
168
  */
164
169
  install(resolved: ResolvedIntegration): Promise<string>;
@@ -280,6 +285,8 @@ interface IntegrationManifest {
280
285
  config_schema: ConfigSchemaField[];
281
286
  capabilities: string[];
282
287
  hooks: IntegrationHooks;
288
+ /** Git repository URL (HTTPS, with .git suffix) — used by installer to clone */
289
+ repository: string;
283
290
  /**
284
291
  * Agent runtimes this integration supports.
285
292
  * If omitted or empty, the integration is considered universal (all runtimes).
@@ -364,6 +371,8 @@ interface IntegrationManagerOptions {
364
371
  runtimeAppliers?: Map<string, RuntimeApplier>;
365
372
  /** Override lock file path (defaults to ~/.alfe/runtime-lock.json) */
366
373
  lockPath?: string;
374
+ /** Fetcher for the integration registry — should be wired to api-client */
375
+ registryFetcher: RegistryFetcher;
367
376
  }
368
377
  interface ManagerResponse {
369
378
  ok: boolean;
@@ -400,7 +409,7 @@ declare class IntegrationManager {
400
409
  private lockManager;
401
410
  /** In-memory secret store — NEVER persisted to disk */
402
411
  private secrets;
403
- constructor(options?: IntegrationManagerOptions);
412
+ constructor(options: IntegrationManagerOptions);
404
413
  /**
405
414
  * Install an integration from the registry.
406
415
  *
@@ -714,4 +723,4 @@ declare class IntegrationManagerAdapter implements IIntegrationManager {
714
723
  uninstall(integrationId: string): Promise<void>;
715
724
  }
716
725
  //#endregion
717
- export { 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, OpenClawApplier, type OpenClawApplierOptions, Registry, type RegistryEntry, type RegistryIndex, RegistryResolveError, type ResolvedIntegration, Resolver, type RuntimeApplier, type RuntimeDesiredState, type RuntimeLockFile, type RuntimePluginEntry, type RuntimeSkillEntry, StateManager, buildHookEnv, resolveInstallsForRuntime, runHook, runHookWithContext };
726
+ export { 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, OpenClawApplier, type OpenClawApplierOptions, 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 };
package/dist/index.js CHANGED
@@ -6,37 +6,27 @@ import { cpSync, existsSync, mkdirSync, readFileSync, readdirSync, rmSync, write
6
6
  import { buildConfigValidationSchema, parseManifestFile } from "@alfe.ai/integration-manifest";
7
7
  import { createLogger } from "@auriclabs/logger";
8
8
  //#region src/registry.ts
9
- const DEFAULT_API_URL = "https://api.alfe.ai";
10
- const REGISTRY_PATH = "/integrations/registry";
11
9
  var Registry = class {
12
10
  index = null;
13
- apiUrl;
11
+ fetcher;
14
12
  /**
15
- * @param apiUrl - Base URL for the Alfe API (e.g. "https://api.alfe.ai").
16
- * Falls back to ALFE_API_URL env var, then the production URL.
13
+ * @param fetcher - Function that fetches the integrations array from the registry API.
14
+ * Typically backed by api-client's IntegrationsService.getRegistry().
17
15
  */
18
- constructor(apiUrl) {
19
- this.apiUrl = (apiUrl ?? process.env.ALFE_API_URL ?? DEFAULT_API_URL).replace(/\/+$/, "");
16
+ constructor(fetcher) {
17
+ this.fetcher = fetcher;
20
18
  }
21
19
  /**
22
20
  * Load the registry index. Uses cache if already loaded.
23
21
  */
24
22
  async load() {
25
23
  if (this.index) return this.index;
26
- const url = `${this.apiUrl}${REGISTRY_PATH}`;
27
- const res = await fetch(url);
28
- if (!res.ok) throw new Error(`Failed to fetch registry index from ${url}: ${String(res.status)} ${res.statusText}`);
29
- const body = await res.json();
30
- if (!body.success || !body.data?.integrations) throw new Error(`Invalid registry response from ${url}`);
31
- const raw = body.data.integrations;
32
- let integrations;
33
- if (Array.isArray(raw)) {
34
- integrations = {};
35
- for (const entry of raw) {
36
- const { id, ...rest } = entry;
37
- integrations[id] = rest;
38
- }
39
- } else integrations = raw;
24
+ const raw = await this.fetcher();
25
+ const integrations = {};
26
+ for (const entry of raw) {
27
+ const { id, ...rest } = entry;
28
+ integrations[id] = rest;
29
+ }
40
30
  this.index = {
41
31
  version: 1,
42
32
  integrations
@@ -93,19 +83,18 @@ var Resolver = class {
93
83
  *
94
84
  * @param name - Integration name (e.g. "discord")
95
85
  * @param version - Specific version (e.g. "1.0.0") or undefined for latest
96
- * @returns Resolved integration with repo URL and git tag
86
+ * @returns Resolved integration with repo URL and commit hash
97
87
  */
98
88
  async resolve(id, version) {
99
89
  const entry = await this.registry.get(id);
100
90
  if (!entry) throw new RegistryResolveError(`Integration "${id}" not found in registry`);
101
- const resolvedVersion = version ?? entry.latest;
91
+ const resolvedVersion = version && version.length > 0 ? version : entry.latest;
102
92
  if (!entry.versions.includes(resolvedVersion)) throw new RegistryResolveError(`Version "${resolvedVersion}" not found for integration "${id}". Available versions: ${entry.versions.join(", ")}`);
103
93
  return {
104
94
  id,
105
95
  name: entry.name,
106
- repo: entry.repository ? `${entry.repository}.git` : entry.repo,
96
+ repository: entry.repository,
107
97
  version: resolvedVersion,
108
- tag: `v${resolvedVersion}`,
109
98
  commit: entry.commit,
110
99
  subdir: entry.subdir,
111
100
  description: entry.description
@@ -129,10 +118,10 @@ var Resolver = class {
129
118
  //#endregion
130
119
  //#region src/installer.ts
131
120
  /**
132
- * Installer — git clone/fetch integrations to ~/.alfe/integrations/{name}/.
121
+ * Installer — git clone integrations to ~/.alfe/integrations/{name}/.
133
122
  *
134
123
  * Manages the local installation directory for integration repos.
135
- * Each integration is a shallow git clone checked out to a version tag.
124
+ * Each integration is cloned and checked out to a pinned commit hash.
136
125
  */
137
126
  const execFileAsync$1 = promisify(execFile);
138
127
  const INTEGRATIONS_DIR = join(homedir(), ".alfe", "integrations");
@@ -170,7 +159,7 @@ var Installer = class {
170
159
  return join(this.basePath, name);
171
160
  }
172
161
  /**
173
- * Install an integration by cloning its git repo and checking out the version tag.
162
+ * Install an integration by cloning its git repo and checking out the pinned commit.
174
163
  * Returns the effective install path (with subdir if present).
175
164
  */
176
165
  async install(resolved) {
@@ -179,25 +168,15 @@ var Installer = class {
179
168
  if (resolved.subdir) this.subdirs.set(resolved.id, resolved.subdir);
180
169
  mkdirSync(this.basePath, { recursive: true });
181
170
  try {
182
- if (resolved.commit) {
183
- await execFileAsync$1("git", [
184
- "clone",
185
- resolved.repo,
186
- clonePath
187
- ], { timeout: GIT_TIMEOUT_MS });
188
- await execFileAsync$1("git", ["checkout", resolved.commit], {
189
- cwd: clonePath,
190
- timeout: GIT_TIMEOUT_MS
191
- });
192
- } else await execFileAsync$1("git", [
171
+ await execFileAsync$1("git", [
193
172
  "clone",
194
- "--depth",
195
- "1",
196
- "--branch",
197
- resolved.tag,
198
- resolved.repo,
173
+ resolved.repository,
199
174
  clonePath
200
175
  ], { timeout: GIT_TIMEOUT_MS });
176
+ await execFileAsync$1("git", ["checkout", resolved.commit], {
177
+ cwd: clonePath,
178
+ timeout: GIT_TIMEOUT_MS
179
+ });
201
180
  } catch (err) {
202
181
  if (existsSync(clonePath)) rmSync(clonePath, {
203
182
  recursive: true,
@@ -218,18 +197,11 @@ var Installer = class {
218
197
  if (resolved.subdir) this.subdirs.set(name, resolved.subdir);
219
198
  else this.subdirs.delete(name);
220
199
  try {
221
- await execFileAsync$1("git", [
222
- "fetch",
223
- "--depth",
224
- "1",
225
- "origin",
226
- `tag`,
227
- resolved.tag
228
- ], {
200
+ await execFileAsync$1("git", ["fetch", "origin"], {
229
201
  cwd: clonePath,
230
202
  timeout: GIT_TIMEOUT_MS
231
203
  });
232
- await execFileAsync$1("git", ["checkout", resolved.tag], {
204
+ await execFileAsync$1("git", ["checkout", resolved.commit], {
233
205
  cwd: clonePath,
234
206
  timeout: GIT_TIMEOUT_MS
235
207
  });
@@ -686,12 +658,12 @@ var IntegrationManager = class {
686
658
  /** In-memory secret store — NEVER persisted to disk */
687
659
  secrets = /* @__PURE__ */ new Map();
688
660
  constructor(options) {
689
- this.state = new StateManager(options?.statePath);
690
- this.registry = new Registry();
661
+ this.state = new StateManager(options.statePath);
662
+ this.registry = new Registry(options.registryFetcher);
691
663
  this.resolver = new Resolver(this.registry);
692
- this.installer = new Installer(options?.integrationsDir);
693
- this.runtimeAppliers = options?.runtimeAppliers ?? /* @__PURE__ */ new Map();
694
- this.lockManager = new LockManager(options?.lockPath);
664
+ this.installer = new Installer(options.integrationsDir);
665
+ this.runtimeAppliers = options.runtimeAppliers ?? /* @__PURE__ */ new Map();
666
+ this.lockManager = new LockManager(options.lockPath);
695
667
  }
696
668
  /**
697
669
  * Install an integration from the registry.
@@ -721,7 +693,7 @@ var IntegrationManager = class {
721
693
  });
722
694
  try {
723
695
  const resolved = await this.resolver.resolve(name, version);
724
- this.log.info(`Resolved ${name}@${resolved.version} from ${resolved.repo}`);
696
+ this.log.info(`Resolved ${name}@${resolved.version} from ${resolved.repository}`);
725
697
  const installPath = await this.installer.install(resolved);
726
698
  this.log.info(`Cloned to ${installPath}`);
727
699
  const manifestPath = join(installPath, "alfe-integration.yaml");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alfe.ai/integrations",
3
- "version": "0.0.2",
3
+ "version": "0.0.4",
4
4
  "description": "Integration lifecycle management for Alfe — registry, resolution, installation, and state",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -13,7 +13,7 @@
13
13
  },
14
14
  "dependencies": {
15
15
  "@auriclabs/logger": "^0.1.1",
16
- "@alfe.ai/integration-manifest": "^0.0.2"
16
+ "@alfe.ai/integration-manifest": "^0.0.3"
17
17
  },
18
18
  "files": [
19
19
  "dist"