create-loopengine 0.1.14 → 0.1.16
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 +56 -6
- package/dist/cli.d.ts +26 -0
- package/dist/cli.js +81 -2
- package/package.json +1 -1
- package/template/adapters/http.ts +8 -1
- package/template/package.json +1 -1
package/README.md
CHANGED
|
@@ -61,8 +61,8 @@ own `discoverAgents`: it scans `agents/` and keys each entry by
|
|
|
61
61
|
one new file, not a rewrite and not a registry edit either. See
|
|
62
62
|
[loopengine's own README](https://github.com/evanyan22/loopengine) for the
|
|
63
63
|
full `AgentConfig` surface: permission rules, skills, context-budget
|
|
64
|
-
recovery, and how to wire a real model (Anthropic, OpenAI,
|
|
65
|
-
all
|
|
64
|
+
recovery, and how to wire a real model (Anthropic, OpenAI, DeepSeek,
|
|
65
|
+
Kimi, GLM, or Gemini — all six included).
|
|
66
66
|
|
|
67
67
|
## Add another agent
|
|
68
68
|
|
|
@@ -115,10 +115,60 @@ Each of the five template-owned files gets one of:
|
|
|
115
115
|
as any git merge conflict — resolve them by hand, then `npx tsc
|
|
116
116
|
--noEmit` to confirm it builds.
|
|
117
117
|
|
|
118
|
-
`upgrade`
|
|
119
|
-
`package.json`
|
|
120
|
-
|
|
121
|
-
|
|
118
|
+
`upgrade` also bumps whichever of `loopengine`/`actauth`/`skillgarden`
|
|
119
|
+
are already in your `package.json` to their latest published version
|
|
120
|
+
(never adds one you don't already depend on) and runs `npm install` for
|
|
121
|
+
you — a merged file that now calls a newer export is a silent, hard-to-
|
|
122
|
+
notice failure if the installed package hasn't actually caught up, so
|
|
123
|
+
this isn't left as a manual step. If the install itself fails (offline,
|
|
124
|
+
a registry hiccup), the version bump is still written to `package.json`;
|
|
125
|
+
just run `npm install` yourself to finish.
|
|
126
|
+
|
|
127
|
+
`upgrade` never touches `agents/`, `README.md`, `.env.example`, or any
|
|
128
|
+
dependency *other* than those three.
|
|
129
|
+
|
|
130
|
+
## Running in production
|
|
131
|
+
|
|
132
|
+
`npm run dev` (`tsx watch`) restarts on every file change — fine for
|
|
133
|
+
local iteration, wrong for a real server, since a restart drops every
|
|
134
|
+
in-flight request and any pending live approval/question (see
|
|
135
|
+
loopengine's own README on durable vs. live approvers if that matters to
|
|
136
|
+
you). Use [pm2](https://pm2.keymetrics.io/) to run the plain, non-watch
|
|
137
|
+
server instead, with restart-on-crash:
|
|
138
|
+
|
|
139
|
+
```bash
|
|
140
|
+
pm2 start npx --name my-agents --interpreter none -- loopengine serve
|
|
141
|
+
pm2 save # persist across reboots
|
|
142
|
+
pm2 startup # (one-time) launch pm2 itself on boot
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
`--interpreter none` matters — without it pm2 may try to run `npx`
|
|
146
|
+
through `node` directly, which fails since it's a shell-invokable binary,
|
|
147
|
+
not a bare `.js` file. `--` separates pm2's own flags from the command's
|
|
148
|
+
own args (`loopengine serve`).
|
|
149
|
+
|
|
150
|
+
Two gotchas worth knowing before they cost you a debugging session:
|
|
151
|
+
|
|
152
|
+
- **`npm` is not `npx`.** `pm2 start npm --name my-agents -- loopengine
|
|
153
|
+
serve` does *not* work — `npm <args>` treats its args as npm's own
|
|
154
|
+
subcommands, not a binary to run, so it fails with `Unknown command:
|
|
155
|
+
"loopengine"`. If you'd rather invoke via `npm`, add a `"start":
|
|
156
|
+
"loopengine serve"` script to `package.json` first, then `pm2 start npm
|
|
157
|
+
--name my-agents -- start` (npm special-cases `start` as a shorthand
|
|
158
|
+
for running that script, unlike arbitrary script names).
|
|
159
|
+
- **pm2 remembers a name's original `cwd`/script, not whatever you pass
|
|
160
|
+
next time.** Running `pm2 start ... --name my-agents` again after
|
|
161
|
+
moving/recreating the project directory just restarts the *already-
|
|
162
|
+
registered* process with its *original* path — your new files are
|
|
163
|
+
never even read. Run `pm2 delete my-agents` first if the project's
|
|
164
|
+
location changed, then start fresh.
|
|
165
|
+
|
|
166
|
+
`pm2 logs my-agents` tails output; `pm2 restart my-agents` after any
|
|
167
|
+
manual edit to `adapters/http.ts`/`adapters/cli.ts`/`agent-registry.ts`
|
|
168
|
+
(see "Upgrading" above) or an env var change — those don't take effect
|
|
169
|
+
until the process restarts. Adding a skill or tool through the Admin UI
|
|
170
|
+
does *not* need a restart — that's applied to the already-running process
|
|
171
|
+
directly.
|
|
122
172
|
|
|
123
173
|
## Status
|
|
124
174
|
|
package/dist/cli.d.ts
CHANGED
|
@@ -11,10 +11,29 @@ export interface UpgradeFileResult {
|
|
|
11
11
|
file: string;
|
|
12
12
|
status: UpgradeFileStatus;
|
|
13
13
|
}
|
|
14
|
+
export interface UpgradeDependencyResult {
|
|
15
|
+
name: string;
|
|
16
|
+
from: string;
|
|
17
|
+
to: string;
|
|
18
|
+
}
|
|
14
19
|
export interface UpgradeResult {
|
|
15
20
|
fromVersion: string;
|
|
16
21
|
toVersion: string;
|
|
17
22
|
files: UpgradeFileResult[];
|
|
23
|
+
/** Which of loopengine/actauth/skillgarden actually had their
|
|
24
|
+
* package.json range bumped — only ones already present as a
|
|
25
|
+
* dependency, never newly added (see bumpDependencies's own doc
|
|
26
|
+
* comment). Empty if the project has no package.json, or every present
|
|
27
|
+
* one was already at the latest range. */
|
|
28
|
+
dependencies: UpgradeDependencyResult[];
|
|
29
|
+
/** Only attempted when `dependencies` is non-empty — running `npm
|
|
30
|
+
* install` for a project with nothing to bump would be pointless. */
|
|
31
|
+
npmInstall?: {
|
|
32
|
+
ok: true;
|
|
33
|
+
} | {
|
|
34
|
+
ok: false;
|
|
35
|
+
error: string;
|
|
36
|
+
};
|
|
18
37
|
}
|
|
19
38
|
export interface UpgradeOptions {
|
|
20
39
|
/** Defaults to process.cwd(). */
|
|
@@ -31,6 +50,13 @@ export interface UpgradeOptions {
|
|
|
31
50
|
* (a real `npm pack`). Test-only seam to avoid hitting the real
|
|
32
51
|
* registry for a fixture "old" template. */
|
|
33
52
|
resolveBaseTemplateDir?: (version: string) => string;
|
|
53
|
+
/** Resolves a package's current latest version — defaults to a real
|
|
54
|
+
* `npm view <pkg> version`. Test-only seam to avoid hitting the real
|
|
55
|
+
* registry. */
|
|
56
|
+
resolveLatestVersion?: (pkg: string) => string;
|
|
57
|
+
/** Runs `npm install` in projectDir — defaults to a real one. Test-only
|
|
58
|
+
* seam to avoid actually installing anything during tests. */
|
|
59
|
+
runNpmInstall?: (projectDir: string) => void;
|
|
34
60
|
}
|
|
35
61
|
export declare function upgradeProject(options?: UpgradeOptions): UpgradeResult;
|
|
36
62
|
export declare function main(argv?: string[]): void;
|
package/dist/cli.js
CHANGED
|
@@ -163,8 +163,76 @@ export function upgradeProject(options = {}) {
|
|
|
163
163
|
const resolveBase = options.resolveBaseTemplateDir ?? fetchPublishedTemplateDir;
|
|
164
164
|
const baseTemplateDir = resolveBase(fromVersion);
|
|
165
165
|
const files = UPGRADE_CANDIDATE_FILES.map((candidate) => upgradeFile(candidate, baseTemplateDir, currentTemplateDir, projectDir));
|
|
166
|
+
const resolveLatestVersion = options.resolveLatestVersion ?? resolveLatestVersionFromNpm;
|
|
167
|
+
const dependencies = bumpDependencies(projectDir, resolveLatestVersion);
|
|
168
|
+
let npmInstall;
|
|
169
|
+
if (dependencies.length > 0) {
|
|
170
|
+
const install = options.runNpmInstall ?? runRealNpmInstall;
|
|
171
|
+
try {
|
|
172
|
+
install(projectDir);
|
|
173
|
+
npmInstall = { ok: true };
|
|
174
|
+
}
|
|
175
|
+
catch (err) {
|
|
176
|
+
// Non-fatal — the file merge and the package.json bump already
|
|
177
|
+
// succeeded and are on disk either way; a failed install (offline,
|
|
178
|
+
// a registry hiccup) just means the operator runs `npm install`
|
|
179
|
+
// themselves, the same single command this was trying to save them
|
|
180
|
+
// from typing, not a reason to unwind everything already done.
|
|
181
|
+
npmInstall = { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
182
|
+
}
|
|
183
|
+
}
|
|
166
184
|
writeFileSync(provenancePath, JSON.stringify({ version: toVersion }, null, 2) + '\n');
|
|
167
|
-
return { fromVersion, toVersion, files };
|
|
185
|
+
return { fromVersion, toVersion, files, dependencies, npmInstall };
|
|
186
|
+
}
|
|
187
|
+
function resolveLatestVersionFromNpm(pkg) {
|
|
188
|
+
return execFileSync('npm', ['view', pkg, 'version'], { stdio: 'pipe' }).toString().trim();
|
|
189
|
+
}
|
|
190
|
+
function runRealNpmInstall(projectDir) {
|
|
191
|
+
execFileSync('npm', ['install'], { cwd: projectDir, stdio: 'pipe' });
|
|
192
|
+
}
|
|
193
|
+
// Only loopengine/actauth/skillgarden — the three packages a scaffolded
|
|
194
|
+
// project's own adapters/http.ts can import directly (see this repo's own
|
|
195
|
+
// README on why: template code that imports a package must declare it as
|
|
196
|
+
// its own direct dependency, not rely on loopengine's transitive one) —
|
|
197
|
+
// and only ones already present in the project's own package.json.
|
|
198
|
+
// Deliberately never *adds* a dependency a project doesn't already have:
|
|
199
|
+
// this command upgrades what's there, it doesn't decide what a project
|
|
200
|
+
// should depend on.
|
|
201
|
+
const UPGRADABLE_DEPENDENCIES = ['loopengine', 'actauth', 'skillgarden'];
|
|
202
|
+
/** Bumps package.json's dependency ranges for whichever of
|
|
203
|
+
* loopengine/actauth/skillgarden are already present, to `^<latest>` —
|
|
204
|
+
* run automatically as part of `upgrade` (not left as a manual follow-up
|
|
205
|
+
* step) because a project whose merged template code now calls a newer
|
|
206
|
+
* export, but whose installed package is still old, fails silently: no
|
|
207
|
+
* conflict marker, no build error, just a feature that's quietly not
|
|
208
|
+
* there yet (confirmed live — this exact gap is what prompted adding this
|
|
209
|
+
* function in the first place, see this repo's own git history). Returns
|
|
210
|
+
* [] untouched if the project has no package.json at all, same
|
|
211
|
+
* "can't merge what isn't there" reasoning upgradeFile's own missing-file
|
|
212
|
+
* check uses. */
|
|
213
|
+
function bumpDependencies(projectDir, resolveLatestVersion) {
|
|
214
|
+
const pkgPath = join(projectDir, 'package.json');
|
|
215
|
+
if (!existsSync(pkgPath))
|
|
216
|
+
return [];
|
|
217
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
218
|
+
const deps = pkg.dependencies;
|
|
219
|
+
if (!deps)
|
|
220
|
+
return [];
|
|
221
|
+
const changes = [];
|
|
222
|
+
for (const name of UPGRADABLE_DEPENDENCIES) {
|
|
223
|
+
const currentRange = deps[name];
|
|
224
|
+
if (currentRange === undefined)
|
|
225
|
+
continue;
|
|
226
|
+
const latest = resolveLatestVersion(name);
|
|
227
|
+
const newRange = `^${latest}`;
|
|
228
|
+
if (currentRange === newRange)
|
|
229
|
+
continue;
|
|
230
|
+
deps[name] = newRange;
|
|
231
|
+
changes.push({ name, from: currentRange, to: newRange });
|
|
232
|
+
}
|
|
233
|
+
if (changes.length > 0)
|
|
234
|
+
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
235
|
+
return changes;
|
|
168
236
|
}
|
|
169
237
|
function parseUpgradeArgs(argv) {
|
|
170
238
|
let from;
|
|
@@ -187,7 +255,18 @@ function printUpgradeSummary(result) {
|
|
|
187
255
|
console.log(`${conflicted.length} file(s) have <<<<<<< conflict markers to resolve by hand before this will build.`);
|
|
188
256
|
}
|
|
189
257
|
else {
|
|
190
|
-
console.log('No conflicts.
|
|
258
|
+
console.log('No conflicts.');
|
|
259
|
+
}
|
|
260
|
+
if (result.dependencies.length > 0) {
|
|
261
|
+
console.log();
|
|
262
|
+
for (const dep of result.dependencies)
|
|
263
|
+
console.log(` ${dep.name}: ${dep.from} -> ${dep.to}`);
|
|
264
|
+
if (result.npmInstall?.ok) {
|
|
265
|
+
console.log('npm install completed.');
|
|
266
|
+
}
|
|
267
|
+
else if (result.npmInstall) {
|
|
268
|
+
console.log(`npm install failed (${result.npmInstall.error}) — run it yourself to finish picking up the new versions.`);
|
|
269
|
+
}
|
|
191
270
|
}
|
|
192
271
|
}
|
|
193
272
|
function parseArgs(argv) {
|
package/package.json
CHANGED
|
@@ -645,7 +645,14 @@ function parseAgentTemplateOptions(body: Record<string, unknown>): { ok: true; v
|
|
|
645
645
|
return { ok: false, error: 'model must be an object' }
|
|
646
646
|
}
|
|
647
647
|
const provider = (body.model as Record<string, unknown>).provider
|
|
648
|
-
if (
|
|
648
|
+
if (
|
|
649
|
+
provider !== 'anthropic' &&
|
|
650
|
+
provider !== 'openai' &&
|
|
651
|
+
provider !== 'deepseek' &&
|
|
652
|
+
provider !== 'kimi' &&
|
|
653
|
+
provider !== 'glm' &&
|
|
654
|
+
provider !== 'gemini'
|
|
655
|
+
) {
|
|
649
656
|
return { ok: false, error: `unsupported model provider '${String(provider)}'` }
|
|
650
657
|
}
|
|
651
658
|
const modelName = (body.model as Record<string, unknown>).model
|