delimit-cli 1.0.0 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (95) hide show
  1. package/.github/workflows/api-governance.yml +43 -0
  2. package/README.md +70 -113
  3. package/adapters/codex-skill.js +87 -0
  4. package/adapters/cursor-extension.js +190 -0
  5. package/adapters/gemini-action.js +93 -0
  6. package/adapters/openai-function.js +112 -0
  7. package/adapters/xai-plugin.js +151 -0
  8. package/bin/delimit-cli.js +921 -0
  9. package/bin/delimit.js +237 -1
  10. package/delimit.yml +19 -0
  11. package/hooks/evidence-status.sh +12 -0
  12. package/hooks/git/commit-msg +4 -0
  13. package/hooks/git/pre-commit +4 -0
  14. package/hooks/git/pre-push +4 -0
  15. package/hooks/install-hooks.sh +583 -0
  16. package/hooks/message-auth-hook.js +9 -0
  17. package/hooks/message-governance-hook.js +9 -0
  18. package/hooks/models/claude-post.js +4 -0
  19. package/hooks/models/claude-pre.js +4 -0
  20. package/hooks/models/codex-post.js +4 -0
  21. package/hooks/models/codex-pre.js +4 -0
  22. package/hooks/models/cursor-post.js +4 -0
  23. package/hooks/models/cursor-pre.js +4 -0
  24. package/hooks/models/gemini-post.js +4 -0
  25. package/hooks/models/gemini-pre.js +4 -0
  26. package/hooks/models/openai-post.js +4 -0
  27. package/hooks/models/openai-pre.js +4 -0
  28. package/hooks/models/windsurf-post.js +4 -0
  29. package/hooks/models/windsurf-pre.js +4 -0
  30. package/hooks/models/xai-post.js +4 -0
  31. package/hooks/models/xai-pre.js +4 -0
  32. package/hooks/post-bash-hook.js +13 -0
  33. package/hooks/post-mcp-hook.js +13 -0
  34. package/hooks/post-response-hook.js +4 -0
  35. package/hooks/post-tool-hook.js +126 -0
  36. package/hooks/post-write-hook.js +13 -0
  37. package/hooks/pre-bash-hook.js +30 -0
  38. package/hooks/pre-mcp-hook.js +13 -0
  39. package/hooks/pre-read-hook.js +13 -0
  40. package/hooks/pre-search-hook.js +13 -0
  41. package/hooks/pre-submit-hook.js +4 -0
  42. package/hooks/pre-task-hook.js +13 -0
  43. package/hooks/pre-tool-hook.js +121 -0
  44. package/hooks/pre-web-hook.js +13 -0
  45. package/hooks/pre-write-hook.js +31 -0
  46. package/hooks/test-hooks.sh +12 -0
  47. package/hooks/update-delimit.sh +6 -0
  48. package/lib/agent.js +509 -0
  49. package/lib/api-engine.js +156 -0
  50. package/lib/auth-setup.js +891 -0
  51. package/lib/decision-engine.js +474 -0
  52. package/lib/hooks-installer.js +416 -0
  53. package/lib/platform-adapters.js +353 -0
  54. package/lib/proxy-handler.js +114 -0
  55. package/package.json +38 -30
  56. package/scripts/infect.js +128 -0
  57. package/test-decision-engine.js +181 -0
  58. package/test-hook.js +27 -0
  59. package/dist/commands/validate.d.ts +0 -2
  60. package/dist/commands/validate.d.ts.map +0 -1
  61. package/dist/commands/validate.js +0 -106
  62. package/dist/commands/validate.js.map +0 -1
  63. package/dist/index.d.ts +0 -3
  64. package/dist/index.d.ts.map +0 -1
  65. package/dist/index.js +0 -71
  66. package/dist/index.js.map +0 -1
  67. package/dist/types/index.d.ts +0 -39
  68. package/dist/types/index.d.ts.map +0 -1
  69. package/dist/types/index.js +0 -3
  70. package/dist/types/index.js.map +0 -1
  71. package/dist/utils/api.d.ts +0 -3
  72. package/dist/utils/api.d.ts.map +0 -1
  73. package/dist/utils/api.js +0 -64
  74. package/dist/utils/api.js.map +0 -1
  75. package/dist/utils/file.d.ts +0 -7
  76. package/dist/utils/file.d.ts.map +0 -1
  77. package/dist/utils/file.js +0 -69
  78. package/dist/utils/file.js.map +0 -1
  79. package/dist/utils/logger.d.ts +0 -14
  80. package/dist/utils/logger.d.ts.map +0 -1
  81. package/dist/utils/logger.js +0 -28
  82. package/dist/utils/logger.js.map +0 -1
  83. package/dist/utils/masker.d.ts +0 -14
  84. package/dist/utils/masker.d.ts.map +0 -1
  85. package/dist/utils/masker.js +0 -89
  86. package/dist/utils/masker.js.map +0 -1
  87. package/src/commands/validate.ts +0 -150
  88. package/src/index.ts +0 -80
  89. package/src/types/index.ts +0 -41
  90. package/src/utils/api.ts +0 -68
  91. package/src/utils/file.ts +0 -71
  92. package/src/utils/logger.ts +0 -27
  93. package/src/utils/masker.ts +0 -101
  94. package/test-sensitive.yaml +0 -109
  95. package/tsconfig.json +0 -23
