loopengine 0.1.62 → 0.1.64
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/README.md +37 -11
- package/dist/core/agent-env.d.ts +2 -8
- package/dist/core/agent-env.js +7 -19
- package/dist/core/model-calls/anthropic-model-call.js +2 -1
- package/dist/core/model-calls/deepseek-model-call.js +2 -1
- package/dist/core/model-calls/gemini-model-call.js +2 -1
- package/dist/core/model-calls/glm-model-call.js +2 -1
- package/dist/core/model-calls/kimi-model-call.js +2 -1
- package/dist/core/model-calls/openai-model-call.js +2 -1
- package/dist/core/run-agent.d.ts +6 -0
- package/dist/core/run-agent.js +20 -2
- package/dist/core/secret-store.d.ts +32 -0
- package/dist/core/secret-store.js +116 -0
- package/dist/core/trusted-proxy.d.ts +15 -0
- package/dist/core/trusted-proxy.js +47 -0
- package/dist/core/usage.d.ts +18 -0
- package/dist/core/usage.js +32 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +3 -0
- package/dist/web/agents-config-page.js +228 -294
- package/dist/web/agents-list-page.js +80 -99
- package/dist/web/console-shell.d.ts +2 -0
- package/dist/web/console-shell.js +156 -0
- package/dist/web/dev-ui-styles.js +146 -40
- package/dist/web/env-admin.d.ts +26 -25
- package/dist/web/env-admin.js +39 -103
- package/dist/web/global-config-page.js +26 -80
- package/dist/web/playground.js +161 -105
- package/package.json +7 -1
- package/dist/core/system-skills/system-skills/composio-large-outputs/SKILL.md +0 -25
package/dist/web/env-admin.d.ts
CHANGED
|
@@ -65,31 +65,32 @@ export interface DeclaredEnvVar {
|
|
|
65
65
|
* live (process.env for a shared row, the agent's own .env file for an
|
|
66
66
|
* agent row), not cached. */
|
|
67
67
|
export declare function listDeclaredEnvVars(agentName: string): DeclaredEnvVar[];
|
|
68
|
-
/**
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
* value some vars would treat
|
|
78
|
-
* through to a default only when
|
|
79
|
-
* *this* running process's own
|
|
80
|
-
* "no restart needed" promise
|
|
81
|
-
* not an error, when the var was
|
|
82
|
-
* the end state ("not set") is identical
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
*
|
|
68
|
+
/** Persists a project-wide value through the secret store (the project's
|
|
69
|
+
* `.env` by default — see core/secret-store.ts) and applies it to *this*
|
|
70
|
+
* running process immediately via `process.env`, so a newly-installed
|
|
71
|
+
* ability's tools work without a restart. Callers (the PUT route in
|
|
72
|
+
* adapters/http.ts) are responsible for refusing to call this at all
|
|
73
|
+
* when the server has no auth configured — this function itself has no
|
|
74
|
+
* notion of HTTP auth, it just writes. */
|
|
75
|
+
export declare function setEnvVar(name: string, value: string): Promise<void>;
|
|
76
|
+
/** Removes a project-wide value entirely (not just blanking it — an
|
|
77
|
+
* empty string is itself a real, distinct value some vars would treat
|
|
78
|
+
* differently from "unset," e.g. falling through to a default only when
|
|
79
|
+
* truly absent) and clears it from *this* running process's own
|
|
80
|
+
* process.env immediately, the same "no restart needed" promise
|
|
81
|
+
* setEnvVar already makes. A no-op, not an error, when the var was
|
|
82
|
+
* never set to begin with — the end state ("not set") is identical
|
|
83
|
+
* either way. */
|
|
84
|
+
export declare function unsetEnvVar(name: string): Promise<void>;
|
|
85
|
+
/** Persists `agentName`'s own value (agents/<name>/.env by default) —
|
|
86
|
+
* read by that agent's tools through ToolContext.env (core/agent-env.ts),
|
|
87
|
+
* which reads it fresh on every lookup, so no restart is needed.
|
|
87
88
|
* Deliberately never copied into process.env: that's shared by every
|
|
88
89
|
* agent in the process, which is exactly what a per-agent value must
|
|
89
90
|
* not be. Same auth responsibility for callers as setEnvVar. */
|
|
90
|
-
export declare function setAgentEnvVar(agentName: string, name: string, value: string): void
|
|
91
|
-
/** Removes
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
export declare function unsetAgentEnvVar(agentName: string, name: string): void
|
|
91
|
+
export declare function setAgentEnvVar(agentName: string, name: string, value: string): Promise<void>;
|
|
92
|
+
/** Removes `agentName`'s own value — and the legacy prefixed project var
|
|
93
|
+
* for it too (see agentScopedEnvVarName), since createAgentEnv still
|
|
94
|
+
* falls back to that one, and "Remove" on this agent's row should leave
|
|
95
|
+
* the agent with no value of its own either way. */
|
|
96
|
+
export declare function unsetAgentEnvVar(agentName: string, name: string): Promise<void>;
|
package/dist/web/env-admin.js
CHANGED
|
@@ -6,10 +6,11 @@
|
|
|
6
6
|
// `secret` is never echoed back once set — same never-echo-a-secret rule
|
|
7
7
|
// web/http-tool-admin.ts's own `{{ENV_VAR}}` header handling already
|
|
8
8
|
// establishes for a tool's own secrets.
|
|
9
|
-
import { existsSync, readFileSync
|
|
9
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
10
10
|
import { join } from 'node:path';
|
|
11
11
|
import { agentDir } from '../core/gateway-tools.js';
|
|
12
|
-
import {
|
|
12
|
+
import { agentScopedEnvVarName, envScopeOf } from '../core/agent-env.js';
|
|
13
|
+
import { getSecretStore } from '../core/secret-store.js';
|
|
13
14
|
export { agentScopedEnvVarName };
|
|
14
15
|
export class EnvVarNameError extends Error {
|
|
15
16
|
}
|
|
@@ -50,7 +51,7 @@ export function listDeclaredEnvVars(agentName) {
|
|
|
50
51
|
if (!existsSync(path))
|
|
51
52
|
return [];
|
|
52
53
|
const provenance = JSON.parse(readFileSync(path, 'utf8'));
|
|
53
|
-
const agentValues =
|
|
54
|
+
const agentValues = getSecretStore().readAgentValues(agentName, agentDir(agentName));
|
|
54
55
|
const rows = new Map();
|
|
55
56
|
function upsert(slot, abilityName, decl, description) {
|
|
56
57
|
const scope = envScopeOf(decl);
|
|
@@ -103,12 +104,10 @@ export function listDeclaredEnvVars(agentName) {
|
|
|
103
104
|
const scope = envScopeOf(decl);
|
|
104
105
|
if (scope !== 'agent')
|
|
105
106
|
upsert('shared', abilityName, decl, decl.description);
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
upsert('agent', abilityName, decl, `Set per agent — never shared with or borrowed from other agents.${decl.description ? ` ${decl.description}` : ''}`);
|
|
111
|
-
}
|
|
107
|
+
// `slot` and `scope` already say whose value a row is and whether
|
|
108
|
+
// it falls back, so the agent row keeps the ability's own words.
|
|
109
|
+
if (scope !== 'shared')
|
|
110
|
+
upsert('agent', abilityName, decl, decl.description);
|
|
112
111
|
}
|
|
113
112
|
}
|
|
114
113
|
// A name that's `agent`-scoped by any declaring ability is never read
|
|
@@ -120,115 +119,52 @@ export function listDeclaredEnvVars(agentName) {
|
|
|
120
119
|
}
|
|
121
120
|
return [...rows.values()];
|
|
122
121
|
}
|
|
123
|
-
// process.cwd(), matching exactly where bin/cli.ts's own runTsx passes
|
|
124
|
-
// --env-file-if-exists=.env (Node's own --env-file resolves relative to
|
|
125
|
-
// cwd too) — not core/agent-registry.ts's projectDir(), which resolves
|
|
126
|
-
// relative to *that compiled file's own location* and would point at
|
|
127
|
-
// node_modules/loopengine/dist/ in a real scaffolded project, not the
|
|
128
|
-
// project's own root where .env actually lives.
|
|
129
|
-
function envFilePath() {
|
|
130
|
-
return join(process.cwd(), '.env');
|
|
131
|
-
}
|
|
132
|
-
// Node's own --env-file parser (what bin/cli.ts's runTsx already passes
|
|
133
|
-
// as --env-file-if-exists=.env) needs a value containing whitespace, '#',
|
|
134
|
-
// or a quote character quoted, so it reads back as one value instead of
|
|
135
|
-
// being reinterpreted as a comment or truncated at the first space.
|
|
136
|
-
// Single-quoting wins over double — confirmed live against Node's actual
|
|
137
|
-
// parser that a single-quoted value reads back fully literally, with no
|
|
138
|
-
// backslash-escape processing inside it at all: a real embedded newline,
|
|
139
|
-
// a literal double-quote, or a literal backslash (a JSON secret like
|
|
140
|
-
// GOOGLE_APPLICATION_CREDENTIALS_JSON has all three at once) all survive
|
|
141
|
-
// untouched. Double-quoting a value with an *escaped* embedded quote does
|
|
142
|
-
// NOT round-trip — confirmed live that Node's parser has no support for
|
|
143
|
-
// \" as an escaped literal quote inside a double-quoted value; it just
|
|
144
|
-
// ends the value there instead (`X="a\"b"` reads back as only `a\`,
|
|
145
|
-
// silently dropping `b"` entirely). The double-quote fallback below is
|
|
146
|
-
// only reached when the value itself contains a literal single quote —
|
|
147
|
-
// the one character single-quoting can't represent at all.
|
|
148
|
-
function serializeEnvValue(value) {
|
|
149
|
-
if (!/[\s#"'\\]/.test(value) && value !== '')
|
|
150
|
-
return value;
|
|
151
|
-
if (!value.includes("'"))
|
|
152
|
-
return `'${value}'`;
|
|
153
|
-
return `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"')}"`;
|
|
154
|
-
}
|
|
155
122
|
function assertEnvVarName(name) {
|
|
156
123
|
if (!ENV_VAR_NAME_PATTERN.test(name)) {
|
|
157
124
|
throw new EnvVarNameError(`"${name}" isn't a valid env var name (uppercase letters, digits, underscore, not starting with a digit).`);
|
|
158
125
|
}
|
|
159
126
|
}
|
|
160
|
-
/**
|
|
161
|
-
*
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
// newline splitting into an empty last element) before appending,
|
|
169
|
-
// so this doesn't accumulate a growing gap of blank lines across
|
|
170
|
-
// repeated calls.
|
|
171
|
-
if (lines.length > 0 && lines[lines.length - 1] === '')
|
|
172
|
-
lines.pop();
|
|
173
|
-
lines.push(newLine);
|
|
174
|
-
}
|
|
175
|
-
else {
|
|
176
|
-
lines[existingIndex] = newLine;
|
|
177
|
-
}
|
|
178
|
-
writeFileSync(path, lines.join('\n') + '\n');
|
|
179
|
-
}
|
|
180
|
-
/** Removes NAME= from the .env file at `path`, if present. Only
|
|
181
|
-
* rewrites the file when something actually changed — a no-op removal
|
|
182
|
-
* shouldn't still touch the file's own mtime/trailing-newline shape for
|
|
183
|
-
* no reason. */
|
|
184
|
-
function removeEnvFileLine(path, name) {
|
|
185
|
-
if (!existsSync(path))
|
|
186
|
-
return;
|
|
187
|
-
const lines = readFileSync(path, 'utf8').split('\n');
|
|
188
|
-
const filtered = lines.filter((line) => !line.startsWith(`${name}=`));
|
|
189
|
-
if (filtered.length !== lines.length)
|
|
190
|
-
writeFileSync(path, filtered.join('\n').replace(/\n*$/, '\n'));
|
|
191
|
-
}
|
|
192
|
-
/** Upserts `NAME=VALUE` into the project's `.env` file and applies it to
|
|
193
|
-
* *this* running process immediately via `process.env`, so a
|
|
194
|
-
* newly-installed ability's tools work without a restart. Callers (the
|
|
195
|
-
* PUT route in adapters/http.ts) are responsible for refusing to call
|
|
196
|
-
* this at all when `LOOPENGINE_ADMIN_AUTH` isn't set — this function
|
|
197
|
-
* itself has no notion of HTTP auth, it just writes. */
|
|
198
|
-
export function setEnvVar(name, value) {
|
|
127
|
+
/** Persists a project-wide value through the secret store (the project's
|
|
128
|
+
* `.env` by default — see core/secret-store.ts) and applies it to *this*
|
|
129
|
+
* running process immediately via `process.env`, so a newly-installed
|
|
130
|
+
* ability's tools work without a restart. Callers (the PUT route in
|
|
131
|
+
* adapters/http.ts) are responsible for refusing to call this at all
|
|
132
|
+
* when the server has no auth configured — this function itself has no
|
|
133
|
+
* notion of HTTP auth, it just writes. */
|
|
134
|
+
export async function setEnvVar(name, value) {
|
|
199
135
|
assertEnvVarName(name);
|
|
200
|
-
|
|
136
|
+
await getSecretStore().setShared(name, value);
|
|
201
137
|
process.env[name] = value;
|
|
202
138
|
}
|
|
203
|
-
/** Removes
|
|
204
|
-
*
|
|
205
|
-
*
|
|
206
|
-
*
|
|
207
|
-
*
|
|
208
|
-
*
|
|
209
|
-
*
|
|
210
|
-
*
|
|
211
|
-
export function unsetEnvVar(name) {
|
|
139
|
+
/** Removes a project-wide value entirely (not just blanking it — an
|
|
140
|
+
* empty string is itself a real, distinct value some vars would treat
|
|
141
|
+
* differently from "unset," e.g. falling through to a default only when
|
|
142
|
+
* truly absent) and clears it from *this* running process's own
|
|
143
|
+
* process.env immediately, the same "no restart needed" promise
|
|
144
|
+
* setEnvVar already makes. A no-op, not an error, when the var was
|
|
145
|
+
* never set to begin with — the end state ("not set") is identical
|
|
146
|
+
* either way. */
|
|
147
|
+
export async function unsetEnvVar(name) {
|
|
212
148
|
assertEnvVarName(name);
|
|
213
|
-
|
|
149
|
+
await getSecretStore().unsetShared(name);
|
|
214
150
|
delete process.env[name];
|
|
215
151
|
}
|
|
216
|
-
/**
|
|
217
|
-
* by that agent's tools through ToolContext.env (core/agent-env.ts),
|
|
218
|
-
* which reads
|
|
152
|
+
/** Persists `agentName`'s own value (agents/<name>/.env by default) —
|
|
153
|
+
* read by that agent's tools through ToolContext.env (core/agent-env.ts),
|
|
154
|
+
* which reads it fresh on every lookup, so no restart is needed.
|
|
219
155
|
* Deliberately never copied into process.env: that's shared by every
|
|
220
156
|
* agent in the process, which is exactly what a per-agent value must
|
|
221
157
|
* not be. Same auth responsibility for callers as setEnvVar. */
|
|
222
|
-
export function setAgentEnvVar(agentName, name, value) {
|
|
158
|
+
export async function setAgentEnvVar(agentName, name, value) {
|
|
223
159
|
assertEnvVarName(name);
|
|
224
|
-
|
|
160
|
+
await getSecretStore().setAgent(agentName, agentDir(agentName), name, value);
|
|
225
161
|
}
|
|
226
|
-
/** Removes
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
export function unsetAgentEnvVar(agentName, name) {
|
|
162
|
+
/** Removes `agentName`'s own value — and the legacy prefixed project var
|
|
163
|
+
* for it too (see agentScopedEnvVarName), since createAgentEnv still
|
|
164
|
+
* falls back to that one, and "Remove" on this agent's row should leave
|
|
165
|
+
* the agent with no value of its own either way. */
|
|
166
|
+
export async function unsetAgentEnvVar(agentName, name) {
|
|
231
167
|
assertEnvVarName(name);
|
|
232
|
-
|
|
233
|
-
unsetEnvVar(agentScopedEnvVarName(agentName, name));
|
|
168
|
+
await getSecretStore().unsetAgent(agentName, agentDir(agentName), name);
|
|
169
|
+
await unsetEnvVar(agentScopedEnvVarName(agentName, name));
|
|
234
170
|
}
|
|
@@ -20,67 +20,19 @@
|
|
|
20
20
|
// hazard (template-literal escapes run twice: once when this constant
|
|
21
21
|
// is built, once when a browser parses the served <script> as JS).
|
|
22
22
|
import { devUiCss } from './dev-ui-styles.js';
|
|
23
|
+
import { consoleShellScript, consoleSidebarHtml } from './console-shell.js';
|
|
23
24
|
export const globalConfigPageHtml = `<!doctype html>
|
|
24
25
|
<html lang="en">
|
|
25
26
|
<head>
|
|
26
27
|
<meta charset="utf-8">
|
|
27
28
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
28
|
-
<title>LoopEngine
|
|
29
|
+
<title>LoopEngine Settings</title>
|
|
29
30
|
<style>${devUiCss}
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
height: 100vh;
|
|
34
|
-
}
|
|
35
|
-
.layout { flex: 1; display: flex; min-height: 0; }
|
|
36
|
-
nav.sidebar {
|
|
37
|
-
width: 180px;
|
|
38
|
-
flex-shrink: 0;
|
|
39
|
-
border-right: 1px solid light-dark(#ddd, #333);
|
|
40
|
-
padding: 6px;
|
|
41
|
-
}
|
|
42
|
-
nav.sidebar button, nav.sidebar a {
|
|
43
|
-
display: block;
|
|
44
|
-
width: 100%;
|
|
45
|
-
text-align: left;
|
|
46
|
-
background: none;
|
|
47
|
-
border: none;
|
|
48
|
-
border-radius: 6px;
|
|
49
|
-
padding: 8px 10px;
|
|
50
|
-
font-size: 13px;
|
|
51
|
-
cursor: pointer;
|
|
52
|
-
text-decoration: none;
|
|
53
|
-
color: inherit;
|
|
54
|
-
box-sizing: border-box;
|
|
55
|
-
}
|
|
56
|
-
nav.sidebar button:hover, nav.sidebar a:hover { background: light-dark(#eee, #26262b); }
|
|
57
|
-
nav.sidebar button.active { background: light-dark(#dbeafe, #1e3a5f); font-weight: 600; }
|
|
58
|
-
main {
|
|
59
|
-
flex: 1;
|
|
60
|
-
overflow-y: auto;
|
|
61
|
-
padding: 20px 28px;
|
|
62
|
-
}
|
|
63
|
-
h2 { font-size: 20px; margin: 0 0 4px; font-family: ui-monospace, monospace; }
|
|
64
|
-
section { margin-bottom: 22px; }
|
|
65
|
-
section h3 {
|
|
66
|
-
font-size: 12px;
|
|
67
|
-
text-transform: uppercase;
|
|
68
|
-
letter-spacing: 0.04em;
|
|
69
|
-
color: light-dark(#666, #999);
|
|
70
|
-
margin: 0 0 8px;
|
|
71
|
-
border-bottom: 1px solid light-dark(#eee, #2a2a2e);
|
|
72
|
-
padding-bottom: 4px;
|
|
73
|
-
}
|
|
74
|
-
table { width: 100%; border-collapse: collapse; font-size: 12px; }
|
|
75
|
-
th, td {
|
|
76
|
-
text-align: left;
|
|
77
|
-
padding: 6px 8px;
|
|
78
|
-
border-bottom: 1px solid light-dark(#eee, #2a2a2e);
|
|
79
|
-
vertical-align: top;
|
|
80
|
-
}
|
|
81
|
-
th { color: light-dark(#666, #999); font-weight: 600; }
|
|
31
|
+
section { margin-bottom: 28px; }
|
|
32
|
+
section h3 { font-size: 15px; font-weight: 600; margin: 0 0 10px; }
|
|
33
|
+
section > table { border: 1px solid var(--line); border-radius: var(--radius); border-collapse: separate; border-spacing: 0; overflow: hidden; }
|
|
82
34
|
.source {
|
|
83
|
-
border: 1px solid
|
|
35
|
+
border: 1px solid var(--line);
|
|
84
36
|
border-radius: 8px;
|
|
85
37
|
padding: 12px 14px;
|
|
86
38
|
margin-bottom: 10px;
|
|
@@ -88,10 +40,10 @@ export const globalConfigPageHtml = `<!doctype html>
|
|
|
88
40
|
.source.unsupported { opacity: 0.6; }
|
|
89
41
|
.source-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
|
|
90
42
|
.source-head h4 { margin: 0; font-size: 14px; font-family: ui-monospace, monospace; }
|
|
91
|
-
.hint { font-size: 11px; color:
|
|
43
|
+
.hint { font-size: 11px; color: var(--ink-muted); }
|
|
92
44
|
pre {
|
|
93
|
-
background:
|
|
94
|
-
border: 1px solid
|
|
45
|
+
background: var(--bg);
|
|
46
|
+
border: 1px solid var(--line);
|
|
95
47
|
border-radius: 6px;
|
|
96
48
|
padding: 8px 10px;
|
|
97
49
|
font-size: 12px;
|
|
@@ -105,36 +57,30 @@ export const globalConfigPageHtml = `<!doctype html>
|
|
|
105
57
|
this pink-for-destructive convention is meant to drift between them. */
|
|
106
58
|
.delete-btn {
|
|
107
59
|
font-size: 12px;
|
|
108
|
-
color:
|
|
109
|
-
background:
|
|
110
|
-
border-color:
|
|
60
|
+
color: var(--bad);
|
|
61
|
+
background: var(--bad-soft);
|
|
62
|
+
border-color: var(--bad);
|
|
111
63
|
}
|
|
112
|
-
.delete-btn:hover { background:
|
|
64
|
+
.delete-btn:hover { background: var(--bad-soft); }
|
|
113
65
|
</style>
|
|
114
66
|
</head>
|
|
115
|
-
<body>
|
|
116
|
-
<
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
<
|
|
120
|
-
|
|
121
|
-
<div
|
|
122
|
-
<
|
|
123
|
-
<a href="/agents/config">Agents</a>
|
|
124
|
-
<button class="section-btn active" data-section="models">Models</button>
|
|
125
|
-
<button class="section-btn" data-section="gateways">Gateways</button>
|
|
126
|
-
</nav>
|
|
127
|
-
<main>
|
|
128
|
-
<div class="section-panel" data-section-panel="models">
|
|
129
|
-
<h2>Models</h2>
|
|
130
|
-
<div id="modelsContent"><p class="hint">Loading…</p></div>
|
|
67
|
+
<body data-le-page="config">
|
|
68
|
+
<div class="le-app">
|
|
69
|
+
${consoleSidebarHtml}
|
|
70
|
+
<main class="le-main">
|
|
71
|
+
<div class="section-panel" data-section-panel="models" style="display:contents">
|
|
72
|
+
<div class="le-head plain"><div class="le-title"><button type="button" class="le-menu-btn" data-le-menu aria-label="Open navigation">☰</button>
|
|
73
|
+
<div><h1>Models</h1><p>Which model providers have an API key set, and which agents use each one.</p></div></div></div>
|
|
74
|
+
<div class="le-scroll"><div class="le-page" style="display:block"><div id="modelsContent"><p class="hint">Loading…</p></div></div></div>
|
|
131
75
|
</div>
|
|
132
76
|
<div class="section-panel" data-section-panel="gateways" style="display:none">
|
|
133
|
-
<
|
|
134
|
-
|
|
77
|
+
<div class="le-head plain"><div class="le-title"><button type="button" class="le-menu-btn" data-le-menu aria-label="Open navigation">☰</button>
|
|
78
|
+
<div><h1>Integrations</h1><p>Outside tool sources (such as Composio) that agents can add tools from.</p></div></div></div>
|
|
79
|
+
<div class="le-scroll"><div class="le-page" style="display:block"><div id="gatewaysContent"><p class="hint">Loading…</p></div></div></div>
|
|
135
80
|
</div>
|
|
136
81
|
</main>
|
|
137
82
|
</div>
|
|
83
|
+
${consoleShellScript}
|
|
138
84
|
<script>
|
|
139
85
|
(function () {
|
|
140
86
|
var main = document.querySelector('main');
|
|
@@ -279,7 +225,7 @@ export const globalConfigPageHtml = `<!doctype html>
|
|
|
279
225
|
}
|
|
280
226
|
var panels = main.querySelectorAll('.section-panel');
|
|
281
227
|
for (var j = 0; j < panels.length; j++) {
|
|
282
|
-
panels[j].style.display = panels[j].dataset.sectionPanel === section ? '
|
|
228
|
+
panels[j].style.display = panels[j].dataset.sectionPanel === section ? 'contents' : 'none';
|
|
283
229
|
}
|
|
284
230
|
if (section === 'models' && !loadedSections.models) loadModels();
|
|
285
231
|
if (section === 'gateways' && !loadedSections.gateways) loadGateways();
|