Skip to content

fix(update): parse versions endpoint as keyed object, not array - #17

Open
dribblejerp-KlunkDunker wants to merge 1 commit into
agentskill-sh:mainfrom
dribblejerp-KlunkDunker:fix/ags-update-versions-object
Open

fix(update): parse versions endpoint as keyed object, not array#17
dribblejerp-KlunkDunker wants to merge 1 commit into
agentskill-sh:mainfrom
dribblejerp-KlunkDunker:fix/ags-update-versions-object

Conversation

@dribblejerp-KlunkDunker

Copy link
Copy Markdown

Summary

ags update crashes with TypeError: remote.map is not a function as soon as any skill is installed and tracked in the lock file. updateCommand types the GET /agent/skills/version response as an array and calls .map() on it, but the API returns an object keyed by slug ({"versions": {slug: {contentSha}}}), so every ags update (interactive and --json) is a hard crash.

Reproduction

ags install some-skill
ags update

Expected: "All N skills up to date" (or an update prompt).
Actual:

✖ Failed to check versions
TypeError: remote.map is not a function
    at updateCommand (file:///.../dist/commands/update.js)

Root cause

let remote: VersionEntry[]
remote = await apiFetch<VersionEntry[]>(`/agent/skills/version?slugs=...`)
const remoteMap = new Map(remote.map((r) => [r.slug, r.contentSha]))

The endpoint returns a keyed object:

$ curl -s 'https://agentskill.sh/api/agent/skills/version?slugs=agentskill-sh%2Flearn'
{ "versions": { "agentskill-sh/learn": { "contentSha": "e6b59a3" } } }

remote is { versions: {...} } — an object, which has no .map(). The VersionEntry[] type hides the mismatch from the compiler.

Fix

Parse the keyed object instead of assuming an array; the resolved map is unchanged.

Verification

  • tsc --noEmit passes with the change.
  • The identical logic change applied to the installed CLI (2.0.2) verified end-to-end against the live API: ags update --json{"updated":[],"upToDate":2} with the resolved map matching both lock-tracked skills' contentShas exactly.

Suggested follow-ups for maintainers

  • Audit the same array-vs-object assumption in other commands consuming apiFetch results (e.g. list, find).
  • Consider having apiFetch validate the response shape so a mismatch throws a descriptive error instead of a raw TypeError deep in a command.

GET /agent/skills/version returns an object keyed by slug ({"versions":
{slug: {contentSha}}}), but updateCommand typed it as VersionEntry[] and
called .map() on it, so every `ags update` crashed with
"remote.map is not a function" as soon as any skill was installed.
Parse the keyed object instead; the resolved map is unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant