mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-04 14:39:42 +02:00
feat(cli-registry): add cliManagementEnabled flag and GET /api/clis
Phases 1-2 of docs/cli-enable-disable-plan.md ("PR C" from the #343
review): a synced, default-OFF master flag gating the upcoming CLI
management surface, plus a read-only GET /api/clis endpoint listing
every registry entry (stock + custom, enabled or not) for the
Settings UI. Non-admins in multi-user mode see an empty list rather
than a 403. Write endpoints, auto-install, custom entry CRUD and the
Settings UI list itself land in later phases.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GuHtuPiHXdykq9T6rKQJ9n
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
9466acfc1a
commit
da07b38c42
@@ -314,7 +314,6 @@ describe('declared-for-later fields', () => {
|
||||
* list — and wiring one up should make its line here fail, which is the good direction.
|
||||
*/
|
||||
const DECLARED_FOR_LATER = [
|
||||
'shortBadge',
|
||||
'accent',
|
||||
'capabilities.echo',
|
||||
'capabilities.wheelForward',
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* @fileoverview Route tests for GET /api/clis (docs/cli-enable-disable-plan.md,
|
||||
* Phase 2). Mirrors the admin-gating test shape used for other admin/settings
|
||||
* surfaces (see test/routes/search-routes.test.ts's multi-user block).
|
||||
*
|
||||
* Port: N/A (app.inject(), no live server).
|
||||
*/
|
||||
import { afterEach, describe, expect, it } from 'vitest';
|
||||
import { createRouteTestHarness } from './_route-test-utils.js';
|
||||
import { registerCliRegistryRoutes, type CliListItem } from '../../src/web/routes/cli-registry-routes.js';
|
||||
|
||||
describe('GET /api/clis', () => {
|
||||
afterEach(() => {
|
||||
delete process.env.CODEMAN_MULTIUSER;
|
||||
});
|
||||
|
||||
it('single-user mode: returns every registry entry, disabled stock CLIs included', async () => {
|
||||
const { app } = await createRouteTestHarness(registerCliRegistryRoutes);
|
||||
const res = await app.inject({ method: 'GET', url: '/api/clis' });
|
||||
expect(res.statusCode).toBe(200);
|
||||
const body = res.json() as { success: true; data: CliListItem[] };
|
||||
expect(body.success).toBe(true);
|
||||
const ids = body.data.map((c) => c.id);
|
||||
expect(ids).toContain('claude');
|
||||
expect(ids).toContain('shell');
|
||||
expect(ids.length).toBeGreaterThanOrEqual(9);
|
||||
});
|
||||
|
||||
it('every item has the expected shape and excludes spawn-time fields', async () => {
|
||||
const { app } = await createRouteTestHarness(registerCliRegistryRoutes);
|
||||
const res = await app.inject({ method: 'GET', url: '/api/clis' });
|
||||
const body = res.json() as { success: true; data: CliListItem[] };
|
||||
for (const cli of body.data) {
|
||||
expect(typeof cli.id).toBe('string');
|
||||
expect(typeof cli.label).toBe('string');
|
||||
expect(typeof cli.shortBadge).toBe('string');
|
||||
expect(typeof cli.order).toBe('number');
|
||||
expect(['agent', 'shell']).toContain(cli.kind);
|
||||
expect(typeof cli.enabled).toBe('boolean');
|
||||
expect(typeof cli.stock).toBe('boolean');
|
||||
expect(typeof cli.installed).toBe('boolean');
|
||||
expect(cli).not.toHaveProperty('launch');
|
||||
expect(cli).not.toHaveProperty('env');
|
||||
expect(cli).not.toHaveProperty('capabilities');
|
||||
expect(cli).not.toHaveProperty('overlays');
|
||||
expect(cli).not.toHaveProperty('discovery');
|
||||
}
|
||||
});
|
||||
|
||||
it('every entry is stock: true (no custom entries exist before Phase 5)', async () => {
|
||||
const { app } = await createRouteTestHarness(registerCliRegistryRoutes);
|
||||
const res = await app.inject({ method: 'GET', url: '/api/clis' });
|
||||
const body = res.json() as { success: true; data: CliListItem[] };
|
||||
expect(body.data.every((c) => c.stock === true)).toBe(true);
|
||||
});
|
||||
|
||||
it('multi-user mode: an admin sees the full list', async () => {
|
||||
process.env.CODEMAN_MULTIUSER = '1';
|
||||
const { app } = await createRouteTestHarness(registerCliRegistryRoutes, {
|
||||
authUser: { username: 'root', role: 'admin' },
|
||||
});
|
||||
const res = await app.inject({ method: 'GET', url: '/api/clis' });
|
||||
const body = res.json() as { success: true; data: CliListItem[] };
|
||||
expect(body.data.length).toBeGreaterThanOrEqual(9);
|
||||
});
|
||||
|
||||
it('multi-user mode: a non-admin sees an empty list, not a 403', async () => {
|
||||
process.env.CODEMAN_MULTIUSER = '1';
|
||||
const { app } = await createRouteTestHarness(registerCliRegistryRoutes, {
|
||||
authUser: { username: 'bob', role: 'user' },
|
||||
});
|
||||
const res = await app.inject({ method: 'GET', url: '/api/clis' });
|
||||
expect(res.statusCode).toBe(200);
|
||||
const body = res.json() as { success: true; data: CliListItem[] };
|
||||
expect(body.data).toEqual([]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user