@alfe.ai/integrations 0.0.26 → 0.0.28
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 +47 -3
- package/dist/index.js +84 -25
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -193,6 +193,11 @@ declare class Installer {
|
|
|
193
193
|
* Node module resolution from hook scripts (e.g.
|
|
194
194
|
* ~/.alfe/integrations/console/hooks/post_install.js) walks up to
|
|
195
195
|
* ~/.alfe/integrations/node_modules/ and finds these packages.
|
|
196
|
+
*
|
|
197
|
+
* Uses `"latest"` versions — npm install is always run when node_modules
|
|
198
|
+
* exists to pick up newer published versions (e.g. new API client methods).
|
|
199
|
+
* The in-memory `sharedPackagesReady` flag prevents redundant installs
|
|
200
|
+
* within a single daemon process lifetime.
|
|
196
201
|
*/
|
|
197
202
|
private ensureSharedPackages;
|
|
198
203
|
/**
|
|
@@ -240,6 +245,8 @@ interface ConfigSchemaField {
|
|
|
240
245
|
select_options?: SelectOption[];
|
|
241
246
|
/** OAuth provider identifier for oauth_connect fields */
|
|
242
247
|
oauth_provider?: string;
|
|
248
|
+
/** If true, this field is not shown in the dashboard UI (install wizard or configure modal) */
|
|
249
|
+
hidden?: boolean;
|
|
243
250
|
/** Only show this field if another field has a truthy value (string) or matches a specific value (object) */
|
|
244
251
|
depends_on_field?: string | {
|
|
245
252
|
key: string;
|
|
@@ -374,6 +381,8 @@ interface IntegrationStateEntry {
|
|
|
374
381
|
installedAt: string;
|
|
375
382
|
config: Record<string, unknown>;
|
|
376
383
|
error?: string;
|
|
384
|
+
/** Number of consecutive auto-reinstall attempts. Reset on success or explicit reinstall. */
|
|
385
|
+
reinstallAttempts?: number;
|
|
377
386
|
}
|
|
378
387
|
interface IntegrationsStateFile {
|
|
379
388
|
version: number;
|
|
@@ -389,7 +398,9 @@ interface RuntimeApplier {
|
|
|
389
398
|
/** The runtime identifier (e.g. 'openclaw', 'nanoclaw') */
|
|
390
399
|
readonly runtime: string;
|
|
391
400
|
/** Install a plugin package into this runtime */
|
|
392
|
-
applyPlugin(pkg: string, integrationInstallPath: string
|
|
401
|
+
applyPlugin(pkg: string, integrationInstallPath: string, opts?: {
|
|
402
|
+
force?: boolean;
|
|
403
|
+
}): Promise<void>;
|
|
393
404
|
/** Remove a plugin package from this runtime */
|
|
394
405
|
removePlugin(pkg: string): Promise<void>;
|
|
395
406
|
/** Copy a skill directory into this runtime's skills location */
|
|
@@ -517,7 +528,9 @@ declare class IntegrationManager {
|
|
|
517
528
|
* 4. Run health_check hook if present
|
|
518
529
|
* 5. Mark as active in state
|
|
519
530
|
*/
|
|
520
|
-
activate(integrationId: string
|
|
531
|
+
activate(integrationId: string, opts?: {
|
|
532
|
+
forcePlugins?: boolean;
|
|
533
|
+
}): Promise<ManagerResponse>;
|
|
521
534
|
/**
|
|
522
535
|
* Deactivate a running integration.
|
|
523
536
|
*
|
|
@@ -560,6 +573,18 @@ declare class IntegrationManager {
|
|
|
560
573
|
* Returns false if the state says installed but files are missing (corrupted install).
|
|
561
574
|
*/
|
|
562
575
|
isInstallIntact(integrationId: string): boolean;
|
|
576
|
+
/**
|
|
577
|
+
* Get the number of consecutive auto-reinstall attempts for an integration.
|
|
578
|
+
*/
|
|
579
|
+
getReinstallAttempts(integrationId: string): number;
|
|
580
|
+
/**
|
|
581
|
+
* Increment the auto-reinstall attempt counter.
|
|
582
|
+
*/
|
|
583
|
+
incrementReinstallAttempts(integrationId: string): void;
|
|
584
|
+
/**
|
|
585
|
+
* Reset the auto-reinstall attempt counter.
|
|
586
|
+
*/
|
|
587
|
+
resetReinstallAttempts(integrationId: string): void;
|
|
563
588
|
/**
|
|
564
589
|
* Get a specific integration by name.
|
|
565
590
|
*/
|
|
@@ -630,6 +655,14 @@ declare class StateManager {
|
|
|
630
655
|
* Check if an integration is in a given status.
|
|
631
656
|
*/
|
|
632
657
|
hasStatus(name: string, ...statuses: IntegrationStatus[]): boolean;
|
|
658
|
+
/**
|
|
659
|
+
* Increment the auto-reinstall attempt counter.
|
|
660
|
+
*/
|
|
661
|
+
incrementReinstallAttempts(name: string): void;
|
|
662
|
+
/**
|
|
663
|
+
* Reset the auto-reinstall attempt counter (on success or explicit reinstall).
|
|
664
|
+
*/
|
|
665
|
+
resetReinstallAttempts(name: string): void;
|
|
633
666
|
}
|
|
634
667
|
//#endregion
|
|
635
668
|
//#region src/hooks.d.ts
|
|
@@ -728,7 +761,9 @@ declare class OpenClawApplier implements RuntimeApplier {
|
|
|
728
761
|
private skillsDir;
|
|
729
762
|
private trackingPath;
|
|
730
763
|
constructor(options: OpenClawApplierOptions);
|
|
731
|
-
applyPlugin(pkg: string
|
|
764
|
+
applyPlugin(pkg: string, _installPath?: string, opts?: {
|
|
765
|
+
force?: boolean;
|
|
766
|
+
}): Promise<void>;
|
|
732
767
|
/**
|
|
733
768
|
* Ensure the plugin is in plugins.allow in openclaw.json.
|
|
734
769
|
* Uses `openclaw config set` to avoid clobbering OpenClaw's own file format.
|
|
@@ -856,6 +891,12 @@ interface IIntegrationManager {
|
|
|
856
891
|
}>;
|
|
857
892
|
/** Check if an integration's install directory and manifest are intact on disk */
|
|
858
893
|
isInstallIntact(integrationId: string): Promise<boolean>;
|
|
894
|
+
/** Get the number of consecutive auto-reinstall attempts */
|
|
895
|
+
getReinstallAttempts(integrationId: string): number;
|
|
896
|
+
/** Increment the auto-reinstall attempt counter */
|
|
897
|
+
incrementReinstallAttempts(integrationId: string): void;
|
|
898
|
+
/** Reset the auto-reinstall attempt counter */
|
|
899
|
+
resetReinstallAttempts(integrationId: string): void;
|
|
859
900
|
}
|
|
860
901
|
declare class IntegrationManagerAdapter implements IIntegrationManager {
|
|
861
902
|
private readonly manager;
|
|
@@ -876,6 +917,9 @@ declare class IntegrationManagerAdapter implements IIntegrationManager {
|
|
|
876
917
|
configApplied: boolean;
|
|
877
918
|
}>;
|
|
878
919
|
isInstallIntact(integrationId: string): Promise<boolean>;
|
|
920
|
+
getReinstallAttempts(integrationId: string): number;
|
|
921
|
+
incrementReinstallAttempts(integrationId: string): void;
|
|
922
|
+
resetReinstallAttempts(integrationId: string): void;
|
|
879
923
|
}
|
|
880
924
|
//#endregion
|
|
881
925
|
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
|
@@ -276,24 +276,15 @@ var Installer = class {
|
|
|
276
276
|
* Node module resolution from hook scripts (e.g.
|
|
277
277
|
* ~/.alfe/integrations/console/hooks/post_install.js) walks up to
|
|
278
278
|
* ~/.alfe/integrations/node_modules/ and finds these packages.
|
|
279
|
+
*
|
|
280
|
+
* Uses `"latest"` versions — npm install is always run when node_modules
|
|
281
|
+
* exists to pick up newer published versions (e.g. new API client methods).
|
|
282
|
+
* The in-memory `sharedPackagesReady` flag prevents redundant installs
|
|
283
|
+
* within a single daemon process lifetime.
|
|
279
284
|
*/
|
|
280
285
|
async ensureSharedPackages() {
|
|
281
286
|
if (this.sharedPackagesReady) return;
|
|
282
287
|
const pkgJsonPath = join(this.basePath, "package.json");
|
|
283
|
-
const nodeModulesExists = existsSync(join(this.basePath, "node_modules"));
|
|
284
|
-
let needsInstall = false;
|
|
285
|
-
if (existsSync(pkgJsonPath)) try {
|
|
286
|
-
const deps = JSON.parse(readFileSync(pkgJsonPath, "utf-8")).dependencies ?? {};
|
|
287
|
-
needsInstall = Object.keys(SHARED_PACKAGES).some((pkg) => !(pkg in deps));
|
|
288
|
-
} catch {
|
|
289
|
-
needsInstall = true;
|
|
290
|
-
}
|
|
291
|
-
else needsInstall = true;
|
|
292
|
-
if (!needsInstall && !nodeModulesExists) needsInstall = true;
|
|
293
|
-
if (!needsInstall) {
|
|
294
|
-
this.sharedPackagesReady = true;
|
|
295
|
-
return;
|
|
296
|
-
}
|
|
297
288
|
const pkgJson = {
|
|
298
289
|
name: "alfe-integrations-root",
|
|
299
290
|
private: true,
|
|
@@ -479,6 +470,24 @@ var StateManager = class {
|
|
|
479
470
|
if (!entry) return false;
|
|
480
471
|
return statuses.includes(entry.status);
|
|
481
472
|
}
|
|
473
|
+
/**
|
|
474
|
+
* Increment the auto-reinstall attempt counter.
|
|
475
|
+
*/
|
|
476
|
+
incrementReinstallAttempts(name) {
|
|
477
|
+
const state = this.read();
|
|
478
|
+
if (!(name in state.integrations)) return;
|
|
479
|
+
state.integrations[name].reinstallAttempts = (state.integrations[name].reinstallAttempts ?? 0) + 1;
|
|
480
|
+
this.write(state);
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
483
|
+
* Reset the auto-reinstall attempt counter (on success or explicit reinstall).
|
|
484
|
+
*/
|
|
485
|
+
resetReinstallAttempts(name) {
|
|
486
|
+
const state = this.read();
|
|
487
|
+
if (!(name in state.integrations)) return;
|
|
488
|
+
state.integrations[name].reinstallAttempts = 0;
|
|
489
|
+
this.write(state);
|
|
490
|
+
}
|
|
482
491
|
};
|
|
483
492
|
//#endregion
|
|
484
493
|
//#region src/lock.ts
|
|
@@ -1016,7 +1025,7 @@ var IntegrationManager = class {
|
|
|
1016
1025
|
* 4. Run health_check hook if present
|
|
1017
1026
|
* 5. Mark as active in state
|
|
1018
1027
|
*/
|
|
1019
|
-
async activate(integrationId) {
|
|
1028
|
+
async activate(integrationId, opts) {
|
|
1020
1029
|
const entry = this.state.get(integrationId);
|
|
1021
1030
|
if (!entry) return this.err("NOT_FOUND", `Integration "${integrationId}" is not installed`);
|
|
1022
1031
|
if (entry.status === "active") return {
|
|
@@ -1050,10 +1059,19 @@ var IntegrationManager = class {
|
|
|
1050
1059
|
continue;
|
|
1051
1060
|
}
|
|
1052
1061
|
const { plugins, skills, config: runtimeConfig } = resolveInstallsForRuntime(manifest, runtimeName);
|
|
1062
|
+
const pluginFailures = [];
|
|
1053
1063
|
for (const plugin of plugins) {
|
|
1054
1064
|
this.log.info(`Applying plugin ${plugin.package} to ${runtimeName}`);
|
|
1055
|
-
|
|
1065
|
+
try {
|
|
1066
|
+
await applier.applyPlugin(plugin.package, installPath, { force: opts?.forcePlugins });
|
|
1067
|
+
} catch (err) {
|
|
1068
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1069
|
+
this.log.error(`Failed to apply plugin ${plugin.package}: ${msg}`);
|
|
1070
|
+
pluginFailures.push(plugin.package);
|
|
1071
|
+
}
|
|
1056
1072
|
}
|
|
1073
|
+
if (pluginFailures.length > 0 && pluginFailures.length === plugins.length) throw new Error(`All plugins failed to install: ${pluginFailures.join(", ")}`);
|
|
1074
|
+
if (pluginFailures.length > 0) this.log.warn(`Continuing activation with ${String(pluginFailures.length)} failed plugin(s): ${pluginFailures.join(", ")}`);
|
|
1057
1075
|
for (const skill of skills) if (skill.clawhub) {
|
|
1058
1076
|
this.log.info(`Installing ClawHub skill ${skill.clawhub} to ${runtimeName}`);
|
|
1059
1077
|
await applier.applyClawHubSkill(skill.clawhub);
|
|
@@ -1083,7 +1101,8 @@ var IntegrationManager = class {
|
|
|
1083
1101
|
configApplied = true;
|
|
1084
1102
|
}
|
|
1085
1103
|
}
|
|
1086
|
-
|
|
1104
|
+
const appliedPlugins = plugins.filter((p) => !pluginFailures.includes(p.package));
|
|
1105
|
+
if (appliedPlugins.length > 0 || skills.length > 0) this.lockManager.addEntries(runtimeName, integrationId, manifest.version, appliedPlugins, skills, installPath);
|
|
1087
1106
|
}
|
|
1088
1107
|
this.state.setStatus(integrationId, "active");
|
|
1089
1108
|
if (manifest.hooks.post_activate) {
|
|
@@ -1122,6 +1141,7 @@ var IntegrationManager = class {
|
|
|
1122
1141
|
} catch (err) {
|
|
1123
1142
|
const message = err instanceof Error ? err.message : String(err);
|
|
1124
1143
|
this.log.error(`Failed to activate "${integrationId}": ${message}`);
|
|
1144
|
+
this.state.setStatus(integrationId, "error", message);
|
|
1125
1145
|
return this.err("ACTIVATE_FAILED", message);
|
|
1126
1146
|
}
|
|
1127
1147
|
}
|
|
@@ -1290,7 +1310,7 @@ var IntegrationManager = class {
|
|
|
1290
1310
|
if (!uninstallResult.ok) this.log.warn(`Uninstall returned error during reinstall: ${String(uninstallResult.error?.message)} — proceeding with install`);
|
|
1291
1311
|
const installResult = await this.install(params);
|
|
1292
1312
|
if (!installResult.ok) return installResult;
|
|
1293
|
-
return await this.activate(name);
|
|
1313
|
+
return await this.activate(name, { forcePlugins: true });
|
|
1294
1314
|
} catch (err) {
|
|
1295
1315
|
const message = err instanceof Error ? err.message : String(err);
|
|
1296
1316
|
this.log.error(`Failed to reinstall "${name}": ${message}`);
|
|
@@ -1344,6 +1364,24 @@ var IntegrationManager = class {
|
|
|
1344
1364
|
return existsSync(join(this.installer.getInstallPath(integrationId), "alfe-integration.yaml"));
|
|
1345
1365
|
}
|
|
1346
1366
|
/**
|
|
1367
|
+
* Get the number of consecutive auto-reinstall attempts for an integration.
|
|
1368
|
+
*/
|
|
1369
|
+
getReinstallAttempts(integrationId) {
|
|
1370
|
+
return this.state.get(integrationId)?.reinstallAttempts ?? 0;
|
|
1371
|
+
}
|
|
1372
|
+
/**
|
|
1373
|
+
* Increment the auto-reinstall attempt counter.
|
|
1374
|
+
*/
|
|
1375
|
+
incrementReinstallAttempts(integrationId) {
|
|
1376
|
+
this.state.incrementReinstallAttempts(integrationId);
|
|
1377
|
+
}
|
|
1378
|
+
/**
|
|
1379
|
+
* Reset the auto-reinstall attempt counter.
|
|
1380
|
+
*/
|
|
1381
|
+
resetReinstallAttempts(integrationId) {
|
|
1382
|
+
this.state.resetReinstallAttempts(integrationId);
|
|
1383
|
+
}
|
|
1384
|
+
/**
|
|
1347
1385
|
* Get a specific integration by name.
|
|
1348
1386
|
*/
|
|
1349
1387
|
get(name) {
|
|
@@ -1494,8 +1532,19 @@ var OpenClawApplier = class {
|
|
|
1494
1532
|
this.skillsDir = options.skillsDir ?? DEFAULT_SKILLS_DIR;
|
|
1495
1533
|
this.trackingPath = options.configPath ?? join(this.workspace, "config.json");
|
|
1496
1534
|
}
|
|
1497
|
-
async applyPlugin(pkg) {
|
|
1535
|
+
async applyPlugin(pkg, _installPath, opts) {
|
|
1498
1536
|
await this.ensurePluginsAllow(pkg);
|
|
1537
|
+
if (opts?.force && this.isPluginInstalled(pkg)) {
|
|
1538
|
+
log.info({ pkg }, "Force mode — uninstalling plugin before reinstall");
|
|
1539
|
+
try {
|
|
1540
|
+
await this.removePlugin(pkg);
|
|
1541
|
+
} catch (err) {
|
|
1542
|
+
log.warn({
|
|
1543
|
+
pkg,
|
|
1544
|
+
err: err instanceof Error ? err.message : String(err)
|
|
1545
|
+
}, "Failed to uninstall plugin during force reinstall — proceeding");
|
|
1546
|
+
}
|
|
1547
|
+
}
|
|
1499
1548
|
if (!this.isPluginInstalled(pkg)) {
|
|
1500
1549
|
try {
|
|
1501
1550
|
const args = [
|
|
@@ -1555,8 +1604,7 @@ var OpenClawApplier = class {
|
|
|
1555
1604
|
"config",
|
|
1556
1605
|
"set",
|
|
1557
1606
|
"plugins.allow",
|
|
1558
|
-
JSON.stringify(updated)
|
|
1559
|
-
"--strict-json"
|
|
1607
|
+
JSON.stringify(updated)
|
|
1560
1608
|
], { timeout: 1e4 });
|
|
1561
1609
|
} catch (err) {
|
|
1562
1610
|
log.warn({
|
|
@@ -1600,6 +1648,10 @@ var OpenClawApplier = class {
|
|
|
1600
1648
|
log.info({ slug }, "ClawHub skill installed");
|
|
1601
1649
|
} catch (err) {
|
|
1602
1650
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1651
|
+
if (msg.includes("already exists") || msg.includes("Skill already exists")) {
|
|
1652
|
+
log.info({ slug }, "ClawHub skill already installed (skipped)");
|
|
1653
|
+
return;
|
|
1654
|
+
}
|
|
1603
1655
|
log.error({
|
|
1604
1656
|
slug,
|
|
1605
1657
|
err: msg
|
|
@@ -1646,8 +1698,7 @@ var OpenClawApplier = class {
|
|
|
1646
1698
|
"config",
|
|
1647
1699
|
"set",
|
|
1648
1700
|
"--batch-json",
|
|
1649
|
-
JSON.stringify(batch)
|
|
1650
|
-
"--strict-json"
|
|
1701
|
+
JSON.stringify(batch)
|
|
1651
1702
|
], { timeout: 1e4 });
|
|
1652
1703
|
} catch (err) {
|
|
1653
1704
|
log.error({
|
|
@@ -1719,8 +1770,7 @@ var OpenClawApplier = class {
|
|
|
1719
1770
|
"config",
|
|
1720
1771
|
"set",
|
|
1721
1772
|
"--batch-json",
|
|
1722
|
-
JSON.stringify(batch)
|
|
1723
|
-
"--strict-json"
|
|
1773
|
+
JSON.stringify(batch)
|
|
1724
1774
|
], { timeout: 1e4 });
|
|
1725
1775
|
} catch (err) {
|
|
1726
1776
|
log.error({
|
|
@@ -1825,6 +1875,15 @@ var IntegrationManagerAdapter = class {
|
|
|
1825
1875
|
isInstallIntact(integrationId) {
|
|
1826
1876
|
return Promise.resolve(this.manager.isInstallIntact(integrationId));
|
|
1827
1877
|
}
|
|
1878
|
+
getReinstallAttempts(integrationId) {
|
|
1879
|
+
return this.manager.getReinstallAttempts(integrationId);
|
|
1880
|
+
}
|
|
1881
|
+
incrementReinstallAttempts(integrationId) {
|
|
1882
|
+
this.manager.incrementReinstallAttempts(integrationId);
|
|
1883
|
+
}
|
|
1884
|
+
resetReinstallAttempts(integrationId) {
|
|
1885
|
+
this.manager.resetReinstallAttempts(integrationId);
|
|
1886
|
+
}
|
|
1828
1887
|
};
|
|
1829
1888
|
//#endregion
|
|
1830
1889
|
export { Installer, InstallerError, IntegrationManager, IntegrationManagerAdapter, LockManager, OpenClawApplier, Registry, RegistryResolveError, Resolver, StateManager, buildHookEnv, resolveInstallsForRuntime, runHook, runHookWithContext };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@alfe.ai/integrations",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.28",
|
|
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.
|
|
16
|
+
"@alfe.ai/integration-manifest": "^0.0.10"
|
|
17
17
|
},
|
|
18
18
|
"files": [
|
|
19
19
|
"dist"
|