create-loopengine 0.1.58 → 0.1.60

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 CHANGED
@@ -16,7 +16,7 @@ clone of loopengine's own source repo.
16
16
  npx create-loopengine@latest my-agents
17
17
  cd my-agents
18
18
  npm install
19
- cp .env.example .env # fill in ANTHROPIC_API_KEY
19
+ cp .env.example .env # fill in OPENAI_API_KEY (or set it later on the Models page)
20
20
  npm run dev # HTTP server on :8787
21
21
  ```
22
22
 
package/dist/cli.js CHANGED
@@ -285,7 +285,7 @@ export function main(argv = process.argv.slice(2)) {
285
285
  console.log();
286
286
  console.log(` cd ${destination}`);
287
287
  console.log(' npm install');
288
- console.log(' cp .env.example .env # fill in ANTHROPIC_API_KEY');
288
+ console.log(' cp .env.example .env # fill in OPENAI_API_KEY (or set it on the Models page)');
289
289
  console.log(' npm run dev');
290
290
  }
291
291
  catch (err) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-loopengine",
3
- "version": "0.1.58",
3
+ "version": "0.1.60",
4
4
  "description": "Scaffold a new loopengine agent project — a runnable HTTP server with one example agent, in your own repo.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,7 +1,8 @@
1
- ANTHROPIC_API_KEY=
2
- # Using OpenAI instead? Swap agents/example-agent/index.ts's createModelCall
3
- # for createOpenAIModelCall({ model: '...' }) and set this instead:
4
- # OPENAI_API_KEY=
1
+ # The example agent uses OpenAI (agents/example-agent/index.ts's `model`).
2
+ # Set the key here, or on the console's Models page once the server runs.
3
+ OPENAI_API_KEY=
4
+ # Switching the agent to another provider? Set its key instead, e.g.:
5
+ # ANTHROPIC_API_KEY=
5
6
 
6
7
  PORT=8787
7
8
 
@@ -6,7 +6,7 @@ An agent server built with [loopengine](https://www.npmjs.com/package/loopengine
6
6
 
7
7
  ```bash
8
8
  npm install
