@alfe.ai/integrations 0.0.8 → 0.0.9
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 +10 -18
- package/dist/index.js +106 -84
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -604,20 +604,19 @@ interface OpenClawApplierOptions {
|
|
|
604
604
|
workspace: string;
|
|
605
605
|
/** Override skills directory (defaults to ~/.alfe/skills/) */
|
|
606
606
|
skillsDir?: string;
|
|
607
|
-
/** Path to the
|
|
607
|
+
/** Path to the integration tracking file (defaults to {workspace}/config.json) */
|
|
608
608
|
configPath?: string;
|
|
609
609
|
}
|
|
610
610
|
declare class OpenClawApplier implements RuntimeApplier {
|
|
611
611
|
readonly runtime = "openclaw";
|
|
612
612
|
private workspace;
|
|
613
613
|
private skillsDir;
|
|
614
|
-
private
|
|
614
|
+
private trackingPath;
|
|
615
615
|
constructor(options: OpenClawApplierOptions);
|
|
616
616
|
applyPlugin(pkg: string): Promise<void>;
|
|
617
617
|
/**
|
|
618
618
|
* Ensure the plugin is in plugins.allow in openclaw.json.
|
|
619
|
-
*
|
|
620
|
-
* the allowlist may not include the plugin — add it if missing.
|
|
619
|
+
* Uses `openclaw config set` to avoid clobbering OpenClaw's own file format.
|
|
621
620
|
*/
|
|
622
621
|
private ensurePluginsAllow;
|
|
623
622
|
/**
|
|
@@ -629,29 +628,22 @@ declare class OpenClawApplier implements RuntimeApplier {
|
|
|
629
628
|
applySkill(name: string, srcPath: string): Promise<void>;
|
|
630
629
|
removeSkill(name: string): Promise<void>;
|
|
631
630
|
/**
|
|
632
|
-
*
|
|
631
|
+
* Apply integration config to the OpenClaw runtime via `openclaw config set`.
|
|
633
632
|
*
|
|
634
|
-
* Each integration's config contribution is tracked in
|
|
635
|
-
*
|
|
636
|
-
* cleanly removed later.
|
|
633
|
+
* Each integration's config contribution is tracked in the tracking file
|
|
634
|
+
* (config.json) so it can be cleanly removed later.
|
|
637
635
|
*/
|
|
638
636
|
applyConfig(integrationId: string, config: Record<string, unknown>): Promise<void>;
|
|
639
637
|
/**
|
|
640
638
|
* Remove config previously applied by an integration.
|
|
641
639
|
*
|
|
642
|
-
*
|
|
643
|
-
*
|
|
640
|
+
* Reads the tracking file to find which config keys this integration set,
|
|
641
|
+
* then removes them via `openclaw config unset`.
|
|
644
642
|
*/
|
|
645
643
|
removeConfig(integrationId: string): Promise<void>;
|
|
646
644
|
isAvailable(): Promise<boolean>;
|
|
647
|
-
private
|
|
648
|
-
private
|
|
649
|
-
/**
|
|
650
|
-
* Extract the base config by stripping all keys that were contributed
|
|
651
|
-
* by integrations. This is done by removing each integration's keys
|
|
652
|
-
* from the current config.
|
|
653
|
-
*/
|
|
654
|
-
private getBaseConfig;
|
|
645
|
+
private readTracking;
|
|
646
|
+
private writeTracking;
|
|
655
647
|
}
|
|
656
648
|
//#endregion
|
|
657
649
|
//#region src/lock.d.ts
|
package/dist/index.js
CHANGED
|
@@ -1178,67 +1178,82 @@ var IntegrationManager = class {
|
|
|
1178
1178
|
/**
|
|
1179
1179
|
* OpenClawApplier — applies plugins, skills, and config to the OpenClaw runtime.
|
|
1180
1180
|
*
|
|
1181
|
-
* Plugins are installed via `
|
|
1181
|
+
* Plugins are installed via `openclaw plugins install`.
|
|
1182
1182
|
* Skills are copied to ~/.alfe/skills/{name}.
|
|
1183
|
-
* Config is
|
|
1184
|
-
*
|
|
1183
|
+
* Config is applied via `openclaw config set` so OpenClaw manages its own
|
|
1184
|
+
* config file (openclaw.json) without clobbering. Per-integration tracking
|
|
1185
|
+
* is stored in a separate tracking file (config.json) for clean removal.
|
|
1185
1186
|
*/
|
|
1186
1187
|
const execFileAsync = promisify(execFile);
|
|
1188
|
+
const log = createLogger("OpenClawApplier");
|
|
1187
1189
|
const DEFAULT_SKILLS_DIR = join(homedir(), ".alfe", "skills");
|
|
1188
1190
|
/**
|
|
1189
|
-
*
|
|
1190
|
-
*
|
|
1191
|
+
* Flatten a nested config object into dot-path key-value pairs.
|
|
1192
|
+
* e.g. { gateway: { bind: "lan" } } → [["gateway.bind", "lan"]]
|
|
1191
1193
|
*/
|
|
1192
|
-
function
|
|
1193
|
-
const
|
|
1194
|
-
for (const key of Object.
|
|
1195
|
-
const
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
return result;
|
|
1194
|
+
function flattenConfig(obj, prefix = "") {
|
|
1195
|
+
const pairs = [];
|
|
1196
|
+
for (const [key, val] of Object.entries(obj)) {
|
|
1197
|
+
const path = prefix ? `${prefix}.${key}` : key;
|
|
1198
|
+
if (val !== null && typeof val === "object" && !Array.isArray(val)) pairs.push(...flattenConfig(val, path));
|
|
1199
|
+
else pairs.push([path, val]);
|
|
1200
|
+
}
|
|
1201
|
+
return pairs;
|
|
1201
1202
|
}
|
|
1202
1203
|
var OpenClawApplier = class {
|
|
1203
1204
|
runtime = "openclaw";
|
|
1204
1205
|
workspace;
|
|
1205
1206
|
skillsDir;
|
|
1206
|
-
|
|
1207
|
+
trackingPath;
|
|
1207
1208
|
constructor(options) {
|
|
1208
1209
|
this.workspace = options.workspace;
|
|
1209
1210
|
this.skillsDir = options.skillsDir ?? DEFAULT_SKILLS_DIR;
|
|
1210
|
-
this.
|
|
1211
|
+
this.trackingPath = options.configPath ?? join(this.workspace, "config.json");
|
|
1211
1212
|
}
|
|
1212
1213
|
async applyPlugin(pkg) {
|
|
1213
|
-
if (this.isPluginInstalled(pkg)) {
|
|
1214
|
-
|
|
1215
|
-
|
|
1214
|
+
if (!this.isPluginInstalled(pkg)) {
|
|
1215
|
+
await execFileAsync("openclaw", [
|
|
1216
|
+
"plugins",
|
|
1217
|
+
"install",
|
|
1218
|
+
pkg
|
|
1219
|
+
], { timeout: 6e4 });
|
|
1220
|
+
await new Promise((r) => {
|
|
1221
|
+
setTimeout(r, 500);
|
|
1222
|
+
});
|
|
1216
1223
|
}
|
|
1217
|
-
await
|
|
1218
|
-
"plugins",
|
|
1219
|
-
"install",
|
|
1220
|
-
pkg
|
|
1221
|
-
], { timeout: 6e4 });
|
|
1224
|
+
await this.ensurePluginsAllow(pkg);
|
|
1222
1225
|
}
|
|
1223
1226
|
/**
|
|
1224
1227
|
* Ensure the plugin is in plugins.allow in openclaw.json.
|
|
1225
|
-
*
|
|
1226
|
-
* the allowlist may not include the plugin — add it if missing.
|
|
1228
|
+
* Uses `openclaw config set` to avoid clobbering OpenClaw's own file format.
|
|
1227
1229
|
*/
|
|
1228
|
-
ensurePluginsAllow(pkg) {
|
|
1229
|
-
|
|
1230
|
-
if (!existsSync(configPath)) return;
|
|
1230
|
+
async ensurePluginsAllow(pkg) {
|
|
1231
|
+
let currentAllow = [];
|
|
1231
1232
|
try {
|
|
1232
|
-
const
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
1240
|
-
}
|
|
1233
|
+
const { stdout } = await execFileAsync("openclaw", [
|
|
1234
|
+
"config",
|
|
1235
|
+
"get",
|
|
1236
|
+
"plugins.allow"
|
|
1237
|
+
], { timeout: 1e4 });
|
|
1238
|
+
const parsed = JSON.parse(stdout.trim());
|
|
1239
|
+
if (Array.isArray(parsed)) currentAllow = parsed;
|
|
1241
1240
|
} catch {}
|
|
1241
|
+
if (currentAllow.includes(pkg)) return;
|
|
1242
|
+
const updated = [...currentAllow, pkg];
|
|
1243
|
+
try {
|
|
1244
|
+
await execFileAsync("openclaw", [
|
|
1245
|
+
"config",
|
|
1246
|
+
"set",
|
|
1247
|
+
"plugins.allow",
|
|
1248
|
+
JSON.stringify(updated),
|
|
1249
|
+
"--strict-json"
|
|
1250
|
+
], { timeout: 1e4 });
|
|
1251
|
+
} catch (err) {
|
|
1252
|
+
log.warn({
|
|
1253
|
+
err: err instanceof Error ? err.message : String(err),
|
|
1254
|
+
pkg
|
|
1255
|
+
}, "Failed to set plugins.allow via openclaw config set");
|
|
1256
|
+
}
|
|
1242
1257
|
}
|
|
1243
1258
|
/**
|
|
1244
1259
|
* Check if a plugin is already installed in ~/.openclaw/extensions/.
|
|
@@ -1273,69 +1288,76 @@ var OpenClawApplier = class {
|
|
|
1273
1288
|
return Promise.resolve();
|
|
1274
1289
|
}
|
|
1275
1290
|
/**
|
|
1276
|
-
*
|
|
1291
|
+
* Apply integration config to the OpenClaw runtime via `openclaw config set`.
|
|
1277
1292
|
*
|
|
1278
|
-
* Each integration's config contribution is tracked in
|
|
1279
|
-
*
|
|
1280
|
-
* cleanly removed later.
|
|
1293
|
+
* Each integration's config contribution is tracked in the tracking file
|
|
1294
|
+
* (config.json) so it can be cleanly removed later.
|
|
1281
1295
|
*/
|
|
1282
|
-
applyConfig(integrationId, config) {
|
|
1283
|
-
const
|
|
1284
|
-
const integrations =
|
|
1296
|
+
async applyConfig(integrationId, config) {
|
|
1297
|
+
const tracking = this.readTracking();
|
|
1298
|
+
const integrations = tracking._integrations ?? {};
|
|
1285
1299
|
integrations[integrationId] = config;
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1300
|
+
tracking._integrations = integrations;
|
|
1301
|
+
this.writeTracking(tracking);
|
|
1302
|
+
const pairs = flattenConfig(config);
|
|
1303
|
+
for (const [path, value] of pairs) try {
|
|
1304
|
+
await execFileAsync("openclaw", [
|
|
1305
|
+
"config",
|
|
1306
|
+
"set",
|
|
1307
|
+
path,
|
|
1308
|
+
JSON.stringify(value),
|
|
1309
|
+
"--strict-json"
|
|
1310
|
+
], { timeout: 1e4 });
|
|
1311
|
+
} catch (err) {
|
|
1312
|
+
log.error({
|
|
1313
|
+
err: err instanceof Error ? err.message : String(err),
|
|
1314
|
+
path,
|
|
1315
|
+
value
|
|
1316
|
+
}, "Failed to set config via openclaw config set");
|
|
1317
|
+
throw err;
|
|
1318
|
+
}
|
|
1291
1319
|
}
|
|
1292
1320
|
/**
|
|
1293
1321
|
* Remove config previously applied by an integration.
|
|
1294
1322
|
*
|
|
1295
|
-
*
|
|
1296
|
-
*
|
|
1323
|
+
* Reads the tracking file to find which config keys this integration set,
|
|
1324
|
+
* then removes them via `openclaw config unset`.
|
|
1297
1325
|
*/
|
|
1298
|
-
removeConfig(integrationId) {
|
|
1299
|
-
const
|
|
1300
|
-
const integrations =
|
|
1301
|
-
if (!(integrationId in integrations)) return
|
|
1302
|
-
const
|
|
1303
|
-
|
|
1304
|
-
for (const
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1326
|
+
async removeConfig(integrationId) {
|
|
1327
|
+
const tracking = this.readTracking();
|
|
1328
|
+
const integrations = tracking._integrations ?? {};
|
|
1329
|
+
if (!(integrationId in integrations)) return;
|
|
1330
|
+
const integrationConfig = integrations[integrationId];
|
|
1331
|
+
const pairs = flattenConfig(integrationConfig);
|
|
1332
|
+
for (const [path] of pairs) try {
|
|
1333
|
+
await execFileAsync("openclaw", [
|
|
1334
|
+
"config",
|
|
1335
|
+
"unset",
|
|
1336
|
+
path
|
|
1337
|
+
], { timeout: 1e4 });
|
|
1338
|
+
} catch (err) {
|
|
1339
|
+
log.warn({
|
|
1340
|
+
err: err instanceof Error ? err.message : String(err),
|
|
1341
|
+
path
|
|
1342
|
+
}, "Failed to unset config via openclaw config unset");
|
|
1343
|
+
}
|
|
1344
|
+
tracking._integrations = Object.fromEntries(Object.entries(integrations).filter(([key]) => key !== integrationId));
|
|
1345
|
+
this.writeTracking(tracking);
|
|
1308
1346
|
}
|
|
1309
1347
|
isAvailable() {
|
|
1310
1348
|
return Promise.resolve(existsSync(this.workspace));
|
|
1311
1349
|
}
|
|
1312
|
-
|
|
1313
|
-
if (!existsSync(this.
|
|
1350
|
+
readTracking() {
|
|
1351
|
+
if (!existsSync(this.trackingPath)) return {};
|
|
1314
1352
|
try {
|
|
1315
|
-
return JSON.parse(readFileSync(this.
|
|
1353
|
+
return JSON.parse(readFileSync(this.trackingPath, "utf-8"));
|
|
1316
1354
|
} catch {
|
|
1317
1355
|
return {};
|
|
1318
1356
|
}
|
|
1319
1357
|
}
|
|
1320
|
-
|
|
1321
|
-
mkdirSync(join(this.
|
|
1322
|
-
writeFileSync(this.
|
|
1323
|
-
}
|
|
1324
|
-
/**
|
|
1325
|
-
* Extract the base config by stripping all keys that were contributed
|
|
1326
|
-
* by integrations. This is done by removing each integration's keys
|
|
1327
|
-
* from the current config.
|
|
1328
|
-
*/
|
|
1329
|
-
getBaseConfig(current) {
|
|
1330
|
-
const integrations = current._integrations ?? {};
|
|
1331
|
-
const allIntegrationKeys = /* @__PURE__ */ new Set();
|
|
1332
|
-
for (const cfg of Object.values(integrations)) for (const key of Object.keys(cfg)) allIntegrationKeys.add(key);
|
|
1333
|
-
const base = {};
|
|
1334
|
-
for (const [key, val] of Object.entries(current)) {
|
|
1335
|
-
if (key === "_integrations") continue;
|
|
1336
|
-
if (!allIntegrationKeys.has(key)) base[key] = val;
|
|
1337
|
-
}
|
|
1338
|
-
return base;
|
|
1358
|
+
writeTracking(config) {
|
|
1359
|
+
mkdirSync(join(this.trackingPath, ".."), { recursive: true });
|
|
1360
|
+
writeFileSync(this.trackingPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
1339
1361
|
}
|
|
1340
1362
|
};
|
|
1341
1363
|
//#endregion
|
package/package.json
CHANGED