loopengine 0.1.47 → 0.1.49
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.
|
@@ -17,6 +17,15 @@ export interface AbilityEnvDecl {
|
|
|
17
17
|
* of a free-text field. Omit for anything that isn't genuinely a fixed
|
|
18
18
|
* enum (an API key, a free-form model name, a path, ...). */
|
|
19
19
|
options?: string[];
|
|
20
|
+
/** True for a value that's naturally multi-line (a pasted JSON key
|
|
21
|
+
* file, a PEM block, ...) — the Admin UI renders a <textarea> instead
|
|
22
|
+
* of a single-line <input>, which per the HTML spec's own value
|
|
23
|
+
* sanitization strips line breaks entirely and would otherwise mangle
|
|
24
|
+
* exactly this kind of value on paste, regardless of how correctly the
|
|
25
|
+
* .env file itself round-trips real embedded newlines (see
|
|
26
|
+
* env-admin.ts's own serializeEnvValue doc comment). Omit for anything
|
|
27
|
+
* genuinely single-line. */
|
|
28
|
+
multiline?: boolean;
|
|
20
29
|
}
|
|
21
30
|
/** `name`/`version` deliberately aren't declared here — they're read off
|
|
22
31
|
* the ability's own sibling `package.json` instead (see readManifest),
|
|
@@ -429,6 +429,22 @@ export const agentsConfigPageHtml = `<!doctype html>
|
|
|
429
429
|
});
|
|
430
430
|
}
|
|
431
431
|
|
|
432
|
+
// A stale keep-alive TCP connection reused right after the server (or
|
|
433
|
+
// whatever's proxying it) closed it server-side fails silently — an
|
|
434
|
+
// empty/truncated response, surfaced here as "Unexpected end of JSON
|
|
435
|
+
// input" from r.json() — not a real request failure; a fresh
|
|
436
|
+
// connection on an immediate retry works. Retried exactly once, not
|
|
437
|
+
// looped: a genuine server-side error fails the retry too and should
|
|
438
|
+
// still surface, not get masked by retrying forever.
|
|
439
|
+
function fetchJsonRetryOnce(url, options) {
|
|
440
|
+
function attempt() {
|
|
441
|
+
return fetch(url, options).then(function (r) {
|
|
442
|
+
return r.json().then(function (j) { return { ok: r.ok, body: j }; });
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
return attempt().catch(function () { return attempt(); });
|
|
446
|
+
}
|
|
447
|
+
|
|
432
448
|
function badge(label) {
|
|
433
449
|
return '<span class="badge ' + (label === 'custom' ? 'badge-custom' : 'badge-default') + '">' + escapeHtml(label) + '</span>';
|
|
434
450
|
}
|
|
@@ -808,12 +824,11 @@ export const agentsConfigPageHtml = `<!doctype html>
|
|
|
808
824
|
if (maxTokens && String(maxTokens).trim()) body.model.maxTokens = parseInt(maxTokens, 10);
|
|
809
825
|
var reasoningEffort = data.get('reasoningEffort');
|
|
810
826
|
if (reasoningEffort) body.model.reasoningEffort = reasoningEffort;
|
|
811
|
-
|
|
827
|
+
fetchJsonRetryOnce('/agents/' + encodeURIComponent(name), {
|
|
812
828
|
method: 'PUT',
|
|
813
829
|
headers: { 'content-type': 'application/json' },
|
|
814
830
|
body: JSON.stringify(body),
|
|
815
831
|
})
|
|
816
|
-
.then(function (r) { return r.json().then(function (j) { return { ok: r.ok, body: j }; }); })
|
|
817
832
|
.then(function (result) {
|
|
818
833
|
if (!result.ok) throw new Error(result.body.error || 'request failed');
|
|
819
834
|
currentCfg = result.body;
|
|
@@ -1172,11 +1187,21 @@ export const agentsConfigPageHtml = `<!doctype html>
|
|
|
1172
1187
|
// field, so there's nothing to mistype — the same "value" form field
|
|
1173
1188
|
// name either element uses means wireEnvHandlers's own submit
|
|
1174
1189
|
// handler doesn't need to know which one it's looking at.
|
|
1190
|
+
// A naturally multi-line value (AbilityEnvDecl.multiline, e.g. a
|
|
1191
|
+
// pasted JSON key file) gets a <textarea> — a single-line <input>
|
|
1192
|
+
// (even type="text") strips embedded line breaks from its value
|
|
1193
|
+
// entirely per the HTML spec's own value sanitization, mangling
|
|
1194
|
+
// exactly this kind of paste. No password-style masking for it
|
|
1195
|
+
// either — <textarea> has no such mode, and masking a large blob
|
|
1196
|
+
// secret buys little anyway compared to being able to paste it
|
|
1197
|
+
// correctly at all.
|
|
1175
1198
|
var fieldHtml = v.options
|
|
1176
1199
|
? '<select name="value">' + v.options.map(function (opt) {
|
|
1177
1200
|
return '<option value="' + escapeHtml(opt) + '"' + (v.value === opt ? ' selected' : '') + '>' + escapeHtml(opt) + '</option>';
|
|
1178
1201
|
}).join('') + '</select>'
|
|
1179
|
-
:
|
|
1202
|
+
: v.multiline
|
|
1203
|
+
? '<textarea name="value" rows="8" placeholder="' + (v.set ? 'unchanged unless you paste a new value' : 'paste the full value') + '" required></textarea>'
|
|
1204
|
+
: '<input type="' + (v.secret ? 'password' : 'text') + '" name="value" placeholder="' + (v.set ? 'unchanged unless you type a new value' : 'value') + '" required>';
|
|
1180
1205
|
return '<tr>' +
|
|
1181
1206
|
'<td><code>' + escapeHtml(v.name) + '</code>' + sharedNote + '</td>' +
|
|
1182
1207
|
'<td style="max-width:320px">' + escapeHtml(v.description || '') + '</td>' +
|
package/dist/web/env-admin.d.ts
CHANGED
|
@@ -18,6 +18,11 @@ export interface DeclaredEnvVar {
|
|
|
18
18
|
* only when the declaring ability actually named one; the Admin UI
|
|
19
19
|
* renders a dropdown instead of a free-text field when this is set. */
|
|
20
20
|
options?: string[];
|
|
21
|
+
/** True for a naturally multi-line value (see AbilityEnvDecl.multiline)
|
|
22
|
+
* — the Admin UI renders a <textarea> instead of a single-line
|
|
23
|
+
* <input>, which strips embedded line breaks entirely per the HTML
|
|
24
|
+
* spec's own value sanitization. */
|
|
25
|
+
multiline?: boolean;
|
|
21
26
|
}
|
|
22
27
|
/** Every env var any ability installed for `agentName` declared it
|
|
23
28
|
* needs, merged by name. There's one .env per *project*, not per agent
|
package/dist/web/env-admin.js
CHANGED
|
@@ -66,6 +66,7 @@ export function listDeclaredEnvVars(agentName) {
|
|
|
66
66
|
set: rawValue !== undefined,
|
|
67
67
|
value: decl.secret === true ? undefined : rawValue,
|
|
68
68
|
options: decl.options,
|
|
69
|
+
multiline: decl.multiline,
|
|
69
70
|
});
|
|
70
71
|
}
|
|
71
72
|
}
|
|
@@ -81,13 +82,26 @@ function envFilePath() {
|
|
|
81
82
|
return join(process.cwd(), '.env');
|
|
82
83
|
}
|
|
83
84
|
// Node's own --env-file parser (what bin/cli.ts's runTsx already passes
|
|
84
|
-
// as --env-file-if-exists=.env)
|
|
85
|
-
//
|
|
86
|
-
//
|
|
87
|
-
//
|
|
85
|
+
// as --env-file-if-exists=.env) needs a value containing whitespace, '#',
|
|
86
|
+
// or a quote character quoted, so it reads back as one value instead of
|
|
87
|
+
// being reinterpreted as a comment or truncated at the first space.
|
|
88
|
+
// Single-quoting wins over double — confirmed live against Node's actual
|
|
89
|
+
// parser that a single-quoted value reads back fully literally, with no
|
|
90
|
+
// backslash-escape processing inside it at all: a real embedded newline,
|
|
91
|
+
// a literal double-quote, or a literal backslash (a JSON secret like
|
|
92
|
+
// GOOGLE_APPLICATION_CREDENTIALS_JSON has all three at once) all survive
|
|
93
|
+
// untouched. Double-quoting a value with an *escaped* embedded quote does
|
|
94
|
+
// NOT round-trip — confirmed live that Node's parser has no support for
|
|
95
|
+
// \" as an escaped literal quote inside a double-quoted value; it just
|
|
96
|
+
// ends the value there instead (`X="a\"b"` reads back as only `a\`,
|
|
97
|
+
// silently dropping `b"` entirely). The double-quote fallback below is
|
|
98
|
+
// only reached when the value itself contains a literal single quote —
|
|
99
|
+
// the one character single-quoting can't represent at all.
|
|
88
100
|
function serializeEnvValue(value) {
|
|
89
101
|
if (!/[\s#"'\\]/.test(value) && value !== '')
|
|
90
102
|
return value;
|
|
103
|
+
if (!value.includes("'"))
|
|
104
|
+
return `'${value}'`;
|
|
91
105
|
return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
92
106
|
}
|
|
93
107
|
/** Upserts `NAME=VALUE` into the project's `.env` file — preserving
|