CommunityProgramación y desarrollogithub.com

studiolxd/skills

AI agent skills for Studio LXD libraries — Claude Code, Cursor, Codex/ChatGPT and generic agents (@studiolxd/scorm, @studiolxd/xapi)

¿Qué es skills?

skills is a Claude Code agent skill that aI agent skills for Studio LXD libraries — Claude Code, Cursor, Codex/ChatGPT and generic agents (@studiolxd/scorm, @studiolxd/xapi).

Compatible conClaude CodeCodex CLICursor
npx skills add studiolxd/skills

Installed? Explore more Programación y desarrollo skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

Preguntar en tu IA favorita

Abre un nuevo chat con esta habilidad de agente ya precargada.

Documentación

Using @studiolxd/xapi

@studiolxd/xapi is a headless xAPI 1.0.3/2.0 client. A framework-agnostic core (createXapiClient) plus thin adapters, talking to any standards-compliant LRS. Built-in in-memory mock LRS runs without a real LRS.

Pick the entry point

  • Vanilla / any framework: import { createXapiClient, buildStatement, VERBS } from '@studiolxd/xapi'
  • React: import { XapiProvider, useXapiClient, useXapiStatus } from '@studiolxd/xapi/react'
  • Vue: import { useXapiClient } from '@studiolxd/xapi/vue'
  • Angular (>=17): import { provideXapi, XAPI } from '@studiolxd/xapi/angular'
  • Svelte (>=4): import { createXapiStore } from '@studiolxd/xapi/svelte'
  • Implementing an LRS (server side): import { … } from '@studiolxd/xapi/server'
  • CDN <script>: global window.Xapi

The golden rules

  1. Always check .ok before .value. Every network method returns Promise<Result<T, XapiError>> — the API never throws. (Unlike SCORM, everything here is async: HTTP.)
  2. Build statements with client.buildStatement() (or the standalone buildStatement): verb/object accept a plain IRI string, id (UUID) and timestamp (ISO 8601) are auto-generated, and the client's defaults (actor, registration, context) are merged in. An actor needs exactly one identifier: mbox | mbox_sha1sum | openid | account.
  3. Use the mock LRS for dev/tests: createXapiClient({ endpoint: 'https://mock.lrs/xapi', fetch: createMemoryLrs().fetch }).
  4. Documents ≠ statements. State/Activity Profile/Agent Profile are mutable key-value documents (session state, settings); statements are an immutable historical log. getState/get*Profile resolve ok(null) on 404 — missing is not an error.
  5. Voiding does not delete. voidStatement(targetId) sends a new statement (VERBS.voided + StatementRef); the target disappears from normal queries but stays retrievable via getVoidedStatement(id).
  6. Version is an option, not a fork: version: '1.0.3' | '2.0' (default '1.0.3'); the client adapts payloads and headers per version.

Canonical vanilla example

import { createXapiClient, VERBS } from '@studiolxd/xapi';

const client = createXapiClient({
  endpoint: 'https://lrs.example.com/xapi',
  auth: { username: 'key', password: 'secret' }, // or { token } or { header }
  defaults: { actor: { mbox: 'mailto:[email protected]' } },
});

const sent = await client.sendStatement(
  client.buildStatement({
    verb: VERBS.completed,                    // or a raw IRI string
    object: 'https://example.com/course/1',   // IRI shorthand → Activity
    result: { score: { scaled: 0.9 }, success: true },
  }),
);
if (sent.ok) console.log('statement id:', sent.value);
else console.error(sent.error.kind, sent.error.status, sent.error.message);

// Session state survives page reloads via the State document API:
await client.setState('https://example.com/course/1', 'bookmark', { page: 4 });
const state = await client.getState('https://example.com/course/1', 'bookmark');
if (state.ok && state.value) console.log(state.value.content); // { page: 4 }

Launch (content started by an LMS)

TinCan/Rustici-style launches pass endpoint, auth, actor, registration and activity_id as query params. Don't parse them by hand:

import { createXapiClientFromLaunch } from '@studiolxd/xapi';
const launched = createXapiClientFromLaunch(); // reads window.location.href, SSR-safe
if (launched.ok) {
  const client = launched.value; // actor/registration already set as defaults
}

Common gotchas (these cause real bugs)

  • IRIs (verb.id, Activity object.id) must have a scheme and no spaces — validateStatement rejects them with kind: 'validation' before any network call.
  • mbox must be mailto:.... mbox_sha1sum is a SHA-1 hex digest without mailto:. Never send both — exactly one identifier per actor.
  • Resending the same statement id with different content → 409 conflict. Identical content is idempotent (no error, no duplicate).
  • getStatements() paginates: follow result.value.more with getMoreStatements(more) until it is null.
  • concurrency: 'auto' (default) manages document ETags and retries a 412 once; some strict 1.0.3 LRSs reject conditional headers — use concurrency: 'off' there.
  • ./server helpers use Web APIs only (Request/Response/crypto.subtle) — they run in Next.js Route Handlers/edge/Node >=18, but not with node:http objects.
  • SSR (Next.js/Remix): parseXapiLaunch() without an explicit URL returns err(kind: 'usage') on the server instead of throwing — pass the URL explicitly or call it client-side only.
  • client.destroy() when done (adapters do it for you on unmount/dispose).

XapiError fields

kind ('network'|'http'|'validation'|'timeout'|'version'|'usage'), operation, endpoint, status (HTTP code or null), responseBody (truncated LRS error body), issues (validation only: { path, rule, message }[]), exception.

Skills relacionados