620 lines
26 KiB
JavaScript
620 lines
26 KiB
JavaScript
/**
|
|
* V3 CLI Claims Command
|
|
* Claims-based authorization, permissions, and access control
|
|
*
|
|
* Created with ❤️ by ruv.io
|
|
*/
|
|
import { existsSync, readFileSync, writeFileSync, mkdirSync, renameSync } from 'node:fs';
|
|
import { resolve, dirname } from 'node:path';
|
|
import { output } from '../output.js';
|
|
const CLAIMS_CONFIG_PATHS = [
|
|
'.claude-flow/claims.json',
|
|
'claude-flow.claims.json',
|
|
];
|
|
function getClaimsConfigPaths() {
|
|
return [
|
|
resolve(CLAIMS_CONFIG_PATHS[0]),
|
|
resolve(CLAIMS_CONFIG_PATHS[1]),
|
|
resolve(process.env.HOME || '~', '.config/claude-flow/claims.json'),
|
|
];
|
|
}
|
|
function loadClaimsConfig() {
|
|
const configPaths = getClaimsConfigPaths();
|
|
for (const configPath of configPaths) {
|
|
if (existsSync(configPath)) {
|
|
const content = readFileSync(configPath, 'utf-8');
|
|
return { config: JSON.parse(content), path: configPath };
|
|
}
|
|
}
|
|
// Return default config with the first path as the default write location
|
|
const defaultConfig = {
|
|
roles: {
|
|
admin: ['*'],
|
|
developer: ['swarm:*', 'agent:*', 'memory:*', 'task:*', 'session:*'],
|
|
operator: ['swarm:status', 'agent:list', 'memory:read', 'task:list'],
|
|
viewer: ['*:list', '*:status', '*:read'],
|
|
},
|
|
defaultClaims: ['swarm:create', 'swarm:status', 'agent:spawn', 'agent:list', 'memory:read', 'memory:write', 'task:create'],
|
|
};
|
|
return { config: defaultConfig, path: configPaths[0] };
|
|
}
|
|
function saveClaimsConfig(config, configPath) {
|
|
const dir = dirname(configPath);
|
|
if (!existsSync(dir)) {
|
|
mkdirSync(dir, { recursive: true });
|
|
}
|
|
const tmpPath = configPath + '.tmp';
|
|
writeFileSync(tmpPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
|
|
renameSync(tmpPath, configPath);
|
|
}
|
|
// List subcommand
|
|
const listCommand = {
|
|
name: 'list',
|
|
description: 'List claims and permissions',
|
|
options: [
|
|
{ name: 'user', short: 'u', type: 'string', description: 'Filter by user ID' },
|
|
{ name: 'role', short: 'r', type: 'string', description: 'Filter by role' },
|
|
{ name: 'resource', type: 'string', description: 'Filter by resource' },
|
|
],
|
|
examples: [
|
|
{ command: 'claude-flow claims list', description: 'List all claims' },
|
|
{ command: 'claude-flow claims list -u user123', description: 'List user claims' },
|
|
],
|
|
action: async (_ctx) => {
|
|
try {
|
|
const { config, path: configPath } = loadClaimsConfig();
|
|
output.writeln();
|
|
output.writeln(output.bold('Claims Configuration'));
|
|
output.writeln(output.dim('─'.repeat(50)));
|
|
// Roles table
|
|
const roles = config.roles || {};
|
|
const roleEntries = Object.entries(roles);
|
|
if (roleEntries.length > 0) {
|
|
output.writeln();
|
|
output.writeln(output.bold('Roles'));
|
|
output.printTable({
|
|
columns: [
|
|
{ key: 'role', header: 'Role' },
|
|
{ key: 'count', header: 'Claims' },
|
|
{ key: 'preview', header: 'Preview', width: 50 },
|
|
],
|
|
data: roleEntries.map(([name, claims]) => ({
|
|
role: name,
|
|
count: claims.length,
|
|
preview: claims.slice(0, 4).join(', ') + (claims.length > 4 ? ', ...' : ''),
|
|
})),
|
|
border: true,
|
|
header: true,
|
|
});
|
|
}
|
|
// Users table
|
|
const users = config.users || {};
|
|
const userEntries = Object.entries(users);
|
|
if (userEntries.length > 0) {
|
|
output.writeln();
|
|
output.writeln(output.bold('Users'));
|
|
output.printTable({
|
|
columns: [
|
|
{ key: 'user', header: 'User' },
|
|
{ key: 'role', header: 'Role' },
|
|
{ key: 'extraClaims', header: 'Extra Claims' },
|
|
],
|
|
data: userEntries.map(([name, info]) => ({
|
|
user: name,
|
|
role: info.role || output.dim('(none)'),
|
|
extraClaims: info.claims ? info.claims.join(', ') : output.dim('(none)'),
|
|
})),
|
|
border: true,
|
|
header: true,
|
|
});
|
|
}
|
|
// Default claims
|
|
const defaults = config.defaultClaims || [];
|
|
if (defaults.length > 0) {
|
|
output.writeln();
|
|
output.writeln(output.bold('Default Claims'));
|
|
output.printList(defaults);
|
|
}
|
|
output.writeln();
|
|
output.writeln(output.dim(`Config: ${configPath}`));
|
|
return { success: true };
|
|
}
|
|
catch (error) {
|
|
output.printError(`Failed to list claims: ${error.message}`);
|
|
return { success: false, exitCode: 1 };
|
|
}
|
|
},
|
|
};
|
|
// Check subcommand
|
|
const checkCommand = {
|
|
name: 'check',
|
|
description: 'Check if a specific claim is granted',
|
|
options: [
|
|
{ name: 'claim', short: 'c', type: 'string', description: 'Claim to check', required: true },
|
|
{ name: 'user', short: 'u', type: 'string', description: 'User ID to check' },
|
|
{ name: 'resource', short: 'r', type: 'string', description: 'Resource context' },
|
|
],
|
|
examples: [
|
|
{ command: 'claude-flow claims check -c swarm:create', description: 'Check swarm creation permission' },
|
|
{ command: 'claude-flow claims check -c admin:delete -u user123', description: 'Check user permission' },
|
|
],
|
|
action: async (ctx) => {
|
|
const claim = ctx.flags.claim;
|
|
const user = ctx.flags.user || 'current';
|
|
const resource = ctx.flags.resource;
|
|
if (!claim) {
|
|
output.printError('Claim is required');
|
|
return { success: false, exitCode: 1 };
|
|
}
|
|
output.writeln();
|
|
output.writeln(output.bold('Claim Check'));
|
|
output.writeln(output.dim('─'.repeat(40)));
|
|
const spinner = output.createSpinner({ text: 'Evaluating claim...', spinner: 'dots' });
|
|
spinner.start();
|
|
const fs = await import('fs');
|
|
const path = await import('path');
|
|
// Real claims evaluation from config file
|
|
let isGranted = false;
|
|
let reason = 'Claim not found in policy';
|
|
let policySource = 'default';
|
|
try {
|
|
// Check for claims config file
|
|
const claimsConfigPaths = [
|
|
path.resolve('.claude-flow/claims.json'),
|
|
path.resolve('claude-flow.claims.json'),
|
|
path.resolve(process.env.HOME || '~', '.config/claude-flow/claims.json'),
|
|
];
|
|
let claimsConfig = {
|
|
// Default policy - allows basic operations
|
|
roles: {
|
|
admin: ['*'],
|
|
developer: ['swarm:*', 'agent:*', 'memory:*', 'task:*', 'session:*'],
|
|
operator: ['swarm:status', 'agent:list', 'memory:read', 'task:list'],
|
|
viewer: ['*:list', '*:status', '*:read'],
|
|
},
|
|
defaultClaims: ['swarm:create', 'swarm:status', 'agent:spawn', 'agent:list', 'memory:read', 'memory:write', 'task:create'],
|
|
};
|
|
for (const configPath of claimsConfigPaths) {
|
|
if (fs.existsSync(configPath)) {
|
|
const content = fs.readFileSync(configPath, 'utf-8');
|
|
claimsConfig = { ...claimsConfig, ...JSON.parse(content) };
|
|
policySource = configPath;
|
|
break;
|
|
}
|
|
}
|
|
// Resolve user's claims
|
|
const userConfig = claimsConfig.users?.[user];
|
|
let userClaims = [...(claimsConfig.defaultClaims || [])];
|
|
if (userConfig) {
|
|
// Add user-specific claims
|
|
if (userConfig.claims) {
|
|
userClaims = [...userClaims, ...userConfig.claims];
|
|
}
|
|
// Add role-based claims
|
|
if (userConfig.role && claimsConfig.roles?.[userConfig.role]) {
|
|
userClaims = [...userClaims, ...claimsConfig.roles[userConfig.role]];
|
|
}
|
|
}
|
|
// Check if claim is granted
|
|
const checkClaim = (claimToCheck, grantedClaims) => {
|
|
for (const granted of grantedClaims) {
|
|
// Exact match
|
|
if (granted === claimToCheck)
|
|
return true;
|
|
// Wildcard match (e.g., "swarm:*" matches "swarm:create")
|
|
if (granted === '*')
|
|
return true;
|
|
if (granted.endsWith(':*')) {
|
|
const prefix = granted.slice(0, -1);
|
|
if (claimToCheck.startsWith(prefix))
|
|
return true;
|
|
}
|
|
// Pattern match (e.g., "*:list" matches "swarm:list")
|
|
if (granted.startsWith('*:')) {
|
|
const suffix = granted.slice(1);
|
|
if (claimToCheck.endsWith(suffix))
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
};
|
|
isGranted = checkClaim(claim, userClaims);
|
|
if (isGranted) {
|
|
reason = userConfig?.role
|
|
? `Granted via role: ${userConfig.role}`
|
|
: 'Granted via default policy';
|
|
}
|
|
else {
|
|
reason = 'Not in user claims or role permissions';
|
|
}
|
|
spinner.stop();
|
|
}
|
|
catch (error) {
|
|
spinner.stop();
|
|
// On error, fall back to permissive default
|
|
isGranted = !claim.startsWith('admin:');
|
|
reason = isGranted ? 'Granted (default permissive policy)' : 'Admin claims require explicit grant';
|
|
policySource = 'fallback';
|
|
}
|
|
if (isGranted) {
|
|
output.writeln(output.success('✓ Claim granted'));
|
|
}
|
|
else {
|
|
output.writeln(output.error('✗ Claim denied'));
|
|
}
|
|
output.writeln();
|
|
output.printBox([
|
|
`Claim: ${claim}`,
|
|
`User: ${user}`,
|
|
`Resource: ${resource || 'global'}`,
|
|
`Result: ${isGranted ? output.success('GRANTED') : output.error('DENIED')}`,
|
|
``,
|
|
`Reason: ${reason}`,
|
|
`Policy: ${policySource}`,
|
|
].join('\n'), 'Result');
|
|
return { success: isGranted };
|
|
},
|
|
};
|
|
// Grant subcommand
|
|
const grantCommand = {
|
|
name: 'grant',
|
|
description: 'Grant a claim to user or role',
|
|
options: [
|
|
{ name: 'claim', short: 'c', type: 'string', description: 'Claim to grant', required: true },
|
|
{ name: 'user', short: 'u', type: 'string', description: 'User ID' },
|
|
{ name: 'role', short: 'r', type: 'string', description: 'Role name' },
|
|
{ name: 'scope', short: 's', type: 'string', description: 'Scope: global, namespace, resource', default: 'global' },
|
|
{ name: 'expires', short: 'e', type: 'string', description: 'Expiration time (e.g., 24h, 7d)' },
|
|
],
|
|
examples: [
|
|
{ command: 'claude-flow claims grant -c swarm:create -u user123', description: 'Grant to user' },
|
|
{ command: 'claude-flow claims grant -c agent:spawn -r developer', description: 'Grant to role' },
|
|
],
|
|
action: async (ctx) => {
|
|
const claim = ctx.flags.claim;
|
|
const user = ctx.flags.user;
|
|
const role = ctx.flags.role;
|
|
if (!claim) {
|
|
output.printError('Claim is required');
|
|
return { success: false, exitCode: 1 };
|
|
}
|
|
if (!user && !role) {
|
|
output.printError('Either user or role is required');
|
|
return { success: false, exitCode: 1 };
|
|
}
|
|
try {
|
|
const { config, path: configPath } = loadClaimsConfig();
|
|
if (user) {
|
|
if (!config.users)
|
|
config.users = {};
|
|
if (!config.users[user])
|
|
config.users[user] = {};
|
|
if (!config.users[user].claims)
|
|
config.users[user].claims = [];
|
|
if (!config.users[user].claims.includes(claim)) {
|
|
config.users[user].claims.push(claim);
|
|
}
|
|
}
|
|
if (role) {
|
|
if (!config.roles)
|
|
config.roles = {};
|
|
if (!config.roles[role])
|
|
config.roles[role] = [];
|
|
if (!config.roles[role].includes(claim)) {
|
|
config.roles[role].push(claim);
|
|
}
|
|
}
|
|
saveClaimsConfig(config, configPath);
|
|
output.writeln();
|
|
const target = user ? `user "${user}"` : `role "${role}"`;
|
|
output.writeln(output.success(`Granted "${claim}" to ${target}`));
|
|
output.writeln(output.dim(`Saved to: ${configPath}`));
|
|
return { success: true };
|
|
}
|
|
catch (error) {
|
|
output.printError(`Failed to grant claim: ${error.message}`);
|
|
return { success: false, exitCode: 1 };
|
|
}
|
|
},
|
|
};
|
|
// Revoke subcommand
|
|
const revokeCommand = {
|
|
name: 'revoke',
|
|
description: 'Revoke a claim from user or role',
|
|
options: [
|
|
{ name: 'claim', short: 'c', type: 'string', description: 'Claim to revoke', required: true },
|
|
{ name: 'user', short: 'u', type: 'string', description: 'User ID' },
|
|
{ name: 'role', short: 'r', type: 'string', description: 'Role name' },
|
|
],
|
|
examples: [
|
|
{ command: 'claude-flow claims revoke -c swarm:delete -u user123', description: 'Revoke from user' },
|
|
{ command: 'claude-flow claims revoke -c admin:* -r guest', description: 'Revoke from role' },
|
|
],
|
|
action: async (ctx) => {
|
|
const claim = ctx.flags.claim;
|
|
const user = ctx.flags.user;
|
|
const role = ctx.flags.role;
|
|
if (!claim) {
|
|
output.printError('Claim is required');
|
|
return { success: false, exitCode: 1 };
|
|
}
|
|
if (!user && !role) {
|
|
output.printError('Either user or role is required');
|
|
return { success: false, exitCode: 1 };
|
|
}
|
|
try {
|
|
const { config, path: configPath } = loadClaimsConfig();
|
|
let removed = false;
|
|
if (user && config.users?.[user]?.claims) {
|
|
const idx = config.users[user].claims.indexOf(claim);
|
|
if (idx !== -1) {
|
|
config.users[user].claims.splice(idx, 1);
|
|
removed = true;
|
|
}
|
|
}
|
|
if (role && config.roles?.[role]) {
|
|
const idx = config.roles[role].indexOf(claim);
|
|
if (idx !== -1) {
|
|
config.roles[role].splice(idx, 1);
|
|
removed = true;
|
|
}
|
|
}
|
|
if (!removed) {
|
|
const target = user ? `user "${user}"` : `role "${role}"`;
|
|
output.writeln();
|
|
output.printError(`Claim "${claim}" not found on ${target}`);
|
|
return { success: false, exitCode: 1 };
|
|
}
|
|
saveClaimsConfig(config, configPath);
|
|
output.writeln();
|
|
const target = user ? `user "${user}"` : `role "${role}"`;
|
|
output.writeln(output.success(`Revoked "${claim}" from ${target}`));
|
|
output.writeln(output.dim(`Saved to: ${configPath}`));
|
|
return { success: true };
|
|
}
|
|
catch (error) {
|
|
output.printError(`Failed to revoke claim: ${error.message}`);
|
|
return { success: false, exitCode: 1 };
|
|
}
|
|
},
|
|
};
|
|
// Roles subcommand
|
|
const rolesCommand = {
|
|
name: 'roles',
|
|
description: 'Manage roles and their claims',
|
|
options: [
|
|
{ name: 'action', short: 'a', type: 'string', description: 'Action: list, create, delete, show', default: 'list' },
|
|
{ name: 'name', short: 'n', type: 'string', description: 'Role name' },
|
|
],
|
|
examples: [
|
|
{ command: 'claude-flow claims roles', description: 'List all roles' },
|
|
{ command: 'claude-flow claims roles -a show -n admin', description: 'Show role details' },
|
|
],
|
|
action: async (ctx) => {
|
|
const action = ctx.flags.action || 'list';
|
|
const name = ctx.flags.name;
|
|
try {
|
|
const { config, path: configPath } = loadClaimsConfig();
|
|
if (action === 'list') {
|
|
const roles = config.roles || {};
|
|
const entries = Object.entries(roles);
|
|
if (entries.length === 0) {
|
|
output.writeln();
|
|
output.writeln(output.dim('No roles defined.'));
|
|
return { success: true };
|
|
}
|
|
output.writeln();
|
|
output.writeln(output.bold('Roles'));
|
|
output.printTable({
|
|
columns: [
|
|
{ key: 'role', header: 'Role' },
|
|
{ key: 'count', header: 'Claims' },
|
|
{ key: 'claims', header: 'Claims List', width: 60 },
|
|
],
|
|
data: entries.map(([roleName, claims]) => ({
|
|
role: roleName,
|
|
count: claims.length,
|
|
claims: claims.join(', '),
|
|
})),
|
|
border: true,
|
|
header: true,
|
|
});
|
|
output.writeln(output.dim(`Config: ${configPath}`));
|
|
return { success: true };
|
|
}
|
|
if (action === 'show') {
|
|
if (!name) {
|
|
output.printError('Role name is required (use -n <name>)');
|
|
return { success: false, exitCode: 1 };
|
|
}
|
|
const claims = config.roles?.[name];
|
|
if (!claims) {
|
|
output.printError(`Role "${name}" not found`);
|
|
return { success: false, exitCode: 1 };
|
|
}
|
|
output.writeln();
|
|
output.writeln(output.bold(`Role: ${name}`));
|
|
output.writeln(output.dim('─'.repeat(40)));
|
|
output.writeln(`Claims (${claims.length}):`);
|
|
output.printList(claims);
|
|
return { success: true };
|
|
}
|
|
if (action === 'create') {
|
|
if (!name) {
|
|
output.printError('Role name is required (use -n <name>)');
|
|
return { success: false, exitCode: 1 };
|
|
}
|
|
if (!config.roles)
|
|
config.roles = {};
|
|
if (config.roles[name]) {
|
|
output.printError(`Role "${name}" already exists`);
|
|
return { success: false, exitCode: 1 };
|
|
}
|
|
config.roles[name] = [];
|
|
saveClaimsConfig(config, configPath);
|
|
output.writeln();
|
|
output.writeln(output.success(`Created role "${name}"`));
|
|
output.writeln(output.dim('Use "claims grant -c <claim> -r ' + name + '" to add claims.'));
|
|
return { success: true };
|
|
}
|
|
if (action === 'delete') {
|
|
if (!name) {
|
|
output.printError('Role name is required (use -n <name>)');
|
|
return { success: false, exitCode: 1 };
|
|
}
|
|
if (!config.roles?.[name]) {
|
|
output.printError(`Role "${name}" not found`);
|
|
return { success: false, exitCode: 1 };
|
|
}
|
|
delete config.roles[name];
|
|
saveClaimsConfig(config, configPath);
|
|
output.writeln();
|
|
output.writeln(output.success(`Deleted role "${name}"`));
|
|
return { success: true };
|
|
}
|
|
output.printError(`Unknown action "${action}". Use: list, create, delete, show`);
|
|
return { success: false, exitCode: 1 };
|
|
}
|
|
catch (error) {
|
|
output.printError(`Failed to manage roles: ${error.message}`);
|
|
return { success: false, exitCode: 1 };
|
|
}
|
|
},
|
|
};
|
|
// Policies subcommand
|
|
const policiesCommand = {
|
|
name: 'policies',
|
|
description: 'Manage claim policies',
|
|
options: [
|
|
{ name: 'action', short: 'a', type: 'string', description: 'Action: list, create, delete', default: 'list' },
|
|
{ name: 'name', short: 'n', type: 'string', description: 'Policy name' },
|
|
],
|
|
examples: [
|
|
{ command: 'claude-flow claims policies', description: 'List policies' },
|
|
{ command: 'claude-flow claims policies -a create -n rate-limit', description: 'Create policy' },
|
|
],
|
|
action: async (ctx) => {
|
|
const action = ctx.flags.action || 'list';
|
|
const name = ctx.flags.name;
|
|
try {
|
|
const { config, path: configPath } = loadClaimsConfig();
|
|
if (action === 'list') {
|
|
output.writeln();
|
|
output.writeln(output.bold('Policies'));
|
|
output.writeln(output.dim('─'.repeat(50)));
|
|
// Default claims as a policy
|
|
const defaults = config.defaultClaims || [];
|
|
output.writeln();
|
|
output.writeln(output.bold('Default Policy'));
|
|
if (defaults.length > 0) {
|
|
output.printList(defaults);
|
|
}
|
|
else {
|
|
output.writeln(output.dim(' (no default claims)'));
|
|
}
|
|
// Role-based policies
|
|
const roles = config.roles || {};
|
|
const entries = Object.entries(roles);
|
|
if (entries.length > 0) {
|
|
output.writeln();
|
|
output.writeln(output.bold('Role-Based Policies'));
|
|
output.printTable({
|
|
columns: [
|
|
{ key: 'policy', header: 'Policy (Role)' },
|
|
{ key: 'count', header: 'Claims' },
|
|
{ key: 'preview', header: 'Preview', width: 50 },
|
|
],
|
|
data: entries.map(([roleName, claims]) => ({
|
|
policy: roleName,
|
|
count: claims.length,
|
|
preview: claims.slice(0, 4).join(', ') + (claims.length > 4 ? ', ...' : ''),
|
|
})),
|
|
border: true,
|
|
header: true,
|
|
});
|
|
}
|
|
output.writeln();
|
|
output.writeln(output.dim(`Config: ${configPath}`));
|
|
return { success: true };
|
|
}
|
|
if (action === 'create') {
|
|
if (!name) {
|
|
output.printError('Policy name is required (use -n <name>)');
|
|
return { success: false, exitCode: 1 };
|
|
}
|
|
if (!config.roles)
|
|
config.roles = {};
|
|
if (config.roles[name]) {
|
|
output.printError(`Policy "${name}" already exists`);
|
|
return { success: false, exitCode: 1 };
|
|
}
|
|
config.roles[name] = [];
|
|
saveClaimsConfig(config, configPath);
|
|
output.writeln();
|
|
output.writeln(output.success(`Created policy "${name}"`));
|
|
output.writeln(output.dim('Use "claims grant -c <claim> -r ' + name + '" to add claims.'));
|
|
return { success: true };
|
|
}
|
|
if (action === 'delete') {
|
|
if (!name) {
|
|
output.printError('Policy name is required (use -n <name>)');
|
|
return { success: false, exitCode: 1 };
|
|
}
|
|
if (!config.roles?.[name]) {
|
|
output.printError(`Policy "${name}" not found`);
|
|
return { success: false, exitCode: 1 };
|
|
}
|
|
delete config.roles[name];
|
|
saveClaimsConfig(config, configPath);
|
|
output.writeln();
|
|
output.writeln(output.success(`Deleted policy "${name}"`));
|
|
return { success: true };
|
|
}
|
|
output.printError(`Unknown action "${action}". Use: list, create, delete`);
|
|
return { success: false, exitCode: 1 };
|
|
}
|
|
catch (error) {
|
|
output.printError(`Failed to manage policies: ${error.message}`);
|
|
return { success: false, exitCode: 1 };
|
|
}
|
|
},
|
|
};
|
|
// Main claims command
|
|
export const claimsCommand = {
|
|
name: 'claims',
|
|
description: 'Claims-based authorization, permissions, and access control',
|
|
subcommands: [listCommand, checkCommand, grantCommand, revokeCommand, rolesCommand, policiesCommand],
|
|
examples: [
|
|
{ command: 'claude-flow claims list', description: 'List all claims' },
|
|
{ command: 'claude-flow claims check -c swarm:create', description: 'Check permission' },
|
|
{ command: 'claude-flow claims grant -c agent:spawn -r developer', description: 'Grant claim' },
|
|
],
|
|
action: async () => {
|
|
output.writeln();
|
|
output.writeln(output.bold('RuFlo Claims System'));
|
|
output.writeln(output.dim('Fine-grained authorization and access control'));
|
|
output.writeln();
|
|
output.writeln('Subcommands:');
|
|
output.printList([
|
|
'list - List claims and permissions',
|
|
'check - Check if a claim is granted',
|
|
'grant - Grant a claim to user or role',
|
|
'revoke - Revoke a claim',
|
|
'roles - Manage roles and their claims',
|
|
'policies - Manage claim policies',
|
|
]);
|
|
output.writeln();
|
|
output.writeln('Claim Types:');
|
|
output.printList([
|
|
'swarm:* - Swarm operations (create, delete, scale)',
|
|
'agent:* - Agent operations (spawn, terminate)',
|
|
'memory:* - Memory operations (read, write, delete)',
|
|
'admin:* - Administrative operations',
|
|
]);
|
|
output.writeln();
|
|
output.writeln(output.dim('Created with ❤️ by ruv.io'));
|
|
return { success: true };
|
|
},
|
|
};
|
|
export default claimsCommand;
|
|
//# sourceMappingURL=claims.js.map
|