9
- cp .env.example .env # fill in ANTHROPIC_API_KEY
9
+ cp .env.example .env # fill in OPENAI_API_KEY (or set it later on the Models page)
10
10
  ```
11
11
 
12
12
  ## Run
@@ -153,6 +153,10 @@ import {
153
153
  searchPublicAbilities,
154
154
  fetchLatestAbilityVersion,
155
155
  describeModelProviders,
156
+ loopengineVersion,
157
+ setModelProviderKey,
158
+ clearModelProviderKey,
159
+ ModelKeyError,
156
160
  describeGateways,
157
161
  scaffoldAgent,
158
162
  AgentNameError,
@@ -2981,8 +2985,55 @@ const server = createServer(async (req, res) => {
2981
2985
  res.writeHead(200, { 'content-type': 'text/html' }).end(globalConfigPageHtml)
2982
2986
  return
2983
2987
  }
2988
+ // What the sidebar's sign-in line and the Settings section show. In
2989
+ // hosted mode the email and dashboard link come from the router's
2990
+ // signed identity, so they can't be set by the caller.
2991
+ if (req.method === 'GET' && pathname === '/config/settings') {
2992
+ const identity = trustedProxySecret ? verifyProxyIdentity(req.headers[TRUSTED_PROXY_IDENTITY_HEADER], trustedProxySecret) : undefined
2993
+ const signIn = identity
2994
+ ? {
2995
+ mode: 'hosted',
2996
+ email: identity.email ?? null,
2997
+ dashboardUrl: identity.dashboardUrl && /^https?:\/\//.test(identity.dashboardUrl) ? identity.dashboardUrl : null,
2998
+ }
2999
+ : { mode: adminAuth ? 'basic' : 'open' }
3000
+ res.writeHead(200, { 'content-type': 'application/json', 'cache-control': 'no-store' }).end(
3001
+ JSON.stringify({
3002
+ signIn,
3003
+ version: loopengineVersion(),
3004
+ environment: process.env.LOOPENGINE_ENV ?? 'production',
3005
+ publicUrl: process.env.LOOPENGINE_PUBLIC_URL ?? null,
3006
+ scheduler: process.env.LOOPENGINE_SCHEDULER === 'off' ? 'off' : 'on',
3007
+ }),
3008
+ )
3009
+ return
3010
+ }
2984
3011
  if (req.method === 'GET' && pathname === '/config/models') {
2985
- res.writeHead(200, { 'content-type': 'application/json' }).end(JSON.stringify(describeModelProviders(listAgents(), (name) => getEntry(name)?.config)))
3012
+ res.writeHead(200, { 'content-type': 'application/json' }).end(
3013
+ JSON.stringify({ ...describeModelProviders(listAgents(), (name) => getEntry(name)?.config), canSave: secretsProtected }),
3014
+ )
3015
+ return
3016
+ }
3017
+ // A provider's API key, from the Models page: write-only, never read
3018
+ // back. Same rule as the Environment tab: refused on a server with no
3019
+ // auth, since anyone who could reach it could replace the key.
3020
+ const modelKeyMatch = (req.method === 'PUT' || req.method === 'DELETE') && pathname.match(/^\/config\/models\/([a-z]+)\/key$/)
3021
+ if (modelKeyMatch) {
3022
+ if (!secretsProtected) {
3023
+ res.writeHead(403, { 'content-type': 'application/json' }).end(
3024
+ JSON.stringify({ error: 'Refusing to store an API key without LOOPENGINE_ADMIN_AUTH configured: set it first, or put the key in .env.' }),
3025
+ )
3026
+ return
3027
+ }
3028
+ try {
3029
+ if (req.method === 'PUT') await setModelProviderKey(modelKeyMatch[1], (await readJsonBody(req)).key)
3030
+ else await clearModelProviderKey(modelKeyMatch[1])
3031
+ } catch (err) {
3032
+ const status = err instanceof ModelKeyError ? 400 : 500
3033
+ res.writeHead(status, { 'content-type': 'application/json' }).end(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }))
3034
+ return
3035
+ }
3036
+ res.writeHead(200, { 'content-type': 'application/json' }).end(JSON.stringify({ ok: true }))
2986
3037
  return
2987
3038
  }
2988
3039
  if (req.method === 'GET' && pathname === '/config/gateways') {
@@ -12,18 +12,17 @@
12
12
  // resolves relative to loopengine's own installed location, not your
13
13
  // project, so it only works inside loopengine's own repo — see
14
14
  // tools/index.ts's own comment.
15
- import { createAnthropicModelCall, type AgentConfig, type ModelCall } from 'loopengine'
15
+ import type { AgentConfig } from 'loopengine'
16
16
  import { tools } from './tools/index.js'
17
17
 
18
18
  export const config: AgentConfig = {
19
19
  name: 'example-agent',
20
20
  systemPrompt: 'You are a helpful assistant.',
21
21
  tools,
22
- }
23
-
24
- // Reads ANTHROPIC_API_KEY from the env. Swap for
25
- // createOpenAIModelCall({ model: '...' }) to use OpenAI instead — see
26
- // node_modules/loopengine/README.md's "Wiring a real model" section.
27
- export function createModelCall(): ModelCall {
28
- return createAnthropicModelCall({ model: 'claude-sonnet-5' })
22
+ // Reads OPENAI_API_KEY: set it on the console's Models page, or in .env.
23
+ // For another provider, change `provider` and `model` (e.g.
24
+ // { provider: 'anthropic', model: 'claude-sonnet-5' } with
25
+ // ANTHROPIC_API_KEY). reasoningEffort 'none' lets this gpt-5.x model use
26
+ // function tools; see AgentModelConfig in loopengine's agent-config.
27
+ model: { provider: 'openai', model: 'gpt-5.6-luna', reasoningEffort: 'none' },
29
28
  }
@@ -9,7 +9,7 @@
9
9
  "cli": "tsx adapters/cli.ts"
10
10
  },
11
11
  "dependencies": {
12
- "loopengine": "^0.1.73",
12
+ "loopengine": "^0.1.75",
13
13
  "actauth": "^0.0.12"
14
14
  },
15
15
  "devDependencies": {