@@ -1 +0,0 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,MAAM;gBACL,MAAM;mBACH,MAAM;iBACR,MAAM;gBACP,MAAM;iBACL,MAAM;4BAGK,MAAM,WAAW,MAAM;8BAIrB,MAAM,WAAW,MAAM;eAItC,MAAM;mBAGF,MAAM;gBAGT,MAAM;qBACD,MAAM;CACxB,CAAC"}
@@ -1,28 +0,0 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.logger = void 0;
7
- const chalk_1 = __importDefault(require("chalk"));
8
- exports.logger = {
9
- info: (msg) => console.log(chalk_1.default.blue('ℹ'), msg),
10
- success: (msg) => console.log(chalk_1.default.green('✔'), chalk_1.default.green(msg)),
11
- error: (msg) => console.log(chalk_1.default.red('✖'), chalk_1.default.red(msg)),
12
- warn: (msg) => console.log(chalk_1.default.yellow('⚠'), chalk_1.default.yellow(msg)),
13
- title: (msg) => console.log('\n' + chalk_1.default.bold.magenta(msg) + '\n'),
14
- // Additional helpers for validation output
15
- validationError: (path, message) => {
16
- console.log(chalk_1.default.red(` ✖ ${path}: ${message}`));
17
- },
18
- validationWarning: (path, message) => {
19
- console.log(chalk_1.default.yellow(` ⚠ ${path}: ${message}`));
20
- },
21
- dim: (msg) => console.log(chalk_1.default.dim(msg)),
22
- // Progress indicators
23
- loading: (msg) => console.log(chalk_1.default.cyan('⟳'), chalk_1.default.cyan(msg)),
24
- // Formatting helpers
25
- bold: (msg) => chalk_1.default.bold(msg),
26
- underline: (msg) => chalk_1.default.underline(msg),
27
- };
28
- //# sourceMappingURL=logger.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":";;;;;;AAAA,kDAA0B;AAEb,QAAA,MAAM,GAAG;IACpB,IAAI,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC;IACxD,OAAO,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,eAAK,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACzE,KAAK,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,eAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACnE,IAAI,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,eAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IACxE,KAAK,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,eAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC;IAE1E,2CAA2C;IAC3C,eAAe,EAAE,CAAC,IAAY,EAAE,OAAe,EAAE,EAAE;QACjD,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,GAAG,CAAC,OAAO,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC;IACpD,CAAC;IAED,iBAAiB,EAAE,CAAC,IAAY,EAAE,OAAe,EAAE,EAAE;QACnD,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,MAAM,CAAC,OAAO,IAAI,KAAK,OAAO,EAAE,CAAC,CAAC,CAAC;IACvD,CAAC;IAED,GAAG,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAEjD,sBAAsB;IACtB,OAAO,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,eAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,eAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAEvE,qBAAqB;IACrB,IAAI,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,eAAK,CAAC,IAAI,CAAC,GAAG,CAAC;IACtC,SAAS,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,eAAK,CAAC,SAAS,CAAC,GAAG,CAAC;CACjD,CAAC"}
@@ -1,14 +0,0 @@
1
- /**
2
- * Privacy Shield Implementation
3
- * Removes sensitive data from OpenAPI specs before sending to API
4
- */
5
- export declare function maskSensitiveData(data: any): any;
6
- /**
7
- * Get statistics about what was masked
8
- */
9
- export declare function getMaskingStats(original: any, masked: any): {
10
- fieldsRemoved: number;
11
- bytesReduced: number;
12
- percentReduced: number;
13
- };
14
- //# sourceMappingURL=masker.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"masker.d.ts","sourceRoot":"","sources":["../../src/utils/masker.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,GAAG,GAAG,GAAG,CAyDhD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,GAAG;IAC3D,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;CACxB,CA6BA"}
@@ -1,89 +0,0 @@
1
- "use strict";
2
- /**
3
- * Privacy Shield Implementation
4
- * Removes sensitive data from OpenAPI specs before sending to API
5
- */
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.maskSensitiveData = maskSensitiveData;
8
- exports.getMaskingStats = getMaskingStats;
9
- function maskSensitiveData(data) {
10
- const SENSITIVE_KEYS = new Set([
11
- 'description',
12
- 'summary',
13
- 'example',
14
- 'examples',
15
- 'servers',
16
- 'contact', // Also mask contact info for privacy
17
- 'termsOfService',
18
- 'license'
19
- ]);
20
- // Handle null or undefined
21
- if (data === null || data === undefined) {
22
- return data;
23
- }
24
- // Handle arrays
25
- if (Array.isArray(data)) {
26
- return data.map(item => maskSensitiveData(item));
27
- }
28
- // Handle objects
29
- if (typeof data === 'object') {
30
- const maskedObj = {};
31
- for (const [key, value] of Object.entries(data)) {
32
- // Skip sensitive keys entirely
33
- if (SENSITIVE_KEYS.has(key)) {
34
- // For required fields like servers, provide minimal replacement
35
- if (key === 'servers') {
36
- maskedObj[key] = [{ url: 'https://api.example.com' }];
37
- }
38
- // Skip other sensitive fields
39
- continue;
40
- }
41
- // Special handling for info object - keep structure but mask content
42
- if (key === 'info' && typeof value === 'object' && value !== null) {
43
- const infoObj = value;
44
- maskedObj[key] = {
45
- title: infoObj.title || 'API',
46
- version: infoObj.version || '1.0.0',
47
- // Remove description, contact, license, etc.
48
- };
49
- continue;
50
- }
51
- // Recursively mask nested objects
52
- maskedObj[key] = maskSensitiveData(value);
53
- }
54
- return maskedObj;
55
- }
56
- // Return primitives as-is (strings, numbers, booleans)
57
- return data;
58
- }
59
- /**
60
- * Get statistics about what was masked
61
- */
62
- function getMaskingStats(original, masked) {
63
- const originalSize = JSON.stringify(original).length;
64
- const maskedSize = JSON.stringify(masked).length;
65
- const bytesReduced = originalSize - maskedSize;
66
- const percentReduced = Math.round((bytesReduced / originalSize) * 100);
67
- // Count removed fields (simplified)
68
- const countFields = (obj) => {
69
- let count = 0;
70
- if (obj && typeof obj === 'object') {
71
- for (const value of Object.values(obj)) {
72
- count++;
73
- if (typeof value === 'object') {
74
- count += countFields(value);
75
- }
76
- }
77
- }
78
- return count;
79
- };
80
- const originalFields = countFields(original);
81
- const maskedFields = countFields(masked);
82
- const fieldsRemoved = originalFields - maskedFields;
83
- return {
84
- fieldsRemoved,
85
- bytesReduced,
86
- percentReduced
87
- };
88
- }
89
- //# sourceMappingURL=masker.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"masker.js","sourceRoot":"","sources":["../../src/utils/masker.ts"],"names":[],"mappings":"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"}
@@ -1,150 +0,0 @@
1
- import { readOpenApiFile, getFileInfo } from '../utils/file';
2
- import { maskSensitiveData, getMaskingStats } from '../utils/masker';
3
- import { delimitApi } from '../utils/api';
4
- import { logger } from '../utils/logger';
5
-
6
- interface ValidationResponse {
7
- valid: boolean;
8
- errors?: Array<{
9
- path?: string;
10
- message: string;
11
- severity?: string;
12
- }>;
13
- warnings?: Array<{
14
- path?: string;
15
- message: string;
16
- severity?: string;
17
- }>;
18
- summary?: {
19
- error_count: number;
20
- warning_count: number;
21
- };
22
- }
23
-
24
- export async function validateCommand(filepath: string, options: any = {}) {
25
- try {
26
- // Check if JSON output is requested
27
- const jsonOutput = options.output === 'json';
28
-
29
- if (!jsonOutput) {
30
- logger.title('🚀 Delimit API Governance Validator');
31
- }
32
-
33
- // Step 1: Read file
34
- if (!jsonOutput) {
35
- logger.info(`Reading file: ${filepath}`);
36
- const fileInfo = getFileInfo(filepath);
37
- logger.dim(` File: ${fileInfo.name} (${fileInfo.size})`);
38
- }
39
-
40
- const rawData = readOpenApiFile(filepath);
41
-
42
- // Step 2: Apply Privacy Shield
43
- if (!jsonOutput) {
44
- logger.info('Applying Privacy Shield™ - masking sensitive data...');
45
- }
46
- const maskedData = maskSensitiveData(rawData);
47
-
48
- // Show masking stats if verbose mode
49
- if (options.verbose && !jsonOutput) {
50
- const stats = getMaskingStats(rawData, maskedData);
51
- logger.dim(` Removed ${stats.fieldsRemoved} fields (${stats.percentReduced}% size reduction)`);
52
- }
53
-
54
- // Step 3: Validate with API
55
- if (!jsonOutput) {
56
- logger.loading('Validating with Delimit governance engine...');
57
- }
58
-
59
- const response = await delimitApi.post('/validate', {
60
- spec: maskedData
61
- });
62
-
63
- const result: ValidationResponse = response.data;
64
-
65
- // Step 4: Display results
66
- if (jsonOutput) {
67
- // Output JSON for machine parsing
68
- console.log(JSON.stringify(result));
69
- process.exit(result.valid ? 0 : 1);
70
- }
71
-
72
- // Human-readable output
73
- console.log(''); // Empty line for spacing
74
-
75
- if (result.valid) {
76
- logger.success('✨ Validation PASSED - Your API specification is compliant!');
77
-
78
- if (result.warnings && result.warnings.length > 0) {
79
- console.log('');
80
- logger.warn(`Found ${result.warnings.length} warning(s):`);
81
- result.warnings.forEach(warning => {
82
- logger.validationWarning(
83
- warning.path || 'general',
84
- warning.message
85
- );
86
- });
87
- }
88
-
89
- // Show summary
90
- if (result.summary) {
91
- console.log('');
92
- logger.info('Summary:');
93
- logger.dim(` • Errors: ${result.summary.error_count}`);
94
- logger.dim(` • Warnings: ${result.summary.warning_count}`);
95
- }
96
-
97
- process.exit(0);
98
- } else {
99
- logger.error('❌ Validation FAILED - Your API specification has issues');
100
-
101
- if (result.errors && result.errors.length > 0) {
102
- console.log('');
103
- logger.error(`Found ${result.errors.length} error(s):`);
104
- result.errors.forEach(error => {
105
- logger.validationError(
106
- error.path || 'general',
107
- error.message
108
- );
109
- });
110
- }
111
-
112
- if (result.warnings && result.warnings.length > 0) {
113
- console.log('');
114
- logger.warn(`Found ${result.warnings.length} warning(s):`);
115
- result.warnings.forEach(warning => {
116
- logger.validationWarning(
117
- warning.path || 'general',
118
- warning.message
119
- );
120
- });
121
- }
122
-
123
- // Show summary
124
- if (result.summary) {
125
- console.log('');
126
- logger.info('Summary:');
127
- logger.dim(` • Errors: ${result.summary.error_count}`);
128
- logger.dim(` • Warnings: ${result.summary.warning_count}`);
129
- }
130
-
131
- console.log('');
132
- logger.info('💡 Fix the errors above and run validation again.');
133
-
134
- process.exit(1);
135
- }
136
-
137
- } catch (error: any) {
138
- console.log(''); // Empty line for spacing
139
- logger.error('Validation failed with error:');
140
- logger.error(error.message);
141
-
142
- if (error.message.includes('DELIMIT_API_KEY')) {
143
- console.log('');
144
- logger.info('📝 Get your free API key at:');
145
- logger.info(' https://rapidapi.com/delimit/api/openapi-diff-api');
146
- }
147
-
148
- process.exit(1);
149
- }
150
- }
package/src/index.ts DELETED
@@ -1,80 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { Command } from 'commander';
4
- import { validateCommand } from './commands/validate';
5
- import { logger } from './utils/logger';
6
-
7
- // Create the main program
8
- const program = new Command();
9
-
10
- program
11
- .name('delimit')
12
- .description('CLI tool for Delimit API governance with built-in Privacy Shield™')
13
- .version('1.0.0')
14
- .option('-v, --verbose', 'Show detailed output')
15
- .option('--api-url <url>', 'Override API URL (for testing)');
16
-
17
- // Command: delimit validate <filepath>
18
- program
19
- .command('validate')
20
- .description('Validate a local OpenAPI specification against governance rules')
21
- .argument('<filepath>', 'Path to the OpenAPI specification file (JSON or YAML)')
22
- .option('--verbose', 'Show detailed validation output')
23
- .option('--output <format>', 'Output format: text or json', 'text')
24
- .action(async (filepath, options) => {
25
- // Merge global and command options
26
- const globalOptions = program.opts();
27
- const mergedOptions = { ...globalOptions, ...options };
28
-
29
- // Set API URL if provided
30
- if (mergedOptions.apiUrl) {
31
- process.env.DELIMIT_API_URL = mergedOptions.apiUrl;
32
- }
33
-
34
- await validateCommand(filepath, mergedOptions);
35
- });
36
-
37
- // Future commands placeholder
38
- program
39
- .command('diff')
40
- .description('Compare two OpenAPI specifications (coming soon)')
41
- .argument('<old>', 'Path to the old/previous OpenAPI spec')
42
- .argument('<new>', 'Path to the new/current OpenAPI spec')
43
- .action(() => {
44
- logger.info('The diff command is coming soon in v1.1.0!');
45
- logger.info('It will detect breaking changes between API versions.');
46
- process.exit(0);
47
- });
48
-
49
- program
50
- .command('analyze')
51
- .description('Analyze API changes for risks (coming soon)')
52
- .argument('<old>', 'Path to the old OpenAPI spec')
53
- .argument('<new>', 'Path to the new OpenAPI spec')
54
- .action(() => {
55
- logger.info('The analyze command is coming soon in v1.1.0!');
56
- logger.info('It will identify risky changes and soft breaks.');
57
- process.exit(0);
58
- });
59
-
60
- // Handle unknown commands
61
- program.on('command:*', () => {
62
- logger.error(`Invalid command: ${program.args.join(' ')}`);
63
- logger.info('Run "delimit --help" for a list of available commands.');
64
- process.exit(1);
65
- });
66
-
67
- // Catch unhandled rejections
68
- process.on('unhandledRejection', (reason: any) => {
69
- logger.error('Unexpected error occurred:');
70
- logger.error(reason?.message || reason);
71
- process.exit(1);
72
- });
73
-
74
- // Parse arguments
75
- program.parse(process.argv);
76
-
77
- // Show help if no arguments provided
78
- if (!process.argv.slice(2).length) {
79
- program.outputHelp();
80
- }
@@ -1,41 +0,0 @@
1
- export interface OpenApiSpec {
2
- openapi?: string;
3
- swagger?: string;
4
- info?: {
5
- title?: string;
6
- version?: string;
7
- description?: string;
8
- [key: string]: any;
9
- };
10
- paths?: Record<string, any>;
11
- components?: Record<string, any>;
12
- servers?: Array<any>;
13
- [key: string]: any;
14
- }
15
-
16
- export interface ValidationError {
17
- path?: string;
18
- message: string;
19
- severity?: 'error' | 'warning' | 'info';
20
- code?: string;
21
- }
22
-
23
- export interface ValidationResult {
24
- valid: boolean;
25
- errors: ValidationError[];
26
- warnings: ValidationError[];
27
- summary: {
28
- error_count: number;
29
- warning_count: number;
30
- };
31
- metadata?: {
32
- engine_version?: string;
33
- validation_time?: string;
34
- };
35
- }
36
-
37
- export interface CliOptions {
38
- verbose?: boolean;
39
- output?: 'json' | 'text';
40
- quiet?: boolean;
41
- }
package/src/utils/api.ts DELETED
@@ -1,68 +0,0 @@
1
- import axios, { AxiosInstance } from 'axios';
2
-
3
- // Create axios instance with default config
4
- export const delimitApi: AxiosInstance = axios.create({
5
- baseURL: process.env.DELIMIT_API_URL || 'https://api.delimit.ai/v1',
6
- timeout: 30000, // 30 second timeout
7
- headers: {
8
- 'Content-Type': 'application/json',
9
- 'User-Agent': 'delimit-cli/1.0.0'
10
- }
11
- });
12
-
13
- // Request interceptor to add API key
14
- delimitApi.interceptors.request.use(
15
- (config) => {
16
- const apiKey = process.env.DELIMIT_API_KEY;
17
-
18
- if (!apiKey) {
19
- throw new Error(
20
- 'DELIMIT_API_KEY environment variable is not set.\n' +
21
- 'Please set it using: export DELIMIT_API_KEY="your-api-key"\n' +
22
- 'Get your API key at: https://rapidapi.com/delimit/api/schema-diff'
23
- );
24
- }
25
-
26
- // Use RapidAPI header format
27
- config.headers['X-RapidAPI-Key'] = apiKey;
28
- config.headers['X-RapidAPI-Host'] = 'api.delimit.ai';
29
-
30
- return config;
31
- },
32
- (error) => {
33
- return Promise.reject(error);
34
- }
35
- );
36
-
37
- // Response interceptor for error handling
38
- delimitApi.interceptors.response.use(
39
- (response) => response,
40
- (error) => {
41
- // Enhance error messages
42
- if (error.response) {
43
- // Server responded with error
44
- const status = error.response.status;
45
- const data = error.response.data;
46
-
47
- if (status === 401) {
48
- error.message = 'Invalid API key. Please check your DELIMIT_API_KEY.';
49
- } else if (status === 429) {
50
- error.message = 'Rate limit exceeded. Please try again later.';
51
- } else if (status === 422) {
52
- error.message = 'Invalid OpenAPI specification format.';
53
- if (data?.error?.message) {
54
- error.message += `\n${data.error.message}`;
55
- }
56
- } else if (status >= 500) {
57
- error.message = 'Delimit API server error. Please try again later.';
58
- } else if (data?.error?.message) {
59
- error.message = data.error.message;
60
- }
61
- } else if (error.request) {
62
- // Request was made but no response
63
- error.message = 'Could not connect to Delimit API. Please check your internet connection.';
64
- }
65
-
66
- return Promise.reject(error);
67
- }
68
- );
package/src/utils/file.ts DELETED
@@ -1,71 +0,0 @@
1
- import fs from 'fs';
2
- import path from 'path';
3
- import yaml from 'js-yaml';
4
-
5
- export function readOpenApiFile(filepath: string): any {
6
- // Resolve to absolute path
7
- const absolutePath = path.resolve(filepath);
8
-
9
- if (!fs.existsSync(absolutePath)) {
10
- throw new Error(`File not found: ${absolutePath}`);
11
- }
12
-
13
- const stats = fs.statSync(absolutePath);
14
- if (!stats.isFile()) {
15
- throw new Error(`Path is not a file: ${absolutePath}`);
16
- }
17
-
18
- // Check file size (limit to 10MB for safety)
19
- const maxSize = 10 * 1024 * 1024; // 10MB
20
- if (stats.size > maxSize) {
21
- throw new Error(`File too large (${Math.round(stats.size / 1024 / 1024)}MB). Maximum size is 10MB.`);
22
- }
23
-
24
- const content = fs.readFileSync(absolutePath, 'utf8');
25
-
26
- // Determine file type by extension
27
- const ext = path.extname(absolutePath).toLowerCase();
28
-
29
- try {
30
- if (ext === '.json') {
31
- return JSON.parse(content);
32
- } else if (ext === '.yaml' || ext === '.yml') {
33
- return yaml.load(content);
34
- } else {
35
- // Try to parse as YAML first (it can handle JSON too)
36
- try {
37
- return yaml.load(content);
38
- } catch {
39
- // Fall back to JSON
40
- return JSON.parse(content);
41
- }
42
- }
43
- } catch (err: any) {
44
- throw new Error(
45
- `Failed to parse file as ${ext === '.json' ? 'JSON' : 'YAML'}.\n` +
46
- `Please ensure the file is valid OpenAPI specification.\n` +
47
- `Parser error: ${err.message}`
48
- );
49
- }
50
- }
51
-
52
- export function getFileInfo(filepath: string): {
53
- name: string;
54
- size: string;
55
- extension: string;
56
- } {
57
- const absolutePath = path.resolve(filepath);
58
- const stats = fs.statSync(absolutePath);
59
-
60
- return {
61
- name: path.basename(absolutePath),
62
- size: formatFileSize(stats.size),
63
- extension: path.extname(absolutePath).toLowerCase()
64
- };
65
- }
66
-
67
- function formatFileSize(bytes: number): string {
68
- if (bytes < 1024) return `${bytes} bytes`;
69
- if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
70
- return `${(bytes / 1024 / 1024).toFixed(1)} MB`;
71
- }
@@ -1,27 +0,0 @@
1
- import chalk from 'chalk';
2
-
3
- export const logger = {
4
- info: (msg: string) => console.log(chalk.blue('ℹ'), msg),
5
- success: (msg: string) => console.log(chalk.green('✔'), chalk.green(msg)),
6
- error: (msg: string) => console.log(chalk.red('✖'), chalk.red(msg)),
7
- warn: (msg: string) => console.log(chalk.yellow('⚠'), chalk.yellow(msg)),
8
- title: (msg: string) => console.log('\n' + chalk.bold.magenta(msg) + '\n'),
9
-
10
- // Additional helpers for validation output
11
- validationError: (path: string, message: string) => {
12
- console.log(chalk.red(` ✖ ${path}: ${message}`));
13
- },
14
-
15
- validationWarning: (path: string, message: string) => {
16
- console.log(chalk.yellow(` ⚠ ${path}: ${message}`));
17
- },
18
-
19
- dim: (msg: string) => console.log(chalk.dim(msg)),
20
-
21
- // Progress indicators
22
- loading: (msg: string) => console.log(chalk.cyan('⟳'), chalk.cyan(msg)),
23
-
24
- // Formatting helpers
25
- bold: (msg: string) => chalk.bold(msg),
26
- underline: (msg: string) => chalk.underline(msg),
27
- };