2026-02-21 04:55:45 +00:00
|
|
|
import { describe, it, expect, vi } from 'vitest';
|
|
|
|
|
import { createGetCommand } from '../../src/commands/get.js';
|
|
|
|
|
import type { GetCommandDeps } from '../../src/commands/get.js';
|
|
|
|
|
|
|
|
|
|
function makeDeps(items: unknown[] = []): GetCommandDeps & { output: string[] } {
|
|
|
|
|
const output: string[] = [];
|
|
|
|
|
return {
|
|
|
|
|
output,
|
|
|
|
|
fetchResource: vi.fn(async () => items),
|
|
|
|
|
log: (...args: string[]) => output.push(args.join(' ')),
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
describe('get command', () => {
|
|
|
|
|
it('lists servers in table format', async () => {
|
|
|
|
|
const deps = makeDeps([
|
|
|
|
|
{ id: 'srv-1', name: 'slack', transport: 'STDIO', packageName: '@slack/mcp', dockerImage: null },
|
|
|
|
|
{ id: 'srv-2', name: 'github', transport: 'SSE', packageName: null, dockerImage: 'ghcr.io/github-mcp' },
|
|
|
|
|
]);
|
|
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'servers']);
|
|
|
|
|
|
feat: gated project experience & prompt intelligence
Implements the full gated session flow and prompt intelligence system:
- Prisma schema: add gated, priority, summary, chapters, linkTarget fields
- Session gate: state machine (gated → begin_session → ungated) with LLM-powered
tool selection based on prompt index
- Tag matcher: intelligent prompt-to-tool matching with project/server/action tags
- LLM selector: tiered provider selection (fast for gating, heavy for complex tasks)
- Link resolver: cross-project MCP resource references (project/server:uri format)
- Prompt summary service: LLM-generated summaries and chapter extraction
- System project bootstrap: ensures default project exists on startup
- Structural link health checks: enrichWithLinkStatus on prompt GET endpoints
- CLI: create prompt --priority/--link, create project --gated/--no-gated,
describe project shows prompts section, get prompts shows PRI/LINK/STATUS
- Apply/edit: priority, linkTarget, gated fields supported
- Shell completions: fish updated with new flags
- 1,253 tests passing across all packages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:22:42 +00:00
|
|
|
expect(deps.fetchResource).toHaveBeenCalledWith('servers', undefined, undefined);
|
2026-02-21 04:55:45 +00:00
|
|
|
expect(deps.output[0]).toContain('NAME');
|
|
|
|
|
expect(deps.output[0]).toContain('TRANSPORT');
|
|
|
|
|
expect(deps.output.join('\n')).toContain('slack');
|
|
|
|
|
expect(deps.output.join('\n')).toContain('github');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('resolves resource aliases', async () => {
|
|
|
|
|
const deps = makeDeps([]);
|
|
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'srv']);
|
feat: gated project experience & prompt intelligence
Implements the full gated session flow and prompt intelligence system:
- Prisma schema: add gated, priority, summary, chapters, linkTarget fields
- Session gate: state machine (gated → begin_session → ungated) with LLM-powered
tool selection based on prompt index
- Tag matcher: intelligent prompt-to-tool matching with project/server/action tags
- LLM selector: tiered provider selection (fast for gating, heavy for complex tasks)
- Link resolver: cross-project MCP resource references (project/server:uri format)
- Prompt summary service: LLM-generated summaries and chapter extraction
- System project bootstrap: ensures default project exists on startup
- Structural link health checks: enrichWithLinkStatus on prompt GET endpoints
- CLI: create prompt --priority/--link, create project --gated/--no-gated,
describe project shows prompts section, get prompts shows PRI/LINK/STATUS
- Apply/edit: priority, linkTarget, gated fields supported
- Shell completions: fish updated with new flags
- 1,253 tests passing across all packages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:22:42 +00:00
|
|
|
expect(deps.fetchResource).toHaveBeenCalledWith('servers', undefined, undefined);
|
2026-02-21 04:55:45 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('passes ID when provided', async () => {
|
|
|
|
|
const deps = makeDeps([{ id: 'srv-1', name: 'slack' }]);
|
|
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'servers', 'srv-1']);
|
feat: gated project experience & prompt intelligence
Implements the full gated session flow and prompt intelligence system:
- Prisma schema: add gated, priority, summary, chapters, linkTarget fields
- Session gate: state machine (gated → begin_session → ungated) with LLM-powered
tool selection based on prompt index
- Tag matcher: intelligent prompt-to-tool matching with project/server/action tags
- LLM selector: tiered provider selection (fast for gating, heavy for complex tasks)
- Link resolver: cross-project MCP resource references (project/server:uri format)
- Prompt summary service: LLM-generated summaries and chapter extraction
- System project bootstrap: ensures default project exists on startup
- Structural link health checks: enrichWithLinkStatus on prompt GET endpoints
- CLI: create prompt --priority/--link, create project --gated/--no-gated,
describe project shows prompts section, get prompts shows PRI/LINK/STATUS
- Apply/edit: priority, linkTarget, gated fields supported
- Shell completions: fish updated with new flags
- 1,253 tests passing across all packages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:22:42 +00:00
|
|
|
expect(deps.fetchResource).toHaveBeenCalledWith('servers', 'srv-1', undefined);
|
2026-02-21 04:55:45 +00:00
|
|
|
});
|
|
|
|
|
|
2026-02-27 17:05:05 +00:00
|
|
|
it('outputs apply-compatible JSON format (multi-doc)', async () => {
|
2026-02-22 14:33:25 +00:00
|
|
|
const deps = makeDeps([{ id: 'srv-1', name: 'slack', createdAt: '2025-01-01', updatedAt: '2025-01-01', version: 1 }]);
|
2026-02-21 04:55:45 +00:00
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'servers', '-o', 'json']);
|
|
|
|
|
|
|
|
|
|
const parsed = JSON.parse(deps.output[0] ?? '');
|
2026-02-27 17:05:05 +00:00
|
|
|
// Array of documents with kind field, internal fields stripped
|
|
|
|
|
expect(Array.isArray(parsed)).toBe(true);
|
|
|
|
|
expect(parsed[0].kind).toBe('server');
|
|
|
|
|
expect(parsed[0].name).toBe('slack');
|
|
|
|
|
expect(parsed[0]).not.toHaveProperty('id');
|
|
|
|
|
expect(parsed[0]).not.toHaveProperty('createdAt');
|
|
|
|
|
expect(parsed[0]).not.toHaveProperty('updatedAt');
|
|
|
|
|
expect(parsed[0]).not.toHaveProperty('version');
|
2026-02-21 04:55:45 +00:00
|
|
|
});
|
|
|
|
|
|
2026-02-27 17:05:05 +00:00
|
|
|
it('outputs apply-compatible YAML format (multi-doc)', async () => {
|
2026-02-22 14:33:25 +00:00
|
|
|
const deps = makeDeps([{ id: 'srv-1', name: 'slack', createdAt: '2025-01-01' }]);
|
2026-02-21 04:55:45 +00:00
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'servers', '-o', 'yaml']);
|
2026-02-22 14:33:25 +00:00
|
|
|
const text = deps.output[0];
|
2026-02-27 17:05:05 +00:00
|
|
|
expect(text).toContain('kind: server');
|
2026-02-22 14:33:25 +00:00
|
|
|
expect(text).toContain('name: slack');
|
|
|
|
|
expect(text).not.toContain('id:');
|
|
|
|
|
expect(text).not.toContain('createdAt:');
|
2026-02-21 04:55:45 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('lists instances with correct columns', async () => {
|
|
|
|
|
const deps = makeDeps([
|
2026-02-23 00:07:42 +00:00
|
|
|
{ id: 'inst-1', serverId: 'srv-1', server: { name: 'my-grafana' }, status: 'RUNNING', containerId: 'abc123def456', port: 3000 },
|
2026-02-21 04:55:45 +00:00
|
|
|
]);
|
|
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'instances']);
|
2026-02-23 00:07:42 +00:00
|
|
|
expect(deps.output[0]).toContain('NAME');
|
2026-02-21 04:55:45 +00:00
|
|
|
expect(deps.output[0]).toContain('STATUS');
|
2026-02-23 00:07:42 +00:00
|
|
|
expect(deps.output.join('\n')).toContain('my-grafana');
|
2026-02-21 04:55:45 +00:00
|
|
|
expect(deps.output.join('\n')).toContain('RUNNING');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('shows no results message for empty list', async () => {
|
|
|
|
|
const deps = makeDeps([]);
|
|
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'servers']);
|
2026-02-22 14:33:25 +00:00
|
|
|
expect(deps.output[0]).toContain('No servers found');
|
2026-02-21 04:55:45 +00:00
|
|
|
});
|
feat: granular RBAC with resource/operation bindings, users, groups
- Replace admin role with granular roles: view, create, delete, edit, run
- Two binding types: resource bindings (role+resource+optional name) and
operation bindings (role:run + action like backup, logs, impersonate)
- Name-scoped resource bindings for per-instance access control
- Remove role from project members (all permissions via RBAC)
- Add users, groups, RBAC CRUD endpoints and CLI commands
- describe user/group shows all RBAC access (direct + inherited)
- create rbac supports --subject, --binding, --operation flags
- Backup/restore handles users, groups, RBAC definitions
- mcplocal project-based MCP endpoint discovery
- Full test coverage for all new functionality
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 11:05:19 +00:00
|
|
|
|
|
|
|
|
it('lists users with correct columns (no ROLE column)', async () => {
|
|
|
|
|
const deps = makeDeps([
|
|
|
|
|
{ id: 'usr-1', email: 'alice@test.com', name: 'Alice', provider: null },
|
|
|
|
|
{ id: 'usr-2', email: 'bob@test.com', name: null, provider: 'oidc' },
|
|
|
|
|
]);
|
|
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'users']);
|
|
|
|
|
|
feat: gated project experience & prompt intelligence
Implements the full gated session flow and prompt intelligence system:
- Prisma schema: add gated, priority, summary, chapters, linkTarget fields
- Session gate: state machine (gated → begin_session → ungated) with LLM-powered
tool selection based on prompt index
- Tag matcher: intelligent prompt-to-tool matching with project/server/action tags
- LLM selector: tiered provider selection (fast for gating, heavy for complex tasks)
- Link resolver: cross-project MCP resource references (project/server:uri format)
- Prompt summary service: LLM-generated summaries and chapter extraction
- System project bootstrap: ensures default project exists on startup
- Structural link health checks: enrichWithLinkStatus on prompt GET endpoints
- CLI: create prompt --priority/--link, create project --gated/--no-gated,
describe project shows prompts section, get prompts shows PRI/LINK/STATUS
- Apply/edit: priority, linkTarget, gated fields supported
- Shell completions: fish updated with new flags
- 1,253 tests passing across all packages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:22:42 +00:00
|
|
|
expect(deps.fetchResource).toHaveBeenCalledWith('users', undefined, undefined);
|
feat: granular RBAC with resource/operation bindings, users, groups
- Replace admin role with granular roles: view, create, delete, edit, run
- Two binding types: resource bindings (role+resource+optional name) and
operation bindings (role:run + action like backup, logs, impersonate)
- Name-scoped resource bindings for per-instance access control
- Remove role from project members (all permissions via RBAC)
- Add users, groups, RBAC CRUD endpoints and CLI commands
- describe user/group shows all RBAC access (direct + inherited)
- create rbac supports --subject, --binding, --operation flags
- Backup/restore handles users, groups, RBAC definitions
- mcplocal project-based MCP endpoint discovery
- Full test coverage for all new functionality
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 11:05:19 +00:00
|
|
|
const text = deps.output.join('\n');
|
|
|
|
|
expect(text).toContain('EMAIL');
|
|
|
|
|
expect(text).toContain('NAME');
|
|
|
|
|
expect(text).not.toContain('ROLE');
|
|
|
|
|
expect(text).toContain('PROVIDER');
|
|
|
|
|
expect(text).toContain('alice@test.com');
|
|
|
|
|
expect(text).toContain('Alice');
|
|
|
|
|
expect(text).toContain('bob@test.com');
|
|
|
|
|
expect(text).toContain('oidc');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('resolves user alias', async () => {
|
|
|
|
|
const deps = makeDeps([]);
|
|
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'user']);
|
feat: gated project experience & prompt intelligence
Implements the full gated session flow and prompt intelligence system:
- Prisma schema: add gated, priority, summary, chapters, linkTarget fields
- Session gate: state machine (gated → begin_session → ungated) with LLM-powered
tool selection based on prompt index
- Tag matcher: intelligent prompt-to-tool matching with project/server/action tags
- LLM selector: tiered provider selection (fast for gating, heavy for complex tasks)
- Link resolver: cross-project MCP resource references (project/server:uri format)
- Prompt summary service: LLM-generated summaries and chapter extraction
- System project bootstrap: ensures default project exists on startup
- Structural link health checks: enrichWithLinkStatus on prompt GET endpoints
- CLI: create prompt --priority/--link, create project --gated/--no-gated,
describe project shows prompts section, get prompts shows PRI/LINK/STATUS
- Apply/edit: priority, linkTarget, gated fields supported
- Shell completions: fish updated with new flags
- 1,253 tests passing across all packages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:22:42 +00:00
|
|
|
expect(deps.fetchResource).toHaveBeenCalledWith('users', undefined, undefined);
|
feat: granular RBAC with resource/operation bindings, users, groups
- Replace admin role with granular roles: view, create, delete, edit, run
- Two binding types: resource bindings (role+resource+optional name) and
operation bindings (role:run + action like backup, logs, impersonate)
- Name-scoped resource bindings for per-instance access control
- Remove role from project members (all permissions via RBAC)
- Add users, groups, RBAC CRUD endpoints and CLI commands
- describe user/group shows all RBAC access (direct + inherited)
- create rbac supports --subject, --binding, --operation flags
- Backup/restore handles users, groups, RBAC definitions
- mcplocal project-based MCP endpoint discovery
- Full test coverage for all new functionality
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 11:05:19 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('lists groups with correct columns', async () => {
|
|
|
|
|
const deps = makeDeps([
|
|
|
|
|
{
|
|
|
|
|
id: 'grp-1',
|
|
|
|
|
name: 'dev-team',
|
|
|
|
|
description: 'Developers',
|
|
|
|
|
members: [{ user: { email: 'alice@test.com' } }, { user: { email: 'bob@test.com' } }],
|
|
|
|
|
},
|
|
|
|
|
{ id: 'grp-2', name: 'ops-team', description: 'Operations', members: [] },
|
|
|
|
|
]);
|
|
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'groups']);
|
|
|
|
|
|
feat: gated project experience & prompt intelligence
Implements the full gated session flow and prompt intelligence system:
- Prisma schema: add gated, priority, summary, chapters, linkTarget fields
- Session gate: state machine (gated → begin_session → ungated) with LLM-powered
tool selection based on prompt index
- Tag matcher: intelligent prompt-to-tool matching with project/server/action tags
- LLM selector: tiered provider selection (fast for gating, heavy for complex tasks)
- Link resolver: cross-project MCP resource references (project/server:uri format)
- Prompt summary service: LLM-generated summaries and chapter extraction
- System project bootstrap: ensures default project exists on startup
- Structural link health checks: enrichWithLinkStatus on prompt GET endpoints
- CLI: create prompt --priority/--link, create project --gated/--no-gated,
describe project shows prompts section, get prompts shows PRI/LINK/STATUS
- Apply/edit: priority, linkTarget, gated fields supported
- Shell completions: fish updated with new flags
- 1,253 tests passing across all packages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:22:42 +00:00
|
|
|
expect(deps.fetchResource).toHaveBeenCalledWith('groups', undefined, undefined);
|
feat: granular RBAC with resource/operation bindings, users, groups
- Replace admin role with granular roles: view, create, delete, edit, run
- Two binding types: resource bindings (role+resource+optional name) and
operation bindings (role:run + action like backup, logs, impersonate)
- Name-scoped resource bindings for per-instance access control
- Remove role from project members (all permissions via RBAC)
- Add users, groups, RBAC CRUD endpoints and CLI commands
- describe user/group shows all RBAC access (direct + inherited)
- create rbac supports --subject, --binding, --operation flags
- Backup/restore handles users, groups, RBAC definitions
- mcplocal project-based MCP endpoint discovery
- Full test coverage for all new functionality
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 11:05:19 +00:00
|
|
|
const text = deps.output.join('\n');
|
|
|
|
|
expect(text).toContain('NAME');
|
|
|
|
|
expect(text).toContain('MEMBERS');
|
|
|
|
|
expect(text).toContain('DESCRIPTION');
|
|
|
|
|
expect(text).toContain('dev-team');
|
|
|
|
|
expect(text).toContain('2');
|
|
|
|
|
expect(text).toContain('ops-team');
|
|
|
|
|
expect(text).toContain('0');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('resolves group alias', async () => {
|
|
|
|
|
const deps = makeDeps([]);
|
|
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'group']);
|
feat: gated project experience & prompt intelligence
Implements the full gated session flow and prompt intelligence system:
- Prisma schema: add gated, priority, summary, chapters, linkTarget fields
- Session gate: state machine (gated → begin_session → ungated) with LLM-powered
tool selection based on prompt index
- Tag matcher: intelligent prompt-to-tool matching with project/server/action tags
- LLM selector: tiered provider selection (fast for gating, heavy for complex tasks)
- Link resolver: cross-project MCP resource references (project/server:uri format)
- Prompt summary service: LLM-generated summaries and chapter extraction
- System project bootstrap: ensures default project exists on startup
- Structural link health checks: enrichWithLinkStatus on prompt GET endpoints
- CLI: create prompt --priority/--link, create project --gated/--no-gated,
describe project shows prompts section, get prompts shows PRI/LINK/STATUS
- Apply/edit: priority, linkTarget, gated fields supported
- Shell completions: fish updated with new flags
- 1,253 tests passing across all packages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:22:42 +00:00
|
|
|
expect(deps.fetchResource).toHaveBeenCalledWith('groups', undefined, undefined);
|
feat: granular RBAC with resource/operation bindings, users, groups
- Replace admin role with granular roles: view, create, delete, edit, run
- Two binding types: resource bindings (role+resource+optional name) and
operation bindings (role:run + action like backup, logs, impersonate)
- Name-scoped resource bindings for per-instance access control
- Remove role from project members (all permissions via RBAC)
- Add users, groups, RBAC CRUD endpoints and CLI commands
- describe user/group shows all RBAC access (direct + inherited)
- create rbac supports --subject, --binding, --operation flags
- Backup/restore handles users, groups, RBAC definitions
- mcplocal project-based MCP endpoint discovery
- Full test coverage for all new functionality
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 11:05:19 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('lists rbac definitions with correct columns', async () => {
|
|
|
|
|
const deps = makeDeps([
|
|
|
|
|
{
|
|
|
|
|
id: 'rbac-1',
|
|
|
|
|
name: 'admins',
|
|
|
|
|
subjects: [{ kind: 'User', name: 'admin@test.com' }],
|
|
|
|
|
roleBindings: [{ role: 'edit', resource: '*' }],
|
|
|
|
|
},
|
|
|
|
|
]);
|
|
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'rbac']);
|
|
|
|
|
|
feat: gated project experience & prompt intelligence
Implements the full gated session flow and prompt intelligence system:
- Prisma schema: add gated, priority, summary, chapters, linkTarget fields
- Session gate: state machine (gated → begin_session → ungated) with LLM-powered
tool selection based on prompt index
- Tag matcher: intelligent prompt-to-tool matching with project/server/action tags
- LLM selector: tiered provider selection (fast for gating, heavy for complex tasks)
- Link resolver: cross-project MCP resource references (project/server:uri format)
- Prompt summary service: LLM-generated summaries and chapter extraction
- System project bootstrap: ensures default project exists on startup
- Structural link health checks: enrichWithLinkStatus on prompt GET endpoints
- CLI: create prompt --priority/--link, create project --gated/--no-gated,
describe project shows prompts section, get prompts shows PRI/LINK/STATUS
- Apply/edit: priority, linkTarget, gated fields supported
- Shell completions: fish updated with new flags
- 1,253 tests passing across all packages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:22:42 +00:00
|
|
|
expect(deps.fetchResource).toHaveBeenCalledWith('rbac', undefined, undefined);
|
feat: granular RBAC with resource/operation bindings, users, groups
- Replace admin role with granular roles: view, create, delete, edit, run
- Two binding types: resource bindings (role+resource+optional name) and
operation bindings (role:run + action like backup, logs, impersonate)
- Name-scoped resource bindings for per-instance access control
- Remove role from project members (all permissions via RBAC)
- Add users, groups, RBAC CRUD endpoints and CLI commands
- describe user/group shows all RBAC access (direct + inherited)
- create rbac supports --subject, --binding, --operation flags
- Backup/restore handles users, groups, RBAC definitions
- mcplocal project-based MCP endpoint discovery
- Full test coverage for all new functionality
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 11:05:19 +00:00
|
|
|
const text = deps.output.join('\n');
|
|
|
|
|
expect(text).toContain('NAME');
|
|
|
|
|
expect(text).toContain('SUBJECTS');
|
|
|
|
|
expect(text).toContain('BINDINGS');
|
|
|
|
|
expect(text).toContain('admins');
|
|
|
|
|
expect(text).toContain('User:admin@test.com');
|
|
|
|
|
expect(text).toContain('edit:*');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('resolves rbac-definition alias', async () => {
|
|
|
|
|
const deps = makeDeps([]);
|
|
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'rbac-definition']);
|
feat: gated project experience & prompt intelligence
Implements the full gated session flow and prompt intelligence system:
- Prisma schema: add gated, priority, summary, chapters, linkTarget fields
- Session gate: state machine (gated → begin_session → ungated) with LLM-powered
tool selection based on prompt index
- Tag matcher: intelligent prompt-to-tool matching with project/server/action tags
- LLM selector: tiered provider selection (fast for gating, heavy for complex tasks)
- Link resolver: cross-project MCP resource references (project/server:uri format)
- Prompt summary service: LLM-generated summaries and chapter extraction
- System project bootstrap: ensures default project exists on startup
- Structural link health checks: enrichWithLinkStatus on prompt GET endpoints
- CLI: create prompt --priority/--link, create project --gated/--no-gated,
describe project shows prompts section, get prompts shows PRI/LINK/STATUS
- Apply/edit: priority, linkTarget, gated fields supported
- Shell completions: fish updated with new flags
- 1,253 tests passing across all packages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:22:42 +00:00
|
|
|
expect(deps.fetchResource).toHaveBeenCalledWith('rbac', undefined, undefined);
|
feat: granular RBAC with resource/operation bindings, users, groups
- Replace admin role with granular roles: view, create, delete, edit, run
- Two binding types: resource bindings (role+resource+optional name) and
operation bindings (role:run + action like backup, logs, impersonate)
- Name-scoped resource bindings for per-instance access control
- Remove role from project members (all permissions via RBAC)
- Add users, groups, RBAC CRUD endpoints and CLI commands
- describe user/group shows all RBAC access (direct + inherited)
- create rbac supports --subject, --binding, --operation flags
- Backup/restore handles users, groups, RBAC definitions
- mcplocal project-based MCP endpoint discovery
- Full test coverage for all new functionality
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-23 11:05:19 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('lists projects with new columns', async () => {
|
|
|
|
|
const deps = makeDeps([{
|
|
|
|
|
id: 'proj-1',
|
|
|
|
|
name: 'smart-home',
|
|
|
|
|
description: 'Home automation',
|
|
|
|
|
proxyMode: 'filtered',
|
|
|
|
|
ownerId: 'usr-1',
|
|
|
|
|
servers: [{ server: { name: 'grafana' } }],
|
|
|
|
|
}]);
|
|
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'projects']);
|
|
|
|
|
|
|
|
|
|
const text = deps.output.join('\n');
|
|
|
|
|
expect(text).toContain('MODE');
|
|
|
|
|
expect(text).toContain('SERVERS');
|
|
|
|
|
expect(text).toContain('smart-home');
|
|
|
|
|
expect(text).toContain('filtered');
|
|
|
|
|
expect(text).toContain('1');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('displays mixed resource and operation bindings', async () => {
|
|
|
|
|
const deps = makeDeps([
|
|
|
|
|
{
|
|
|
|
|
id: 'rbac-1',
|
|
|
|
|
name: 'admin-access',
|
|
|
|
|
subjects: [{ kind: 'Group', name: 'admin' }],
|
|
|
|
|
roleBindings: [
|
|
|
|
|
{ role: 'edit', resource: '*' },
|
|
|
|
|
{ role: 'run', action: 'logs' },
|
|
|
|
|
{ role: 'run', action: 'backup' },
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
]);
|
|
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'rbac']);
|
|
|
|
|
|
|
|
|
|
const text = deps.output.join('\n');
|
|
|
|
|
expect(text).toContain('edit:*');
|
|
|
|
|
expect(text).toContain('run>logs');
|
|
|
|
|
expect(text).toContain('run>backup');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('displays name-scoped resource bindings', async () => {
|
|
|
|
|
const deps = makeDeps([
|
|
|
|
|
{
|
|
|
|
|
id: 'rbac-1',
|
|
|
|
|
name: 'ha-viewer',
|
|
|
|
|
subjects: [{ kind: 'User', name: 'alice@test.com' }],
|
|
|
|
|
roleBindings: [{ role: 'view', resource: 'servers', name: 'my-ha' }],
|
|
|
|
|
},
|
|
|
|
|
]);
|
|
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'rbac']);
|
|
|
|
|
|
|
|
|
|
const text = deps.output.join('\n');
|
|
|
|
|
expect(text).toContain('view:servers:my-ha');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('shows no results message for empty users list', async () => {
|
|
|
|
|
const deps = makeDeps([]);
|
|
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'users']);
|
|
|
|
|
expect(deps.output[0]).toContain('No users found');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('shows no results message for empty groups list', async () => {
|
|
|
|
|
const deps = makeDeps([]);
|
|
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'groups']);
|
|
|
|
|
expect(deps.output[0]).toContain('No groups found');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('shows no results message for empty rbac list', async () => {
|
|
|
|
|
const deps = makeDeps([]);
|
|
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'rbac']);
|
|
|
|
|
expect(deps.output[0]).toContain('No rbac found');
|
|
|
|
|
});
|
feat: gated project experience & prompt intelligence
Implements the full gated session flow and prompt intelligence system:
- Prisma schema: add gated, priority, summary, chapters, linkTarget fields
- Session gate: state machine (gated → begin_session → ungated) with LLM-powered
tool selection based on prompt index
- Tag matcher: intelligent prompt-to-tool matching with project/server/action tags
- LLM selector: tiered provider selection (fast for gating, heavy for complex tasks)
- Link resolver: cross-project MCP resource references (project/server:uri format)
- Prompt summary service: LLM-generated summaries and chapter extraction
- System project bootstrap: ensures default project exists on startup
- Structural link health checks: enrichWithLinkStatus on prompt GET endpoints
- CLI: create prompt --priority/--link, create project --gated/--no-gated,
describe project shows prompts section, get prompts shows PRI/LINK/STATUS
- Apply/edit: priority, linkTarget, gated fields supported
- Shell completions: fish updated with new flags
- 1,253 tests passing across all packages
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 23:22:42 +00:00
|
|
|
|
|
|
|
|
it('lists prompts with project name column', async () => {
|
|
|
|
|
const deps = makeDeps([
|
|
|
|
|
{ id: 'p-1', name: 'debug-guide', projectId: 'proj-1', project: { name: 'smart-home' }, createdAt: '2025-01-01T00:00:00Z' },
|
|
|
|
|
{ id: 'p-2', name: 'global-rules', projectId: null, project: null, createdAt: '2025-01-01T00:00:00Z' },
|
|
|
|
|
]);
|
|
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'prompts']);
|
|
|
|
|
|
|
|
|
|
const text = deps.output.join('\n');
|
|
|
|
|
expect(text).toContain('NAME');
|
|
|
|
|
expect(text).toContain('PROJECT');
|
|
|
|
|
expect(text).toContain('debug-guide');
|
|
|
|
|
expect(text).toContain('smart-home');
|
|
|
|
|
expect(text).toContain('global-rules');
|
|
|
|
|
expect(text).toContain('(global)');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('lists promptrequests with project name column', async () => {
|
|
|
|
|
const deps = makeDeps([
|
|
|
|
|
{ id: 'pr-1', name: 'new-rule', projectId: 'proj-1', project: { name: 'my-project' }, createdBySession: 'sess-abc123def456', createdAt: '2025-01-01T00:00:00Z' },
|
|
|
|
|
]);
|
|
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'promptrequests']);
|
|
|
|
|
|
|
|
|
|
const text = deps.output.join('\n');
|
|
|
|
|
expect(text).toContain('new-rule');
|
|
|
|
|
expect(text).toContain('my-project');
|
|
|
|
|
expect(text).toContain('sess-abc123d');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('passes --project option to fetchResource', async () => {
|
|
|
|
|
const deps = makeDeps([]);
|
|
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'prompts', '--project', 'smart-home']);
|
|
|
|
|
|
|
|
|
|
expect(deps.fetchResource).toHaveBeenCalledWith('prompts', undefined, { project: 'smart-home' });
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('does not pass project when --project is not specified', async () => {
|
|
|
|
|
const deps = makeDeps([]);
|
|
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'prompts']);
|
|
|
|
|
|
|
|
|
|
expect(deps.fetchResource).toHaveBeenCalledWith('prompts', undefined, undefined);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('passes --all flag to fetchResource', async () => {
|
|
|
|
|
const deps = makeDeps([]);
|
|
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'prompts', '-A']);
|
|
|
|
|
|
|
|
|
|
expect(deps.fetchResource).toHaveBeenCalledWith('prompts', undefined, { all: true });
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('passes both --project and --all when both given', async () => {
|
|
|
|
|
const deps = makeDeps([]);
|
|
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'prompts', '--project', 'my-proj', '-A']);
|
|
|
|
|
|
|
|
|
|
expect(deps.fetchResource).toHaveBeenCalledWith('prompts', undefined, { project: 'my-proj', all: true });
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('resolves prompt alias', async () => {
|
|
|
|
|
const deps = makeDeps([]);
|
|
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'prompt']);
|
|
|
|
|
expect(deps.fetchResource).toHaveBeenCalledWith('prompts', undefined, undefined);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('resolves pr alias to promptrequests', async () => {
|
|
|
|
|
const deps = makeDeps([]);
|
|
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'pr']);
|
|
|
|
|
expect(deps.fetchResource).toHaveBeenCalledWith('promptrequests', undefined, undefined);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('shows no results message for empty prompts list', async () => {
|
|
|
|
|
const deps = makeDeps([]);
|
|
|
|
|
const cmd = createGetCommand(deps);
|
|
|
|
|
await cmd.parseAsync(['node', 'test', 'prompts']);
|
|
|
|
|
expect(deps.output[0]).toContain('No prompts found');
|
|
|
|
|
});
|
2026-02-21 04:55:45 +00:00
|
|
|
});
|