create-loopengine 0.1.35 → 0.1.36

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-loopengine",
3
- "version": "0.1.35",
3
+ "version": "0.1.36",
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",
@@ -96,11 +96,13 @@ import {
96
96
  ActauthRuleExistsError,
97
97
  ActauthRuleNotFoundError,
98
98
  installAbility,
99
+ upgradeAbility,
99
100
  listInstalledAbilities,
100
101
  AbilityAlreadyInstalledError,
101
102
  AbilityCollisionError,
102
103
  AbilityManifestError,
103
104
  AbilityVersionError,
105
+ AbilityNotInstalledError,
104
106
  searchPublicAbilities,
105
107
  describeModelProviders,
106
108
  describeGateways,
@@ -1065,6 +1067,38 @@ async function handleAbilityInstallPost(req: IncomingMessage, res: ServerRespons
1065
1067
  }
1066
1068
  }
1067
1069
 
1070
+ // Upgrades an already-installed ability to whatever it currently
1071
+ // resolves to (or, optionally, a specific spec) — same admin-auth
1072
+ // posture as handleAbilityInstallPost above, since this runs arbitrary
1073
+ // third-party code inside the agent's project the next time it's
1074
+ // loaded, same as install does.
1075
+ async function handleAbilityUpgradePost(req: IncomingMessage, res: ServerResponse, agentName: string, abilityName: string): Promise<void> {
1076
+ if (!adminAuth) {
1077
+ res.writeHead(403, { 'content-type': 'application/json' }).end(
1078
+ JSON.stringify({ error: 'Refusing to upgrade an ability without LOOPENGINE_ADMIN_AUTH configured — set it before upgrading abilities through this route.' }),
1079
+ )
1080
+ return
1081
+ }
1082
+ if (!getEntry(agentName)) {
1083
+ res.writeHead(404, { 'content-type': 'application/json' }).end(JSON.stringify({ error: `unknown agent '${agentName}'` }))
1084
+ return
1085
+ }
1086
+ const body = await readJsonBody(req)
1087
+ const spec = typeof body.spec === 'string' && body.spec ? body.spec : undefined
1088
+ try {
1089
+ const result = await upgradeAbility(agentName, abilityName, spec ? { spec } : {})
1090
+ res.writeHead(200, { 'content-type': 'application/json' }).end(JSON.stringify(result))
1091
+ } catch (err) {
1092
+ const status =
1093
+ err instanceof AbilityNotInstalledError
1094
+ ? 404
1095
+ : err instanceof AbilityManifestError || err instanceof AbilityVersionError
1096
+ ? 422
1097
+ : 500
1098
+ res.writeHead(status, { 'content-type': 'application/json' }).end(JSON.stringify({ error: err instanceof Error ? err.message : String(err) }))
1099
+ }
1100
+ }
1101
+
1068
1102
  // Backs the Actauth tab's rule editor — parses a rule body shared by both
1069
1103
  // the add (POST) and update (PUT, minus `name`) routes below.
1070
1104
  function parseActauthRuleBody(body: Record<string, unknown>, requireName: boolean): { ok: true; value: { name: string; scope: string; tool: string; decision: Decision } } | { ok: false; error: string } {
@@ -2243,6 +2277,11 @@ const server = createServer(async (req, res) => {
2243
2277
  await handleAbilitiesSearchGet(res, decodeURIComponent(abilitiesSearchMatch[1]), query)
2244
2278
  return
2245
2279
  }
2280
+ const abilityUpgradeMatch = pathname.match(/^\/agents\/([^/]+)\/abilities\/([^/]+)\/upgrade$/)
2281
+ if (abilityUpgradeMatch && req.method === 'POST') {
2282
+ await handleAbilityUpgradePost(req, res, decodeURIComponent(abilityUpgradeMatch[1]), decodeURIComponent(abilityUpgradeMatch[2]))
2283
+ return
2284
+ }
2246
2285
  const abilitiesMatch = pathname.match(/^\/agents\/([^/]+)\/abilities$/)
2247
2286
  if (abilitiesMatch && req.method === 'GET') {
2248
2287
  handleAbilitiesGet(res, decodeURIComponent(abilitiesMatch[1]))