loopengine 0.1.36 → 0.1.38

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.
@@ -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),
@@ -1138,41 +1138,60 @@ export const agentsConfigPageHtml = `<!doctype html>
1138
1138
  var sharedNote = v.abilityNames.length > 1
1139
1139
  ? ' <span class="hint" title="Also declared by: ' + escapeHtml(v.abilityNames.join(', ')) + '. There is only one value for it project-wide, so check whether they actually need the same one.">(shared)</span>'
1140
1140
  : '';
1141
+ // A secret's value is never sent to the browser at all (see
1142
+ // env-admin.ts) — "set" is the most this tab can ever say about one.
1143
+ // A non-secret var's actual value comes through, so show it instead
1144
+ // of a bare "set", since the whole point of looking here is usually
1145
+ // to check *what* something is currently configured to, not just
1146
+ // whether it has a value.
1147
+ var statusHtml = !v.set
1148
+ ? '<span class="error">not set</span>'
1149
+ : v.secret
1150
+ ? '<span class="hint">set</span>'
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>';
1141
1162
  return '<tr>' +
1142
1163
  '<td><code>' + escapeHtml(v.name) + '</code>' + sharedNote + '</td>' +
1143
1164
  '<td style="max-width:320px">' + escapeHtml(v.description || '') + '</td>' +
1144
- '<td>' + (v.set ? '<span class="hint">set</span>' : '<span class="error">not set</span>') + '</td>' +
1165
+ '<td>' + statusHtml + '</td>' +
1145
1166
  '<td><form class="add-source env-var-form" data-name="' + escapeHtml(v.name) + '">' +
1146
- '<input type="' + (v.secret ? 'password' : 'text') + '" name="value" placeholder="' + (v.set ? 'unchanged unless you type a new value' : 'value') + '" required>' +
1167
+ fieldHtml +
1147
1168
  '<button type="submit">Save</button>' +
1148
1169
  '</form></td>' +
1149
1170
  '</tr>';
1150
1171
  }
1151
1172
 
1152
- // One card per ability, matching the same .source/.source-head/
1153
- // status-ok/status-error look Gateway Tools sources already use — so
1154
- // an operator scanning the tab sees, per ability, whether it's fully
1155
- // configured or not, instead of hunting through one long flat table
1156
- // for rows that belong to whichever ability they're actually trying to
1157
- // set up right now.
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.
1158
1183
  function renderEnvAbilityCard(abilityName, vars) {
1159
- var missing = vars.filter(function (v) { return !v.set; }).length;
1160
- var statusHtml = missing === 0
1161
- ? '<span class="status-ok">all set</span>'
1162
- : '<span class="status-error">' + missing + ' of ' + vars.length + ' not set</span>';
1163
1184
  var rows = vars.map(renderEnvRow).join('');
1164
1185
  return '<div class="source">' +
1165
- '<div class="source-head"><h4>' + escapeHtml(abilityName) + '</h4>' + statusHtml + '</div>' +
1166
- '<table><thead><tr><th>Name</th><th style="max-width:320px">Description</th><th>Status</th><th>Set value</th></tr></thead><tbody>' + rows + '</tbody></table>' +
1186
+ '<div class="source-head"><h4>' + escapeHtml(abilityName) + '</h4><span class="hint">' + vars.length + ' var' + (vars.length === 1 ? '' : 's') + '</span></div>' +
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>' +
1167
1188
  '</div>';
1168
1189
  }
1169
1190
 
1170
1191
  function renderEnvConfigHtml(vars) {
1171
1192
  if (!vars.length) return '<p class="hint">No installed ability has declared any environment variables for this agent yet.</p>';
1172
1193
 
1173
- var missingCount = vars.filter(function (v) { return !v.set; }).length;
1174
- var summaryHtml = '<p class="hint">' + vars.length + ' environment variable' + (vars.length === 1 ? '' : 's') + ' across every installed ability' +
1175
- (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>';
1176
1195
 
1177
1196
  // Grouped by declaring ability, not a single flat list — a var
1178
1197
  // declared by more than one ability (rare, but see renderEnvRow's
@@ -9,6 +9,15 @@ export interface DeclaredEnvVar {
9
9
  * an array, not a single name. */
10
10
  abilityNames: string[];
11
11
  set: boolean;
12
+ /** The live value, but only when `secret` is false — a secret is never
13
+ * echoed back once set, same rule this file's own top comment and
14
+ * web/http-tool-admin.ts's `{{ENV_VAR}}` header handling already
15
+ * follow. Undefined whenever `secret` is true or `set` is false. */
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[];
12
21
  }
13
22
  /** Every env var any ability installed for `agentName` declared it
14
23
  * needs, merged by name. There's one .env per *project*, not per agent
@@ -1,9 +1,11 @@
1
1
  // Backs the Admin UI's "Environment" section — every env var an ability
2
2
  // (see ABILITIES.md, bin/ability-manager.ts) declared as required,
3
3
  // across every ability installed for an agent, with set/not-set status
4
- // only. A value marked `secret` is never echoed back once set — same
5
- // never-echo-a-secret rule web/http-tool-admin.ts's own `{{ENV_VAR}}`
6
- // header handling already establishes for a tool's own secrets.
4
+ // and, for a non-secret var, its live value (so an operator can actually
5
+ // tell what's configured, not just that something is). A value marked
6
+ // `secret` is never echoed back once set — same never-echo-a-secret rule
7
+ // web/http-tool-admin.ts's own `{{ENV_VAR}}` header handling already
8
+ // establishes for a tool's own secrets.
7
9
  import { existsSync, readFileSync, writeFileSync } from 'node:fs';
8
10
  import { join } from 'node:path';
9
11
  import { agentDir } from '../core/gateway-tools.js';
@@ -51,14 +53,19 @@ export function listDeclaredEnvVars(agentName) {
51
53
  if (existing) {
52
54
  existing.abilityNames.push(abilityName);
53
55
  existing.secret = existing.secret || decl.secret === true;
56
+ if (existing.secret)
57
+ existing.value = undefined;
54
58
  continue;
55
59
  }
60
+ const rawValue = process.env[decl.name];
56
61
  byName.set(decl.name, {
57
62
  name: decl.name,
58
63
  description: decl.description,
59
64
  secret: decl.secret === true,
60
65
  abilityNames: [abilityName],
61
- set: process.env[decl.name] !== undefined,
66
+ set: rawValue !== undefined,
67
+ value: decl.secret === true ? undefined : rawValue,
68
+ options: decl.options,
62
69
  });
63
70
  }
64
71
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loopengine",
3
- "version": "0.1.36",
3
+ "version": "0.1.38",
4
4
  "type": "module",
5
5
  "description": "Runtime for tool-using AI agents with a transparent ReAct loop.",
6
6
  "license": "MIT",