loopengine 0.1.37 → 0.1.39
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/bin/ability-manager.d.ts +5 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +1 -1
- package/dist/web/agents-config-page.js +77 -16
- package/dist/web/env-admin.d.ts +4 -0
- package/dist/web/env-admin.js +1 -0
- package/package.json +1 -1
|
@@ -12,6 +12,11 @@ export interface AbilityEnvDecl {
|
|
|
12
12
|
name: string;
|
|
13
13
|
description?: string;
|
|
14
14
|
secret?: boolean;
|
|
15
|
+
/** A closed set of valid values, e.g. `["openai", "google"]` for a
|
|
16
|
+
* provider switch — the Admin UI renders these as a dropdown instead
|
|
17
|
+
* of a free-text field. Omit for anything that isn't genuinely a fixed
|
|
18
|
+
* enum (an API key, a free-form model name, a path, ...). */
|
|
19
|
+
options?: string[];
|
|
15
20
|
}
|
|
16
21
|
/** `name`/`version` deliberately aren't declared here — they're read off
|
|
17
22
|
* the ability's own sibling `package.json` instead (see readManifest),
|
package/dist/index.d.ts
CHANGED
|
@@ -57,8 +57,8 @@ export { listDeclaredEnvVars, setEnvVar, EnvVarNameError } from './web/env-admin
|
|
|
57
57
|
export type { DeclaredEnvVar } from './web/env-admin.js';
|
|
58
58
|
export { readActauthConfig, addActauthRule, updateActauthRule, removeActauthRule, setDefaultDecision, ActauthRuleExistsError, ActauthRuleNotFoundError, } from './web/actauth-admin.js';
|
|
59
59
|
export type { ActauthRuleInput, ActauthConfigView } from './web/actauth-admin.js';
|
|
60
|
-
export { installAbility, listInstalledAbilities, AbilityManifestError, AbilityVersionError, AbilityCollisionError, AbilityAlreadyInstalledError, } from './bin/ability-manager.js';
|
|
61
|
-
export type { InstalledAbilityRecord, AbilityEnvDecl } from './bin/ability-manager.js';
|
|
60
|
+
export { installAbility, upgradeAbility, listInstalledAbilities, AbilityManifestError, AbilityVersionError, AbilityCollisionError, AbilityAlreadyInstalledError, AbilityNotInstalledError, } from './bin/ability-manager.js';
|
|
61
|
+
export type { InstalledAbilityRecord, AbilityEnvDecl, UpgradeFileResult } from './bin/ability-manager.js';
|
|
62
62
|
export { searchPublicAbilities } from './web/abilities-admin.js';
|
|
63
63
|
export type { PublicAbilitySearchResult } from './web/abilities-admin.js';
|
|
64
64
|
export { describeModelProviders, describeGateways } from './web/global-config.js';
|
package/dist/index.js
CHANGED
|
@@ -51,7 +51,7 @@ export { playgroundHtml } from './web/playground.js';
|
|
|
51
51
|
export { readSkill, writeSkill, deleteSkill, SkillInvalidIdError, SkillNotFoundError } from './web/skills-admin.js';
|
|
52
52
|
export { listDeclaredEnvVars, setEnvVar, EnvVarNameError } from './web/env-admin.js';
|
|
53
53
|
export { readActauthConfig, addActauthRule, updateActauthRule, removeActauthRule, setDefaultDecision, ActauthRuleExistsError, ActauthRuleNotFoundError, } from './web/actauth-admin.js';
|
|
54
|
-
export { installAbility, listInstalledAbilities, AbilityManifestError, AbilityVersionError, AbilityCollisionError, AbilityAlreadyInstalledError, } from './bin/ability-manager.js';
|
|
54
|
+
export { installAbility, upgradeAbility, listInstalledAbilities, AbilityManifestError, AbilityVersionError, AbilityCollisionError, AbilityAlreadyInstalledError, AbilityNotInstalledError, } from './bin/ability-manager.js';
|
|
55
55
|
export { searchPublicAbilities } from './web/abilities-admin.js';
|
|
56
56
|
export { describeModelProviders, describeGateways } from './web/global-config.js';
|
|
57
57
|
export { createTrackedApprover, listApprovals, decideApproval, findApproval } from './web/web-approver.js';
|
|
@@ -1149,31 +1149,41 @@ export const agentsConfigPageHtml = `<!doctype html>
|
|
|
1149
1149
|
: v.secret
|
|
1150
1150
|
? '<span class="hint">set</span>'
|
|
1151
1151
|
: '<code>' + escapeHtml(v.value || '') + '</code>';
|
|
1152
|
+
// A var with a closed set of valid values (AbilityEnvDecl.options,
|
|
1153
|
+
// e.g. a provider switch) gets a <select> instead of a free-text
|
|
1154
|
+
// field, so there's nothing to mistype — the same "value" form field
|
|
1155
|
+
// name either element uses means wireEnvHandlers's own submit
|
|
1156
|
+
// handler doesn't need to know which one it's looking at.
|
|
1157
|
+
var fieldHtml = v.options
|
|
1158
|
+
? '<select name="value">' + v.options.map(function (opt) {
|
|
1159
|
+
return '<option value="' + escapeHtml(opt) + '"' + (v.value === opt ? ' selected' : '') + '>' + escapeHtml(opt) + '</option>';
|
|
1160
|
+
}).join('') + '</select>'
|
|
1161
|
+
: '<input type="' + (v.secret ? 'password' : 'text') + '" name="value" placeholder="' + (v.set ? 'unchanged unless you type a new value' : 'value') + '" required>';
|
|
1152
1162
|
return '<tr>' +
|
|
1153
1163
|
'<td><code>' + escapeHtml(v.name) + '</code>' + sharedNote + '</td>' +
|
|
1154
1164
|
'<td style="max-width:320px">' + escapeHtml(v.description || '') + '</td>' +
|
|
1155
1165
|
'<td>' + statusHtml + '</td>' +
|
|
1156
1166
|
'<td><form class="add-source env-var-form" data-name="' + escapeHtml(v.name) + '">' +
|
|
1157
|
-
|
|
1167
|
+
fieldHtml +
|
|
1158
1168
|
'<button type="submit">Save</button>' +
|
|
1159
1169
|
'</form></td>' +
|
|
1160
1170
|
'</tr>';
|
|
1161
1171
|
}
|
|
1162
1172
|
|
|
1163
|
-
// One card per ability, matching the same .source/.source-head
|
|
1164
|
-
//
|
|
1165
|
-
//
|
|
1166
|
-
//
|
|
1167
|
-
//
|
|
1168
|
-
// set
|
|
1173
|
+
// One card per ability, matching the same .source/.source-head look
|
|
1174
|
+
// Gateway Tools sources already use — so an operator scanning the tab
|
|
1175
|
+
// sees, per ability, which env vars it declared, instead of hunting
|
|
1176
|
+
// through one long flat table for rows that belong to whichever
|
|
1177
|
+
// ability they're actually trying to set up right now. Deliberately no
|
|
1178
|
+
// "not set" count here (or in renderEnvConfigHtml's own summary) — a
|
|
1179
|
+
// declared var not being set doesn't mean anything is broken, since
|
|
1180
|
+
// plenty of them have working defaults (see each var's own
|
|
1181
|
+
// description) and are only ever declared so an operator *can*
|
|
1182
|
+
// override them, not because every single one is required.
|
|
1169
1183
|
function renderEnvAbilityCard(abilityName, vars) {
|
|
1170
|
-
var missing = vars.filter(function (v) { return !v.set; }).length;
|
|
1171
|
-
var statusHtml = missing === 0
|
|
1172
|
-
? '<span class="status-ok">all set</span>'
|
|
1173
|
-
: '<span class="status-error">' + missing + ' of ' + vars.length + ' not set</span>';
|
|
1174
1184
|
var rows = vars.map(renderEnvRow).join('');
|
|
1175
1185
|
return '<div class="source">' +
|
|
1176
|
-
'<div class="source-head"><h4>' + escapeHtml(abilityName) + '</h4>' +
|
|
1186
|
+
'<div class="source-head"><h4>' + escapeHtml(abilityName) + '</h4><span class="hint">' + vars.length + ' var' + (vars.length === 1 ? '' : 's') + '</span></div>' +
|
|
1177
1187
|
'<table><thead><tr><th>Name</th><th style="max-width:320px">Description</th><th>Current value</th><th>Set value</th></tr></thead><tbody>' + rows + '</tbody></table>' +
|
|
1178
1188
|
'</div>';
|
|
1179
1189
|
}
|
|
@@ -1181,9 +1191,7 @@ export const agentsConfigPageHtml = `<!doctype html>
|
|
|
1181
1191
|
function renderEnvConfigHtml(vars) {
|
|
1182
1192
|
if (!vars.length) return '<p class="hint">No installed ability has declared any environment variables for this agent yet.</p>';
|
|
1183
1193
|
|
|
1184
|
-
var
|
|
1185
|
-
var summaryHtml = '<p class="hint">' + vars.length + ' environment variable' + (vars.length === 1 ? '' : 's') + ' across every installed ability' +
|
|
1186
|
-
(missingCount ? ' — <span class="error">' + missingCount + ' still not set</span>.' : ' — all set.') + '</p>';
|
|
1194
|
+
var summaryHtml = '<p class="hint">' + vars.length + ' environment variable' + (vars.length === 1 ? '' : 's') + ' across every installed ability.</p>';
|
|
1187
1195
|
|
|
1188
1196
|
// Grouped by declaring ability, not a single flat list — a var
|
|
1189
1197
|
// declared by more than one ability (rare, but see renderEnvRow's
|
|
@@ -1284,6 +1292,7 @@ export const agentsConfigPageHtml = `<!doctype html>
|
|
|
1284
1292
|
'<td><code>' + escapeHtml(a.name) + '</code></td>' +
|
|
1285
1293
|
'<td>' + escapeHtml(a.version) + '</td>' +
|
|
1286
1294
|
'<td class="hint">' + escapeHtml((a.tools || []).join(', ')) + '</td>' +
|
|
1295
|
+
'<td><button type="button" class="ability-upgrade-btn" data-name="' + escapeHtml(a.name) + '">Upgrade</button></td>' +
|
|
1287
1296
|
'</tr>';
|
|
1288
1297
|
}
|
|
1289
1298
|
|
|
@@ -1298,7 +1307,8 @@ export const agentsConfigPageHtml = `<!doctype html>
|
|
|
1298
1307
|
function renderAbilitiesConfigHtml(data) {
|
|
1299
1308
|
var installed = data.abilities || [];
|
|
1300
1309
|
var installedHtml = installed.length
|
|
1301
|
-
? '<table><thead><tr><th>Name</th><th>Version</th><th>Tools</th></tr></thead><tbody>' + installed.map(renderInstalledAbilityRow).join('') + '</tbody></table>'
|
|
1310
|
+
? '<table><thead><tr><th>Name</th><th>Version</th><th>Tools</th><th></th></tr></thead><tbody>' + installed.map(renderInstalledAbilityRow).join('') + '</tbody></table>' +
|
|
1311
|
+
'<p id="abilityUpgradeStatus" class="hint"></p>'
|
|
1302
1312
|
: '<p class="hint">No abilities installed for this agent yet.</p>';
|
|
1303
1313
|
|
|
1304
1314
|
return '<h3>Installed</h3>' + installedHtml +
|
|
@@ -1363,6 +1373,48 @@ export const agentsConfigPageHtml = `<!doctype html>
|
|
|
1363
1373
|
});
|
|
1364
1374
|
}
|
|
1365
1375
|
|
|
1376
|
+
// Upgrades an already-installed ability to whatever it currently
|
|
1377
|
+
// resolves to — same endpoint upgradeAbility (bin/ability-manager.ts)
|
|
1378
|
+
// backs, which three-way merges tool/skill files instead of blindly
|
|
1379
|
+
// overwriting a hand-edited one, so a merge conflict is a real,
|
|
1380
|
+
// expected outcome here, not an error — surfaced distinctly from a
|
|
1381
|
+
// request actually failing.
|
|
1382
|
+
function upgradeAbility(name, abilityName, statusEl, btn) {
|
|
1383
|
+
if (btn) { btn.disabled = true; }
|
|
1384
|
+
statusEl.className = 'hint';
|
|
1385
|
+
statusEl.textContent = 'Upgrading ' + abilityName + '…';
|
|
1386
|
+
fetch('/agents/' + encodeURIComponent(name) + '/abilities/' + encodeURIComponent(abilityName) + '/upgrade', {
|
|
1387
|
+
method: 'POST',
|
|
1388
|
+
headers: { 'content-type': 'application/json' },
|
|
1389
|
+
body: JSON.stringify({}),
|
|
1390
|
+
})
|
|
1391
|
+
.then(function (r) { return r.json().then(function (j) { return { ok: r.ok, body: j }; }); })
|
|
1392
|
+
.then(function (result) {
|
|
1393
|
+
if (!result.ok) throw new Error(result.body.error || 'request failed');
|
|
1394
|
+
var files = result.body.files || [];
|
|
1395
|
+
var updated = files.filter(function (f) { return f.status === 'updated'; });
|
|
1396
|
+
var conflicts = files.filter(function (f) { return f.status === 'conflict'; });
|
|
1397
|
+
var message;
|
|
1398
|
+
if (!files.length || (!updated.length && !conflicts.length)) {
|
|
1399
|
+
message = escapeHtml(abilityName) + ' is already up to date.';
|
|
1400
|
+
} else {
|
|
1401
|
+
message = 'Upgraded ' + escapeHtml(abilityName) + ': ' + escapeHtml(updated.map(function (f) { return f.path; }).join(', ') || 'no files changed') +
|
|
1402
|
+
'. Already running under npx loopengine dev? It becomes active automatically. Running under serve (or nothing yet)? Restart the server to pick it up.';
|
|
1403
|
+
if (conflicts.length) {
|
|
1404
|
+
message += ' <strong>' + conflicts.length + ' file(s) need resolving by hand: ' + escapeHtml(conflicts.map(function (f) { return f.path; }).join(', ')) + '</strong> — a hand-edited file with upstream changes too, left alone rather than overwritten.';
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
statusEl.className = conflicts.length ? 'error' : 'hint';
|
|
1408
|
+
statusEl.innerHTML = message;
|
|
1409
|
+
loadAbilitiesTab(name);
|
|
1410
|
+
})
|
|
1411
|
+
.catch(function (err) {
|
|
1412
|
+
statusEl.className = 'error';
|
|
1413
|
+
statusEl.textContent = 'Could not upgrade: ' + err.message;
|
|
1414
|
+
if (btn) { btn.disabled = false; }
|
|
1415
|
+
});
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1366
1418
|
// Shared by the initial (empty-query) load right after the tab opens
|
|
1367
1419
|
// and the search form's own submit — the empty-query case is just
|
|
1368
1420
|
// "list every published ability" (the keyword filter alone, no extra
|
|
@@ -1395,6 +1447,15 @@ export const agentsConfigPageHtml = `<!doctype html>
|
|
|
1395
1447
|
var content = abilitiesContentEl();
|
|
1396
1448
|
if (!content) return;
|
|
1397
1449
|
|
|
1450
|
+
var upgradeStatusEl = content.querySelector('#abilityUpgradeStatus');
|
|
1451
|
+
var upgradeBtns = content.querySelectorAll('.ability-upgrade-btn');
|
|
1452
|
+
for (var u = 0; u < upgradeBtns.length; u++) {
|
|
1453
|
+
upgradeBtns[u].addEventListener('click', function (ev) {
|
|
1454
|
+
var btn = ev.currentTarget;
|
|
1455
|
+
upgradeAbility(name, btn.dataset.name, upgradeStatusEl, btn);
|
|
1456
|
+
});
|
|
1457
|
+
}
|
|
1458
|
+
|
|
1398
1459
|
var searchForm = content.querySelector('#abilitySearchForm');
|
|
1399
1460
|
var resultsEl = content.querySelector('#abilitySearchResults');
|
|
1400
1461
|
if (searchForm && resultsEl) {
|
package/dist/web/env-admin.d.ts
CHANGED
|
@@ -14,6 +14,10 @@ export interface DeclaredEnvVar {
|
|
|
14
14
|
* web/http-tool-admin.ts's `{{ENV_VAR}}` header handling already
|
|
15
15
|
* follow. Undefined whenever `secret` is true or `set` is false. */
|
|
16
16
|
value?: string;
|
|
17
|
+
/** A closed set of valid values (see AbilityEnvDecl.options) — present
|
|
18
|
+
* only when the declaring ability actually named one; the Admin UI
|
|
19
|
+
* renders a dropdown instead of a free-text field when this is set. */
|
|
20
|
+
options?: string[];
|
|
17
21
|
}
|
|
18
22
|
/** Every env var any ability installed for `agentName` declared it
|
|
19
23
|
* needs, merged by name. There's one .env per *project*, not per agent
|
package/dist/web/env-admin.js
CHANGED