loopengine 0.1.41 → 0.1.42
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.
|
@@ -63,6 +63,14 @@ export interface InstalledAbilityRecord {
|
|
|
63
63
|
skills: string[];
|
|
64
64
|
actauthRules: string[];
|
|
65
65
|
env: AbilityEnvDecl[];
|
|
66
|
+
/** Real npm package names (dependencies + optionalDependencies) the
|
|
67
|
+
* ability's own tool files import, as of the last install/upgrade —
|
|
68
|
+
* same values AbilityManifest.dependencies carries, persisted here so
|
|
69
|
+
* the Admin UI's "Install dependencies" button can read them back
|
|
70
|
+
* without refetching the ability from its spec. Optional only because
|
|
71
|
+
* an ability installed before this field existed has no recorded
|
|
72
|
+
* value. */
|
|
73
|
+
dependencies?: string[];
|
|
66
74
|
/** relative path (e.g. "tools/foo.ts") -> sha256 hex, as of the last
|
|
67
75
|
* install/upgrade — remove-ability's dirty-check compares against
|
|
68
76
|
* this rather than storing/refetching full content to diff. */
|
|
@@ -454,6 +454,7 @@ export async function installAbility(agentName, spec, options = {}) {
|
|
|
454
454
|
skills: skillIds,
|
|
455
455
|
actauthRules: ruleNames,
|
|
456
456
|
env: manifest.env ?? [],
|
|
457
|
+
dependencies: manifest.dependencies ?? [],
|
|
457
458
|
contentHashes,
|
|
458
459
|
};
|
|
459
460
|
writeProvenance(agentName, provenance);
|
|
@@ -633,7 +634,14 @@ export async function upgradeAbility(agentName, abilityName, options = {}) {
|
|
|
633
634
|
updateActauthRule(agentName, ruleName, { scope: newRule.scope, tool: newRule.tool, decision: newRule.decision });
|
|
634
635
|
results.push({ path, status: 'updated' });
|
|
635
636
|
}
|
|
636
|
-
provenance[abilityName] = {
|
|
637
|
+
provenance[abilityName] = {
|
|
638
|
+
...record,
|
|
639
|
+
version: newManifest.version,
|
|
640
|
+
spec,
|
|
641
|
+
contentHashes: newContentHashes,
|
|
642
|
+
env: newManifest.env ?? record.env,
|
|
643
|
+
dependencies: newManifest.dependencies ?? record.dependencies,
|
|
644
|
+
};
|
|
637
645
|
writeProvenance(agentName, provenance);
|
|
638
646
|
return { files: results };
|
|
639
647
|
}
|
|
@@ -1297,6 +1297,14 @@ export const agentsConfigPageHtml = `<!doctype html>
|
|
|
1297
1297
|
var actionHtml = a.upToDate
|
|
1298
1298
|
? '<span class="hint">up to date</span>'
|
|
1299
1299
|
: '<button type="button" class="ability-upgrade-btn" data-name="' + escapeHtml(a.name) + '">Upgrade</button>';
|
|
1300
|
+
// Same reasoning as upToDate above — missingDependencies (see
|
|
1301
|
+
// hasNodeModule) only ever hides this once the server actually
|
|
1302
|
+
// found every dependency already present in node_modules; anything
|
|
1303
|
+
// it can't confirm still shows the button.
|
|
1304
|
+
var deps = a.missingDependencies || [];
|
|
1305
|
+
if (deps.length) {
|
|
1306
|
+
actionHtml += ' <button type="button" class="ability-install-deps-btn" data-name="' + escapeHtml(a.name) + '" title="npm install ' + escapeHtml(deps.join(' ')) + '">Install deps</button>';
|
|
1307
|
+
}
|
|
1300
1308
|
return '<tr>' +
|
|
1301
1309
|
'<td><code>' + escapeHtml(a.name) + '</code></td>' +
|
|
1302
1310
|
'<td>' + escapeHtml(a.version) + '</td>' +
|
|
@@ -1372,8 +1380,10 @@ export const agentsConfigPageHtml = `<!doctype html>
|
|
|
1372
1380
|
// has to be installed separately, or the tool fails at runtime
|
|
1373
1381
|
// with an opaque "Cannot find package" the very next time this
|
|
1374
1382
|
// agent's config is loaded. Surfaced here, loudly, instead of
|
|
1375
|
-
// leaving that as a silent trap discovered later
|
|
1376
|
-
|
|
1383
|
+
// leaving that as a silent trap discovered later — the
|
|
1384
|
+
// Installed table below now has its own "Install deps" button
|
|
1385
|
+
// for exactly this (see handleAbilityInstallDepsPost).
|
|
1386
|
+
message += ' <strong>This ability also needs: <code>' + escapeHtml(deps.join(', ')) + '</code></strong> — use the "Install deps" button below, or run <code>npm install ' + escapeHtml(deps.join(' ')) + '</code> yourself.';
|
|
1377
1387
|
}
|
|
1378
1388
|
statusEl.innerHTML = message;
|
|
1379
1389
|
loadAbilitiesTab(name);
|
|
@@ -1427,6 +1437,39 @@ export const agentsConfigPageHtml = `<!doctype html>
|
|
|
1427
1437
|
});
|
|
1428
1438
|
}
|
|
1429
1439
|
|
|
1440
|
+
// Runs npm install for an ability's own missing dependencies
|
|
1441
|
+
// (handleAbilityInstallDepsPost) — the server reads the dependency
|
|
1442
|
+
// list back off this ability's own installed record, not anything
|
|
1443
|
+
// this form could tamper with, so there's nothing to pass except
|
|
1444
|
+
// which ability. Can take a while (a native module like sharp
|
|
1445
|
+
// especially), so the button stays disabled and the status line says
|
|
1446
|
+
// so rather than looking hung.
|
|
1447
|
+
function installAbilityDeps(name, abilityName, statusEl, btn) {
|
|
1448
|
+
if (btn) { btn.disabled = true; }
|
|
1449
|
+
statusEl.className = 'hint';
|
|
1450
|
+
statusEl.textContent = 'Running npm install for ' + abilityName + '’s dependencies… this can take a minute.';
|
|
1451
|
+
fetch('/agents/' + encodeURIComponent(name) + '/abilities/' + encodeURIComponent(abilityName) + '/install-deps', {
|
|
1452
|
+
method: 'POST',
|
|
1453
|
+
headers: { 'content-type': 'application/json' },
|
|
1454
|
+
body: JSON.stringify({}),
|
|
1455
|
+
})
|
|
1456
|
+
.then(function (r) { return r.json().then(function (j) { return { ok: r.ok, body: j }; }); })
|
|
1457
|
+
.then(function (result) {
|
|
1458
|
+
if (!result.ok) throw new Error(result.body.error || 'request failed');
|
|
1459
|
+
var installed = result.body.installed || [];
|
|
1460
|
+
statusEl.className = 'hint';
|
|
1461
|
+
statusEl.textContent = installed.length
|
|
1462
|
+
? 'Installed: ' + installed.join(', ') + '. Restart the server to pick it up.'
|
|
1463
|
+
: abilityName + ' had no dependencies to install.';
|
|
1464
|
+
loadAbilitiesTab(name);
|
|
1465
|
+
})
|
|
1466
|
+
.catch(function (err) {
|
|
1467
|
+
statusEl.className = 'error';
|
|
1468
|
+
statusEl.textContent = 'Could not install dependencies: ' + err.message;
|
|
1469
|
+
if (btn) { btn.disabled = false; }
|
|
1470
|
+
});
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1430
1473
|
// Shared by the initial (empty-query) load right after the tab opens
|
|
1431
1474
|
// and the search form's own submit — the empty-query case is just
|
|
1432
1475
|
// "list every published ability" (the keyword filter alone, no extra
|
|
@@ -1468,6 +1511,14 @@ export const agentsConfigPageHtml = `<!doctype html>
|
|
|
1468
1511
|
});
|
|
1469
1512
|
}
|
|
1470
1513
|
|
|
1514
|
+
var installDepsBtns = content.querySelectorAll('.ability-install-deps-btn');
|
|
1515
|
+
for (var d = 0; d < installDepsBtns.length; d++) {
|
|
1516
|
+
installDepsBtns[d].addEventListener('click', function (ev) {
|
|
1517
|
+
var btn = ev.currentTarget;
|
|
1518
|
+
installAbilityDeps(name, btn.dataset.name, upgradeStatusEl, btn);
|
|
1519
|
+
});
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1471
1522
|
var searchForm = content.querySelector('#abilitySearchForm');
|
|
1472
1523
|
var resultsEl = content.querySelector('#abilitySearchResults');
|
|
1473
1524
|
if (searchForm && resultsEl) {
|