All files / src/entities/deploy-keys registry.ts

100% Statements 30/30
100% Branches 8/8
100% Functions 3/3
100% Lines 30/30

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 10422x 22x 22x 22x 22x   22x     22x                     22x                           9x 8x   8x 1x 1x       7x 7x     7x                                   6x 4x   4x 4x   4x   1x 1x       1x     1x 1x       1x 1x                         22x 9x    
import * as z from 'zod';
import { BrowseDeployKeysSchema } from './schema-readonly';
import { ManageDeployKeySchema } from './schema';
import { gitlab, toQuery } from '../../utils/gitlab-api';
import { normalizeProjectId } from '../../utils/projectIdentifier';
import { ToolRegistry, EnhancedToolDefinition } from '../../types';
import { assertActionAllowed } from '../utils';
 
// Deploy keys have existed since early GitLab and are Free tier throughout.
const FREE_REQ = { tier: 'free' } as const;
 
/**
 * Deploy keys tools registry - 2 CQRS tools.
 *
 * browse_deploy_keys (Query): list, get
 * manage_deploy_key (Command): add, enable, update, delete
 *
 * Deploy keys are SSH public keys granting a project repository access without
 * a user PAT — used by CI/automation. Project-level, Free tier.
 */
export const deployKeysToolRegistry: ToolRegistry = new Map<string, EnhancedToolDefinition>([
  // ============================================================================
  // browse_deploy_keys - CQRS Query Tool
  // ============================================================================
  [
    'browse_deploy_keys',
    {
      name: 'browse_deploy_keys',
      description:
        'List and inspect deploy keys (SSH keys granting repo access to CI/automation). Actions: list (a project’s keys, or all instance keys when project_id is omitted — admin only), get (a single project key by ID). Related: manage_deploy_key to add/enable/update/delete.',
      inputSchema: z.toJSONSchema(BrowseDeployKeysSchema),
      requirements: { default: FREE_REQ },
      gate: { envVar: 'USE_CI_TOKENS', defaultValue: true },
      handler: async (args: unknown): Promise<unknown> => {
        const input = BrowseDeployKeysSchema.parse(args);
        assertActionAllowed('browse_deploy_keys', input.action);
 
        if (input.action === 'get') {
          const encodedProjectId = normalizeProjectId(input.project_id);
          return gitlab.get(`projects/${encodedProjectId}/deploy_keys/${input.key_id}`);
        }
 
        // list: project-scoped when project_id is provided, otherwise instance-wide
        const { action: _action, project_id, ...query } = input;
        const path = project_id
          ? `projects/${normalizeProjectId(project_id)}/deploy_keys`
          : 'deploy_keys';
        return gitlab.get(path, { query: toQuery(query, []) });
      },
    },
  ],
 
  // ============================================================================
  // manage_deploy_key - CQRS Command Tool
  // ============================================================================
  [
    'manage_deploy_key',
    {
      name: 'manage_deploy_key',
      description:
        'Add, enable, update, or remove project deploy keys. Actions: add (register a new SSH public key with title and optional push access/expiry), enable (attach an existing key from another project), update (change title or can_push), delete (remove from this project). Related: browse_deploy_keys to inspect.',
      inputSchema: z.toJSONSchema(ManageDeployKeySchema),
      requirements: { default: FREE_REQ },
      gate: { envVar: 'USE_CI_TOKENS', defaultValue: true },
      handler: async (args: unknown): Promise<unknown> => {
        const input = ManageDeployKeySchema.parse(args);
        assertActionAllowed('manage_deploy_key', input.action);
 
        const encodedProjectId = normalizeProjectId(input.project_id);
        const base = `projects/${encodedProjectId}/deploy_keys`;
 
        switch (input.action) {
          case 'add': {
            const { action: _action, project_id: _project_id, ...body } = input;
            return gitlab.post(base, { body, contentType: 'json' });
          }
 
          case 'enable':
            return gitlab.post(`${base}/${input.key_id}/enable`);
 
          case 'update': {
            const { action: _action, project_id: _project_id, key_id, ...body } = input;
            return gitlab.put(`${base}/${key_id}`, { body, contentType: 'json' });
          }
 
          case 'delete': {
            await gitlab.delete(`${base}/${input.key_id}`);
            return { deleted: true, key_id: input.key_id };
          }
 
          /* istanbul ignore next -- unreachable with Zod discriminatedUnion */
          default:
            throw new Error(`Unknown action: ${(input as { action: string }).action}`);
        }
      },
    },
  ],
]);
 
/** Read-only tool names from the registry (for read-only mode filtering). */
export function getDeployKeysReadOnlyToolNames(): string[] {
  return ['browse_deploy_keys'];
}