anthropic-gateway 1.0.0

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.
@@ -0,0 +1,107 @@
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.UI = void 0;
7
+ const chalk_1 = __importDefault(require("chalk"));
8
+ const logger_1 = require("./logger");
9
+ // Claude Code Inspired Palette
10
+ const Palette = {
11
+ Brand: '#D97757', // Warm Terracotta (Main Brand)
12
+ Error: '#D95858', // Soft Red
13
+ Warning: '#D9A458', // Mustard Yellow
14
+ Dim: '#6B6B6B', // Dark Gray
15
+ Text: '#E6E6E6', // Off-White
16
+ Border: '#3F3F3F', // Subtle Border
17
+ Highlight: '#A78BFA', // Soft Purple (Files/Links)
18
+ };
19
+ class UI {
20
+ static log(message) {
21
+ logger_1.logger.print(message);
22
+ }
23
+ static info(message) {
24
+ this.log(`${chalk_1.default.hex(Palette.Dim).bold('•')} ${chalk_1.default.hex(Palette.Text)(message)}`);
25
+ }
26
+ static success(message) {
27
+ this.log(`${chalk_1.default.hex(Palette.Brand)('✔')} ${chalk_1.default.hex(Palette.Brand)(message)}`);
28
+ }
29
+ static warning(message) {
30
+ this.log(`${chalk_1.default.hex(Palette.Dim)('⚠')} ${message}`);
31
+ }
32
+ static error(message, error) {
33
+ this.log(`${chalk_1.default.hex(Palette.Dim)('✖')} ${message}`);
34
+ if (error && error.message) {
35
+ this.log(chalk_1.default.hex(Palette.Error)(` ${error.message}`));
36
+ }
37
+ }
38
+ static header(subtitle) {
39
+ this.log('');
40
+ this.log(chalk_1.default.hex(Palette.Dim)(` ${subtitle}`));
41
+ this.log('');
42
+ }
43
+ static status(text) {
44
+ this.log(`${chalk_1.default.hex(Palette.Dim)('•')} ${chalk_1.default.hex(Palette.Text)(text)}`);
45
+ }
46
+ static statusDone(success = true, text) {
47
+ if (success) {
48
+ this.log(`${chalk_1.default.hex(Palette.Dim)('✔')} ${text || ''}`);
49
+ }
50
+ else {
51
+ this.log(`${chalk_1.default.hex(Palette.Dim)('✖')} ${text || ''}`);
52
+ }
53
+ }
54
+ static box(title, content) {
55
+ const border = chalk_1.default.hex(Palette.Border)('──────────────────────────────────────────────────');
56
+ this.log('');
57
+ this.log(border);
58
+ this.log(chalk_1.default.hex(Palette.Brand).bold(` ${title}`));
59
+ this.log(border);
60
+ content.forEach((line) => this.log(` ${line}`));
61
+ this.log(border);
62
+ this.log('');
63
+ }
64
+ static newUrl(url) {
65
+ return chalk_1.default.hex(Palette.Highlight).bold.underline(url);
66
+ }
67
+ static dim(text) {
68
+ return chalk_1.default.hex(Palette.Dim)(text);
69
+ }
70
+ static highlight(text) {
71
+ return chalk_1.default.hex(Palette.Highlight)(text);
72
+ }
73
+ static hint(text) {
74
+ this.log(` ${chalk_1.default.hex(Palette.Dim)(text)}`);
75
+ }
76
+ static banner() {
77
+ const brand = chalk_1.default.hex(Palette.Brand);
78
+ const dim = chalk_1.default.hex(Palette.Dim);
79
+ // USB adapter with CLAUDE text inside
80
+ const art = [
81
+ '',
82
+ dim(' ┌────────────────────┐'),
83
+ dim(' │ ') + brand('┌─┐┬ ┌─┐┬ ┬┌┬┐┌─┐') + dim(' ├──┐'),
84
+ dim(' │ ') + brand('│ │ ├─┤│ │ ││├┤ ') + dim(' │▓▓│'),
85
+ dim(' │ ') + brand('└─┘┴─┘┴ ┴└─┘─┴┘└─┘') + dim(' ├──┘'),
86
+ dim(' └──────•ADAPTER──────┘'),
87
+ '',
88
+ ];
89
+ art.forEach((line) => this.log(line));
90
+ }
91
+ static table(rows) {
92
+ const maxLabelWidth = Math.max(...rows.map((r) => r.label.length));
93
+ this.log('');
94
+ rows.forEach((row) => {
95
+ const paddedLabel = row.label.padEnd(maxLabelWidth);
96
+ this.log(` ${chalk_1.default.hex(Palette.Dim)(paddedLabel)} ${chalk_1.default.hex(Palette.Highlight)(row.value)}`);
97
+ });
98
+ this.log('');
99
+ }
100
+ static updateNotify(current, latest) {
101
+ this.log('');
102
+ this.log(`${chalk_1.default.hex(Palette.Dim)('•')} ${chalk_1.default.hex(Palette.Text)('Update available:')} ${chalk_1.default.hex(Palette.Dim)(current)} ${chalk_1.default.hex(Palette.Dim)('→')} ${chalk_1.default.hex(Palette.Highlight)(latest)}`);
103
+ this.hint('Run "npm i -g claude-adapter" to update');
104
+ }
105
+ }
106
+ exports.UI = UI;
107
+ //# sourceMappingURL=ui.js.map
@@ -0,0 +1,112 @@
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.checkForUpdates = checkForUpdates;
7
+ exports.getCachedUpdateInfo = getCachedUpdateInfo;
8
+ const https_1 = __importDefault(require("https"));
9
+ const package_json_1 = require("../../package.json");
10
+ const metadata_1 = require("./metadata");
11
+ const CACHE_TTL = 24 * 60 * 60 * 1000; // 24 hours in milliseconds
12
+ /**
13
+ * Compare two semantic versions
14
+ * Returns true if latest is greater than current
15
+ */
16
+ function isNewerVersion(latest, current) {
17
+ const parseVersion = (v) => v.split('.').map(n => parseInt(n, 10) || 0);
18
+ const [latestParts, currentParts] = [parseVersion(latest), parseVersion(current)];
19
+ for (let i = 0; i < Math.max(latestParts.length, currentParts.length); i++) {
20
+ const l = latestParts[i] || 0;
21
+ const c = currentParts[i] || 0;
22
+ if (l > c)
23
+ return true;
24
+ if (l < c)
25
+ return false;
26
+ }
27
+ return false;
28
+ }
29
+ /**
30
+ * Check if cached version is still valid (within 24 hours)
31
+ */
32
+ function isCacheValid(timestamp) {
33
+ return Date.now() - timestamp < CACHE_TTL;
34
+ }
35
+ /**
36
+ * Fetch latest version from npm registry
37
+ */
38
+ function fetchLatestVersion() {
39
+ return new Promise((resolve) => {
40
+ const timeout = setTimeout(() => resolve(null), 3000); // 3s timeout
41
+ https_1.default.get('https://registry.npmjs.org/claude-adapter/latest', (res) => {
42
+ let data = '';
43
+ res.on('data', chunk => data += chunk);
44
+ res.on('end', () => {
45
+ clearTimeout(timeout);
46
+ try {
47
+ const { version } = JSON.parse(data);
48
+ resolve(version);
49
+ }
50
+ catch {
51
+ resolve(null);
52
+ }
53
+ });
54
+ }).on('error', () => {
55
+ clearTimeout(timeout);
56
+ resolve(null);
57
+ });
58
+ });
59
+ }
60
+ /**
61
+ * Check for updates with 24-hour caching
62
+ * Non-blocking, fails silently on errors
63
+ */
64
+ async function checkForUpdates() {
65
+ try {
66
+ // Check cache first
67
+ const cache = (0, metadata_1.getCachedLatestVersion)();
68
+ if (cache && isCacheValid(cache.timestamp)) {
69
+ return {
70
+ current: package_json_1.version,
71
+ latest: cache.version,
72
+ hasUpdate: isNewerVersion(cache.version, package_json_1.version)
73
+ };
74
+ }
75
+ // Fetch from registry
76
+ const latest = await fetchLatestVersion();
77
+ if (!latest) {
78
+ return null;
79
+ }
80
+ // Update cache in metadata
81
+ (0, metadata_1.updateLatestVersion)(latest);
82
+ return {
83
+ current: package_json_1.version,
84
+ latest,
85
+ hasUpdate: isNewerVersion(latest, package_json_1.version)
86
+ };
87
+ }
88
+ catch {
89
+ return null;
90
+ }
91
+ }
92
+ /**
93
+ * Get cached update info synchronously (for use in request converter)
94
+ * Returns null if cache doesn't exist or is expired
95
+ */
96
+ function getCachedUpdateInfo() {
97
+ try {
98
+ const cache = (0, metadata_1.getCachedLatestVersion)();
99
+ if (cache && isCacheValid(cache.timestamp)) {
100
+ return {
101
+ current: package_json_1.version,
102
+ latest: cache.version,
103
+ hasUpdate: isNewerVersion(cache.version, package_json_1.version)
104
+ };
105
+ }
106
+ }
107
+ catch {
108
+ // Ignore errors
109
+ }
110
+ return null;
111
+ }
112
+ //# sourceMappingURL=update.js.map
@@ -0,0 +1,130 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateAnthropicRequest = validateAnthropicRequest;
4
+ exports.formatValidationErrors = formatValidationErrors;
5
+ /**
6
+ * Validate an incoming Anthropic Messages API request
7
+ */
8
+ function validateAnthropicRequest(body) {
9
+ const errors = [];
10
+ // Check if body is an object
11
+ if (!body || typeof body !== 'object') {
12
+ return { valid: false, errors: [{ field: 'body', message: 'Request body must be an object' }] };
13
+ }
14
+ const request = body;
15
+ // Required field: model
16
+ if (!request.model || typeof request.model !== 'string') {
17
+ errors.push({ field: 'model', message: 'model is required and must be a string' });
18
+ }
19
+ // Required field: max_tokens
20
+ if (request.max_tokens === undefined || typeof request.max_tokens !== 'number') {
21
+ errors.push({ field: 'max_tokens', message: 'max_tokens is required and must be a number' });
22
+ }
23
+ else if (request.max_tokens <= 0) {
24
+ errors.push({ field: 'max_tokens', message: 'max_tokens must be a positive number' });
25
+ }
26
+ // Required field: messages
27
+ if (!request.messages) {
28
+ errors.push({ field: 'messages', message: 'messages is required' });
29
+ }
30
+ else if (!Array.isArray(request.messages)) {
31
+ errors.push({ field: 'messages', message: 'messages must be an array' });
32
+ }
33
+ else if (request.messages.length === 0) {
34
+ errors.push({ field: 'messages', message: 'messages array cannot be empty' });
35
+ }
36
+ else {
37
+ // Validate each message
38
+ const messageErrors = validateMessages(request.messages);
39
+ errors.push(...messageErrors);
40
+ }
41
+ // Optional field validations
42
+ if (request.temperature !== undefined) {
43
+ if (typeof request.temperature !== 'number' ||
44
+ request.temperature < 0 ||
45
+ request.temperature > 1) {
46
+ errors.push({
47
+ field: 'temperature',
48
+ message: 'temperature must be a number between 0 and 1',
49
+ });
50
+ }
51
+ }
52
+ if (request.top_p !== undefined) {
53
+ if (typeof request.top_p !== 'number' || request.top_p < 0 || request.top_p > 1) {
54
+ errors.push({ field: 'top_p', message: 'top_p must be a number between 0 and 1' });
55
+ }
56
+ }
57
+ if (request.stream !== undefined && typeof request.stream !== 'boolean') {
58
+ errors.push({ field: 'stream', message: 'stream must be a boolean' });
59
+ }
60
+ return { valid: errors.length === 0, errors };
61
+ }
62
+ /**
63
+ * Validate message array structure
64
+ */
65
+ function validateMessages(messages) {
66
+ const errors = [];
67
+ for (let i = 0; i < messages.length; i++) {
68
+ const msg = messages[i];
69
+ if (!msg || typeof msg !== 'object') {
70
+ errors.push({ field: `messages[${i}]`, message: 'message must be an object' });
71
+ continue;
72
+ }
73
+ const message = msg;
74
+ // Validate role - accept any string role for forward compatibility
75
+ // Claude Code may introduce new roles (e.g., from session-start hooks)
76
+ // The converter will map unknown roles to valid OpenAI roles downstream
77
+ if (!message.role || typeof message.role !== 'string') {
78
+ errors.push({
79
+ field: `messages[${i}].role`,
80
+ message: 'role is required and must be a string',
81
+ });
82
+ }
83
+ // Validate content - allow missing/empty content for forward compatibility
84
+ // Some message types (e.g., hook attachments) may have minimal content
85
+ if (message.content !== undefined && message.content !== null) {
86
+ if (typeof message.content !== 'string' && !Array.isArray(message.content)) {
87
+ errors.push({
88
+ field: `messages[${i}].content`,
89
+ message: 'content must be a string or array',
90
+ });
91
+ }
92
+ else if (Array.isArray(message.content)) {
93
+ const contentErrors = validateContentBlocks(message.content, i);
94
+ errors.push(...contentErrors);
95
+ }
96
+ }
97
+ }
98
+ return errors;
99
+ }
100
+ /**
101
+ * Validate content blocks array
102
+ */
103
+ function validateContentBlocks(blocks, messageIndex) {
104
+ const errors = [];
105
+ for (let j = 0; j < blocks.length; j++) {
106
+ const block = blocks[j];
107
+ if (!block || typeof block !== 'object') {
108
+ errors.push({
109
+ field: `messages[${messageIndex}].content[${j}]`,
110
+ message: 'content block must be an object',
111
+ });
112
+ continue;
113
+ }
114
+ const contentBlock = block;
115
+ if (!contentBlock.type || typeof contentBlock.type !== 'string') {
116
+ errors.push({
117
+ field: `messages[${messageIndex}].content[${j}].type`,
118
+ message: 'content block type is required',
119
+ });
120
+ }
121
+ }
122
+ return errors;
123
+ }
124
+ /**
125
+ * Format validation errors into a human-readable message
126
+ */
127
+ function formatValidationErrors(errors) {
128
+ return errors.map((e) => `${e.field}: ${e.message}`).join('; ');
129
+ }
130
+ //# sourceMappingURL=validation.js.map
@@ -0,0 +1,6 @@
1
+ {
2
+ "name": "anthropic-gateway-vendor",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "description": "Vendored conversion core from claude-adapter (MIT) — see vendor/LICENSE.claude-adapter"
6
+ }