|
|
|
@@ -54,9 +54,9 @@ export const diagnosticsTools: PaperclipTool[] = [
|
|
|
|
|
|
|
|
|
|
// 3
|
|
|
|
|
{
|
|
|
|
|
name: 'diag_agents_missing_permissions',
|
|
|
|
|
name: 'diag_agents_without_skip_permissions',
|
|
|
|
|
description:
|
|
|
|
|
'Agentes sem dangerouslySkipPermissions=true em adapter_config (check 11 health).',
|
|
|
|
|
'Agentes sem dangerouslySkipPermissions=true em adapter_config (NOTA: NÃO é RBAC — é flag Claude Code/CLI para correr sem prompts. Aplicar conscientemente por agent, não em massa).',
|
|
|
|
|
inputSchema: { type: 'object', properties: {} },
|
|
|
|
|
handler: async () => {
|
|
|
|
|
const rows = await query(
|
|
|
|
@@ -441,4 +441,85 @@ export const diagnosticsTools: PaperclipTool[] = [
|
|
|
|
|
return ok({ updated: rows.length, rows });
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 17 — WRITE
|
|
|
|
|
{
|
|
|
|
|
name: 'ensure_agent_membership',
|
|
|
|
|
description:
|
|
|
|
|
'WRITE: garante membership active em company_memberships para um agent (idempotente). Fix achado #2 sessão 5.',
|
|
|
|
|
inputSchema: {
|
|
|
|
|
type: 'object',
|
|
|
|
|
properties: {
|
|
|
|
|
agent_id: { type: 'string', description: 'UUID do agente' },
|
|
|
|
|
membership_role: { type: 'string', description: 'Default "member"' },
|
|
|
|
|
},
|
|
|
|
|
required: ['agent_id'],
|
|
|
|
|
},
|
|
|
|
|
handler: async (args) => {
|
|
|
|
|
const agentId = String(args.agent_id ?? '');
|
|
|
|
|
const role = String(args.membership_role ?? 'member');
|
|
|
|
|
if (!agentId) throw new Error('agent_id obrigatório');
|
|
|
|
|
const existing = await query(
|
|
|
|
|
`SELECT id, status, membership_role FROM company_memberships
|
|
|
|
|
WHERE company_id = $1 AND principal_type = 'agent' AND principal_id = $2`,
|
|
|
|
|
[COMPANY_ID, agentId]
|
|
|
|
|
);
|
|
|
|
|
if (existing.length > 0) {
|
|
|
|
|
const rows = await query(
|
|
|
|
|
`UPDATE company_memberships
|
|
|
|
|
SET status = 'active', membership_role = $1, updated_at = NOW()
|
|
|
|
|
WHERE id = $2
|
|
|
|
|
RETURNING id, status, membership_role`,
|
|
|
|
|
[role, existing[0].id]
|
|
|
|
|
);
|
|
|
|
|
return ok({ action: 'updated', row: rows[0] });
|
|
|
|
|
}
|
|
|
|
|
const rows = await query(
|
|
|
|
|
`INSERT INTO company_memberships
|
|
|
|
|
(company_id, principal_type, principal_id, status, membership_role)
|
|
|
|
|
VALUES ($1, 'agent', $2, 'active', $3)
|
|
|
|
|
RETURNING id, status, membership_role`,
|
|
|
|
|
[COMPANY_ID, agentId, role]
|
|
|
|
|
);
|
|
|
|
|
return ok({ action: 'inserted', row: rows[0] });
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 18 — WRITE
|
|
|
|
|
{
|
|
|
|
|
name: 'grant_agent_permission',
|
|
|
|
|
description:
|
|
|
|
|
'WRITE: insere row em principal_permission_grants para um agent (idempotente por permission_key). Baseline RBAC. Fix achado #4.',
|
|
|
|
|
inputSchema: {
|
|
|
|
|
type: 'object',
|
|
|
|
|
properties: {
|
|
|
|
|
agent_id: { type: 'string', description: 'UUID do agente' },
|
|
|
|
|
permission_key: { type: 'string', description: 'Ex: tasks:assign, agents:create, runs:read' },
|
|
|
|
|
scope: { type: 'string', description: 'Scope opcional (default null)' },
|
|
|
|
|
},
|
|
|
|
|
required: ['agent_id', 'permission_key'],
|
|
|
|
|
},
|
|
|
|
|
handler: async (args) => {
|
|
|
|
|
const agentId = String(args.agent_id ?? '');
|
|
|
|
|
const permissionKey = String(args.permission_key ?? '');
|
|
|
|
|
const scope = args.scope ? String(args.scope) : null;
|
|
|
|
|
if (!agentId || !permissionKey) throw new Error('agent_id e permission_key obrigatórios');
|
|
|
|
|
const existing = await query(
|
|
|
|
|
`SELECT id FROM principal_permission_grants
|
|
|
|
|
WHERE company_id = $1 AND principal_type = 'agent' AND principal_id = $2 AND permission_key = $3`,
|
|
|
|
|
[COMPANY_ID, agentId, permissionKey]
|
|
|
|
|
);
|
|
|
|
|
if (existing.length > 0) {
|
|
|
|
|
return ok({ action: 'noop', existing: existing[0] });
|
|
|
|
|
}
|
|
|
|
|
const rows = await query(
|
|
|
|
|
`INSERT INTO principal_permission_grants
|
|
|
|
|
(company_id, principal_type, principal_id, permission_key, scope, granted_by_user_id)
|
|
|
|
|
VALUES ($1, 'agent', $2, $3, $4, 'mcp-bootstrap')
|
|
|
|
|
RETURNING id, permission_key, scope`,
|
|
|
|
|
[COMPANY_ID, agentId, permissionKey, scope]
|
|
|
|
|
);
|
|
|
|
|
return ok({ action: 'inserted', row: rows[0] });
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|