{
  "file": "F25-skills.md",
  "bullet": "TypeScript client (`F25.ts-client`).",
  "ids": [
    "F25.ts-client"
  ],
  "line": 743,
  "script": "test -f clients/typescript/dist/clients.js || (cd clients/typescript && npm ci && npm run build)\nG3=$(control-agent-server fixture git-source --name qa-f25-ts --print-path)\nH3=$(git -C \"$G3\" rev-parse HEAD)\ncat > \"$F/qa_f25_ts.mjs\" <<'JS'\nimport assert from 'node:assert/strict';\nimport { resolve } from 'node:path';\nimport { pathToFileURL } from 'node:url';\n\nconst { SkillsClient } = await import(pathToFileURL(resolve('clients/typescript/dist/clients.js')).href);\nconst [src, head] = process.argv.slice(2);\nconst host = process.env.AGENT_SERVER_URL;\nconst client = new SkillsClient({ host, apiKey: process.env.SESSION_API_KEY });\nconst name = 'qa-f25-ts-skill';\nconst userTier = { load_public: false, load_user: true, load_project: false, load_org: false };\nconst inCatalog = async () => (await client.getSkills(userTier)).skills.some((s) => s.name === name);\n\nconst info = await client.installSkill({ source: `file://${src}`, repo_path: `skills/${name}` });\nassert.equal(info.name, name);\nassert.equal(info.enabled, true);\nassert.equal(info.resolved_ref, head);\nassert.ok((await client.listInstalledSkills()).skills.some((s) => s.name === name));\nassert.equal((await client.getInstalledSkill(name)).resolved_ref, head);\nassert.ok(await inCatalog());\nassert.deepEqual(await client.toggleSkill(name, false), { name, enabled: false });\nassert.equal((await client.getInstalledSkill(name)).enabled, false);\nassert.equal(await inCatalog(), false);\nassert.deepEqual(await client.toggleSkill(name, true), { name, enabled: true });\nassert.ok(await inCatalog());\nconst refreshed = await client.refreshSkill(name);\nassert.equal(refreshed.message, `Skill '${name}' updated`);\nassert.equal(refreshed.skill.resolved_ref, head);\nassert.equal(refreshed.skill.enabled, true);\nassert.equal((await client.syncSkills()).status, 'success');\nconst pub = (await client.getMarketplace()).skills.find((s) => s.name === 'qa-f25-pub');\nassert.equal(pub.installed, true);\nassert.deepEqual(await client.uninstallSkill(name), { message: `Skill '${name}' uninstalled` });\nawait assert.rejects(client.getInstalledSkill(name), (e) => e.status === 404);\nawait assert.rejects(new SkillsClient({ host }).listInstalledSkills(), (e) => e.status === 401);\nconsole.log('QA_F25_TS_OK');\nJS\ncontrol-agent-server exec --timeout 180 --expect-output QA_F25_TS_OK --save F25.ts-client/program -- node \"$F/qa_f25_ts.mjs\" \"$G3\" \"$H3\"\ncontrol-agent-server api GET /api/skills/installed --expect 200 --check skills not-contains qa-f25-ts-skill\ntest ! -e \"$I/qa-f25-ts-skill\"\ncontrol-agent-server api GET /api/skills/marketplace --expect 200 --check skills.0.name eq qa-f25-pub --check skills.0.installed eq true\n",
  "exit_code": 0,
  "elapsed_ms": 6338,
  "failed_line": null,
  "failed_command": null,
  "stdout": "{\n  \"ok\": true,\n  \"exit_code\": 0,\n  \"elapsed_ms\": 1819,\n  \"stdout_tail\": \"QA_F25_TS_OK\\n\",\n  \"stderr_tail\": \"\",\n  \"saved\": \"/workspace/verification-artifacts/runs/20261013-025747-be38-map-f25/evidence/F25.ts-client/program.json\"\n}\n{\n  \"ok\": true,\n  \"status\": 200,\n  \"request\": {\n    \"method\": \"GET\",\n    \"path\": \"/api/skills/installed\",\n    \"query\": [],\n    \"auth\": \"header\",\n    \"body\": null,\n    \"files\": []\n  },\n  \"response\": {\n    \"status\": 200,\n    \"headers\": {\n      \"content-length\": \"1872\",\n      \"content-type\": \"application/json\"\n    },\n    \"body\": {\n      \"skills\": [\n        {\n          \"name\": \"qa-f25-skill\",\n          \"version\": \"1.0.0\",\n          \"description\": \"QA fixture skill v2\",\n          \"enabled\": true,\n          \"source\": \"/workspace/verification-artifacts/runs/20261013-025747-be38-map-f25/fixtures/skills/qa-f25-skill\",\n          \"resolved_ref\": null,\n          \"repo_path\": null,\n          \"installed_at\": \"2026-10-13T02:59:30.760813+00:00\",\n          \"install_path\": \"/workspace/verification-artifacts/runs/20261013-025747-be38-map-f25/home/.openhands/skills/installed/qa-f25-skill\"\n        },\n        {\n          \"name\": \"qa-f25-src-skill\",\n          \"version\": \"1.0.0\",\n          \"description\": \"QA fixture skill qa-f25-src-skill; mention QA_SKILL_MARKER when asked about qa-f25-src-skill.\",\n          \"enabled\": false,\n          \"source\": \"file:///workspace/verification-artifacts/runs/20261013-025747-be38-map-f25/fixtures/git/qa-f25-src\",\n          \"resolved_ref\": \"85439e5d678379453aa37a3e0a4f1aec0b0c4e82\",\n          \"repo_path\": \"skills/qa-f25-src-skill\",\n          \"installed_at\": \"2026-10-13T02:59:38.682828+00:00\",\n          \"install_path\": \"/workspace/verification-artifacts/runs/20261013-025747-be38-map-f25/home/.openhands/skills/installed/qa-f25-src-skill\"\n        },\n        {\n          \"name\": \"qa-f25-gone\",\n          \"version\": \"1.0.0\",\n          \"description\": \"QA source that disappears\",\n          \"enabled\": true,\n          \"source\": \"/workspace/verification-artifacts/runs/20261013-025747-be38-map-f25/fixtures/qa-f25/tmp/qa-f25-gone\",\n          \"resolved_ref\": null,\n          \"repo_path\": null,\n          \"installed_at\": \"2026-10-13T02:59:39.266132+00:00\",\n          \"install_path\": \"/workspace/verification-artifacts/runs/20261013-025747-be38-map-f25/home/.openhands/skills/installed/qa-f25-gone\"\n        },\n        {\n          \"name\": \"qa-f25-pub\",\n          \"version\": \"1.0.0\",\n          \"description\": \"QA public stand-in skill\",\n          \"enabled\": true,\n          \"source\": \"/workspace/verification-artifacts/runs/20261013-025747-be38-map-f25/home/.openhands/cache/skills/public-skills/skills/qa-f25-pub\",\n          \"resolved_ref\": null,\n          \"repo_path\": null,\n          \"installed_at\": \"2026-10-13T03:03:11.215576+00:00\",\n          \"install_path\": \"/workspace/verification-artifacts/runs/20261013-025747-be38-map-f25/home/.openhands/skills/installed/qa-f25-pub\"\n        }\n      ]\n    },\n    \"truncated\": false\n  },\n  \"elapsed_ms\": 92,\n  \"checks\": [\n    {\n      \"check\": \"skills not-contains qa-f25-ts-skill\",\n      \"ok\": true,\n      \"actual\": \"[{\\\"name\\\": \\\"qa-f25-skill\\\", \\\"version\\\": \\\"1.0.0\\\", \\\"description\\\": \\\"QA fixture skill v2\\\", \\\"enabled\\\": true, \\\"source\\\": \\\"/workspace/verification-artifacts/runs/20261013-025747-be38-map-f25/fixtures/skills/qa-f\\u2026\"\n    }\n  ]\n}\n{\n  \"ok\": true,\n  \"status\": 200,\n  \"request\": {\n    \"method\": \"GET\",\n    \"path\": \"/api/skills/marketplace\",\n    \"query\": [],\n    \"auth\": \"header\",\n    \"body\": null,\n    \"files\": []\n  },\n  \"response\": {\n    \"status\": 200,\n    \"headers\": {\n      \"content-length\": \"232\",\n      \"content-type\": \"application/json\"\n    },\n    \"body\": {\n      \"skills\": [\n        {\n          \"name\": \"qa-f25-pub\",\n          \"description\": \"QA public stand-in skill\",\n          \"source\": \"/workspace/verification-artifacts/runs/20261013-025747-be38-map-f25/home/.openhands/cache/skills/public-skills/skills/qa-f25-pub\",\n          \"installed\": true\n        }\n      ]\n    },\n    \"truncated\": false\n  },\n  \"elapsed_ms\": 126,\n  \"checks\": [\n    {\n      \"check\": \"skills.0.name eq qa-f25-pub\",\n      \"ok\": true,\n      \"actual\": \"qa-f25-pub\"\n    },\n    {\n      \"check\": \"skills.0.installed eq true\",\n      \"ok\": true,\n      \"actual\": \"true\"\n    }\n  ]\n}\n",
  "stderr": ""
}
