mirror of
https://github.com/iflytek/skillhub.git
synced 2026-10-16 04:28:39 +00:00
Implement complete CLI tool for SkillHub with 12 commands, 7 backend API endpoints, and comprehensive documentation. CLI Commands: - help, version: Basic information - login, logout, whoami: Authentication management - search: Discover published skills - install: Install skills to agent directories (14 Tier 1 agents supported) - list, remove, doctor: Local skill management - publish: Publish skill packages - update: Self-update mechanism Backend API: - Add /api/cli/v1 endpoints for auth, search, resolve, download, delete, publish - Implement CliAuthController and CliSkillController - Add security policies for CLI routes - Full test coverage (19 backend tests) CLI Implementation: - TypeScript with strict mode, Bun runtime - Pure JS zip handling (fflate) for cross-platform compatibility - 15 agent profiles (14 Tier 1 + generic fallback) - Secure token storage (0600 permissions) - Path safety validation for remove operations - Comprehensive error handling (404/403/network distinction) - 41 unit and integration tests Documentation: - CLI user guide (Chinese and English) - README updates with quick start - GitHub Actions workflow for cross-platform CI Quality: - lint: 0 errors - typecheck: pass - test: 41/41 pass - build: 0.30 MB (target=node for npm/npx compatibility)
59 lines
2 KiB
TypeScript
59 lines
2 KiB
TypeScript
import { afterEach, describe, expect, test } from 'bun:test'
|
|
import { createTempHome } from '../helpers/temp-env'
|
|
import { startFakeRegistry } from '../helpers/fake-registry'
|
|
import { runCli } from '../helpers/run-cli'
|
|
|
|
let registry: { url: string; stop: () => void } | undefined
|
|
|
|
afterEach(() => {
|
|
registry?.stop()
|
|
registry = undefined
|
|
})
|
|
|
|
describe('auth commands', () => {
|
|
test('login stores registry and token only after whoami succeeds', async () => {
|
|
const env = await createTempHome()
|
|
registry = await startFakeRegistry({ token: 'sk_ok', user: { handle: 'u1', displayName: 'User One' } })
|
|
|
|
const result = await runCli(['login', '--registry', registry.url, '--token', 'sk_ok'], {
|
|
HOME: env.home,
|
|
USERPROFILE: env.home
|
|
})
|
|
|
|
expect(result.exitCode).toBe(0)
|
|
expect(result.stdout).toContain('Logged in')
|
|
expect(await Bun.file(`${env.home}/.skillhub/config.json`).json()).toMatchObject({ registry: registry.url })
|
|
expect(await Bun.file(`${env.home}/.skillhub/credentials.json`).json()).toMatchObject({ tokens: { [registry.url]: 'sk_ok' } })
|
|
})
|
|
|
|
test('login fails with invalid token', async () => {
|
|
const env = await createTempHome()
|
|
registry = await startFakeRegistry({ token: 'sk_ok' })
|
|
|
|
const result = await runCli(['login', '--registry', registry.url, '--token', 'sk_bad'], {
|
|
HOME: env.home,
|
|
USERPROFILE: env.home
|
|
})
|
|
|
|
expect(result.exitCode).toBe(2)
|
|
expect(result.stderr).toContain('authentication failed')
|
|
})
|
|
|
|
test('logout removes token', async () => {
|
|
const env = await createTempHome()
|
|
registry = await startFakeRegistry({ token: 'sk_ok', user: { handle: 'u1', displayName: 'User One' } })
|
|
|
|
await runCli(['login', '--registry', registry.url, '--token', 'sk_ok'], {
|
|
HOME: env.home,
|
|
USERPROFILE: env.home
|
|
})
|
|
|
|
const result = await runCli(['logout', '--registry', registry.url], {
|
|
HOME: env.home,
|
|
USERPROFILE: env.home
|
|
})
|
|
|
|
expect(result.exitCode).toBe(0)
|
|
expect(result.stdout).toContain('Logged out')
|
|
})
|
|
})
|