opencode-cursor-proxy 1.0.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.
- package/LICENSE +21 -0
- package/README.md +139 -0
- package/README.zh-CN.md +136 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/api/agent-service.d.ts +136 -0
- package/dist/lib/api/agent-service.js +938 -0
- package/dist/lib/api/agent-service.js.map +1 -0
- package/dist/lib/api/ai-service.d.ts +26 -0
- package/dist/lib/api/ai-service.js +38 -0
- package/dist/lib/api/ai-service.js.map +1 -0
- package/dist/lib/api/cursor-client.d.ts +119 -0
- package/dist/lib/api/cursor-client.js +511 -0
- package/dist/lib/api/cursor-client.js.map +1 -0
- package/dist/lib/api/cursor-models.d.ts +13 -0
- package/dist/lib/api/cursor-models.js +34 -0
- package/dist/lib/api/cursor-models.js.map +1 -0
- package/dist/lib/api/openai-compat.d.ts +10 -0
- package/dist/lib/api/openai-compat.js +262 -0
- package/dist/lib/api/openai-compat.js.map +1 -0
- package/dist/lib/api/proto/agent-messages.d.ts +25 -0
- package/dist/lib/api/proto/agent-messages.js +132 -0
- package/dist/lib/api/proto/agent-messages.js.map +1 -0
- package/dist/lib/api/proto/bidi.d.ts +17 -0
- package/dist/lib/api/proto/bidi.js +24 -0
- package/dist/lib/api/proto/bidi.js.map +1 -0
- package/dist/lib/api/proto/decoding.d.ts +19 -0
- package/dist/lib/api/proto/decoding.js +118 -0
- package/dist/lib/api/proto/decoding.js.map +1 -0
- package/dist/lib/api/proto/encoding.d.ts +64 -0
- package/dist/lib/api/proto/encoding.js +180 -0
- package/dist/lib/api/proto/encoding.js.map +1 -0
- package/dist/lib/api/proto/exec.d.ts +12 -0
- package/dist/lib/api/proto/exec.js +383 -0
- package/dist/lib/api/proto/exec.js.map +1 -0
- package/dist/lib/api/proto/index.d.ts +13 -0
- package/dist/lib/api/proto/index.js +10 -0
- package/dist/lib/api/proto/index.js.map +1 -0
- package/dist/lib/api/proto/interaction.d.ts +15 -0
- package/dist/lib/api/proto/interaction.js +99 -0
- package/dist/lib/api/proto/interaction.js.map +1 -0
- package/dist/lib/api/proto/kv.d.ts +52 -0
- package/dist/lib/api/proto/kv.js +156 -0
- package/dist/lib/api/proto/kv.js.map +1 -0
- package/dist/lib/api/proto/tool-calls.d.ts +9 -0
- package/dist/lib/api/proto/tool-calls.js +144 -0
- package/dist/lib/api/proto/tool-calls.js.map +1 -0
- package/dist/lib/api/proto/types.d.ts +201 -0
- package/dist/lib/api/proto/types.js +10 -0
- package/dist/lib/api/proto/types.js.map +1 -0
- package/dist/lib/auth/helpers.d.ts +40 -0
- package/dist/lib/auth/helpers.js +103 -0
- package/dist/lib/auth/helpers.js.map +1 -0
- package/dist/lib/auth/index.d.ts +7 -0
- package/dist/lib/auth/index.js +10 -0
- package/dist/lib/auth/index.js.map +1 -0
- package/dist/lib/auth/login.d.ts +55 -0
- package/dist/lib/auth/login.js +184 -0
- package/dist/lib/auth/login.js.map +1 -0
- package/dist/lib/config.d.ts +153 -0
- package/dist/lib/config.js +182 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/openai-compat/handler.d.ts +40 -0
- package/dist/lib/openai-compat/handler.js +808 -0
- package/dist/lib/openai-compat/handler.js.map +1 -0
- package/dist/lib/openai-compat/index.d.ts +9 -0
- package/dist/lib/openai-compat/index.js +13 -0
- package/dist/lib/openai-compat/index.js.map +1 -0
- package/dist/lib/openai-compat/types.d.ts +127 -0
- package/dist/lib/openai-compat/types.js +6 -0
- package/dist/lib/openai-compat/types.js.map +1 -0
- package/dist/lib/openai-compat/utils.d.ts +143 -0
- package/dist/lib/openai-compat/utils.js +348 -0
- package/dist/lib/openai-compat/utils.js.map +1 -0
- package/dist/lib/session-reuse.d.ts +88 -0
- package/dist/lib/session-reuse.js +198 -0
- package/dist/lib/session-reuse.js.map +1 -0
- package/dist/lib/storage.d.ts +55 -0
- package/dist/lib/storage.js +159 -0
- package/dist/lib/storage.js.map +1 -0
- package/dist/lib/utils/cache.d.ts +131 -0
- package/dist/lib/utils/cache.js +297 -0
- package/dist/lib/utils/cache.js.map +1 -0
- package/dist/lib/utils/fetch.d.ts +84 -0
- package/dist/lib/utils/fetch.js +261 -0
- package/dist/lib/utils/fetch.js.map +1 -0
- package/dist/lib/utils/index.d.ts +13 -0
- package/dist/lib/utils/index.js +22 -0
- package/dist/lib/utils/index.js.map +1 -0
- package/dist/lib/utils/jwt.d.ts +40 -0
- package/dist/lib/utils/jwt.js +102 -0
- package/dist/lib/utils/jwt.js.map +1 -0
- package/dist/lib/utils/logger.d.ts +107 -0
- package/dist/lib/utils/logger.js +227 -0
- package/dist/lib/utils/logger.js.map +1 -0
- package/dist/lib/utils/model-resolver.d.ts +49 -0
- package/dist/lib/utils/model-resolver.js +503 -0
- package/dist/lib/utils/model-resolver.js.map +1 -0
- package/dist/lib/utils/request-pool.d.ts +38 -0
- package/dist/lib/utils/request-pool.js +105 -0
- package/dist/lib/utils/request-pool.js.map +1 -0
- package/dist/lib/utils/request-transformer.d.ts +87 -0
- package/dist/lib/utils/request-transformer.js +154 -0
- package/dist/lib/utils/request-transformer.js.map +1 -0
- package/dist/lib/utils/tokenizer.d.ts +14 -0
- package/dist/lib/utils/tokenizer.js +76 -0
- package/dist/lib/utils/tokenizer.js.map +1 -0
- package/dist/plugin/index.d.ts +8 -0
- package/dist/plugin/index.js +9 -0
- package/dist/plugin/index.js.map +1 -0
- package/dist/plugin/plugin.d.ts +21 -0
- package/dist/plugin/plugin.js +309 -0
- package/dist/plugin/plugin.js.map +1 -0
- package/dist/plugin/types.d.ts +120 -0
- package/dist/plugin/types.js +7 -0
- package/dist/plugin/types.js.map +1 -0
- package/dist/server.d.ts +15 -0
- package/dist/server.js +95 -0
- package/dist/server.js.map +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request Transformer
|
|
3
|
+
*
|
|
4
|
+
* Transforms incoming OpenAI-compatible requests for Cursor API compatibility.
|
|
5
|
+
* Based on techniques from opencode-openai-codex-auth project:
|
|
6
|
+
* - Filters AI SDK-only constructs (item_reference)
|
|
7
|
+
* - Strips message IDs for stateless mode
|
|
8
|
+
* - Handles store:false requirements
|
|
9
|
+
*/
|
|
10
|
+
import type { OpenAIMessage, OpenAIMessageContent } from "../openai-compat/types";
|
|
11
|
+
/**
|
|
12
|
+
* Extended message type that includes optional fields that may need filtering
|
|
13
|
+
*/
|
|
14
|
+
export interface ExtendedMessage extends OpenAIMessage {
|
|
15
|
+
id?: string;
|
|
16
|
+
type?: string;
|
|
17
|
+
item_reference?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Result of request transformation
|
|
21
|
+
*/
|
|
22
|
+
export interface TransformResult {
|
|
23
|
+
/** Transformed messages */
|
|
24
|
+
messages: OpenAIMessage[];
|
|
25
|
+
/** Statistics about the transformation */
|
|
26
|
+
stats: TransformStats;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Statistics about the transformation process
|
|
30
|
+
*/
|
|
31
|
+
export interface TransformStats {
|
|
32
|
+
/** Original message count */
|
|
33
|
+
originalCount: number;
|
|
34
|
+
/** Final message count after filtering */
|
|
35
|
+
filteredCount: number;
|
|
36
|
+
/** Number of item_reference entries filtered */
|
|
37
|
+
itemReferencesFiltered: number;
|
|
38
|
+
/** Number of message IDs stripped */
|
|
39
|
+
idsStripped: number;
|
|
40
|
+
/** IDs that were stripped (for debugging) */
|
|
41
|
+
strippedIds: string[];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Filter and transform messages for Cursor API compatibility
|
|
45
|
+
*
|
|
46
|
+
* Key transformations:
|
|
47
|
+
* 1. Filter out item_reference type messages (AI SDK construct for server state lookup)
|
|
48
|
+
* 2. Strip message IDs (causes "item not found" errors in stateless mode)
|
|
49
|
+
* 3. Preserve all other message content
|
|
50
|
+
*
|
|
51
|
+
* @param messages - Original messages from the request
|
|
52
|
+
* @param options - Transformation options
|
|
53
|
+
* @returns Transformed messages and statistics
|
|
54
|
+
*/
|
|
55
|
+
export declare function transformMessages(messages: ExtendedMessage[], options?: {
|
|
56
|
+
logStats?: boolean;
|
|
57
|
+
}): TransformResult;
|
|
58
|
+
/**
|
|
59
|
+
* Log transformation statistics for debugging
|
|
60
|
+
*/
|
|
61
|
+
export declare function logTransformStats(stats: TransformStats): void;
|
|
62
|
+
/**
|
|
63
|
+
* Check if a message contains multimodal content
|
|
64
|
+
*/
|
|
65
|
+
export declare function hasMultimodalContent(content: OpenAIMessageContent): boolean;
|
|
66
|
+
/**
|
|
67
|
+
* Extract text content from potentially multimodal content
|
|
68
|
+
*/
|
|
69
|
+
export declare function extractTextContent(content: OpenAIMessageContent): string;
|
|
70
|
+
/**
|
|
71
|
+
* Extract image content from multimodal content
|
|
72
|
+
*/
|
|
73
|
+
export declare function extractImageContent(content: OpenAIMessageContent): Array<{
|
|
74
|
+
url: string;
|
|
75
|
+
detail?: "auto" | "low" | "high";
|
|
76
|
+
}>;
|
|
77
|
+
/**
|
|
78
|
+
* Validate that messages are compatible with target model capabilities
|
|
79
|
+
*
|
|
80
|
+
* @param messages - Messages to validate
|
|
81
|
+
* @param supportsVision - Whether the model supports vision/images
|
|
82
|
+
* @returns Validation result with any warnings
|
|
83
|
+
*/
|
|
84
|
+
export declare function validateMessagesForModel(messages: OpenAIMessage[], supportsVision: boolean): {
|
|
85
|
+
valid: boolean;
|
|
86
|
+
warnings: string[];
|
|
87
|
+
};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Request Transformer
|
|
3
|
+
*
|
|
4
|
+
* Transforms incoming OpenAI-compatible requests for Cursor API compatibility.
|
|
5
|
+
* Based on techniques from opencode-openai-codex-auth project:
|
|
6
|
+
* - Filters AI SDK-only constructs (item_reference)
|
|
7
|
+
* - Strips message IDs for stateless mode
|
|
8
|
+
* - Handles store:false requirements
|
|
9
|
+
*/
|
|
10
|
+
import { createLogger } from "./logger";
|
|
11
|
+
const logger = createLogger("request-transformer");
|
|
12
|
+
/**
|
|
13
|
+
* Filter and transform messages for Cursor API compatibility
|
|
14
|
+
*
|
|
15
|
+
* Key transformations:
|
|
16
|
+
* 1. Filter out item_reference type messages (AI SDK construct for server state lookup)
|
|
17
|
+
* 2. Strip message IDs (causes "item not found" errors in stateless mode)
|
|
18
|
+
* 3. Preserve all other message content
|
|
19
|
+
*
|
|
20
|
+
* @param messages - Original messages from the request
|
|
21
|
+
* @param options - Transformation options
|
|
22
|
+
* @returns Transformed messages and statistics
|
|
23
|
+
*/
|
|
24
|
+
export function transformMessages(messages, options = {}) {
|
|
25
|
+
const stats = {
|
|
26
|
+
originalCount: messages.length,
|
|
27
|
+
filteredCount: 0,
|
|
28
|
+
itemReferencesFiltered: 0,
|
|
29
|
+
idsStripped: 0,
|
|
30
|
+
strippedIds: [],
|
|
31
|
+
};
|
|
32
|
+
const transformed = [];
|
|
33
|
+
for (const message of messages) {
|
|
34
|
+
// Filter out item_reference type messages
|
|
35
|
+
// These are AI SDK constructs for server state lookup (not in Cursor API spec)
|
|
36
|
+
if (message.type === "item_reference") {
|
|
37
|
+
stats.itemReferencesFiltered++;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
// Create a clean copy without internal fields
|
|
41
|
+
const cleanMessage = {
|
|
42
|
+
role: message.role,
|
|
43
|
+
content: message.content,
|
|
44
|
+
};
|
|
45
|
+
// Track and strip message IDs
|
|
46
|
+
// In stateless mode, IDs cause "item not found" errors
|
|
47
|
+
if (message.id) {
|
|
48
|
+
stats.idsStripped++;
|
|
49
|
+
stats.strippedIds.push(message.id);
|
|
50
|
+
}
|
|
51
|
+
// Preserve tool_calls for assistant messages
|
|
52
|
+
if (message.tool_calls && message.tool_calls.length > 0) {
|
|
53
|
+
cleanMessage.tool_calls = message.tool_calls;
|
|
54
|
+
}
|
|
55
|
+
// Preserve tool_call_id for tool response messages
|
|
56
|
+
if (message.tool_call_id) {
|
|
57
|
+
cleanMessage.tool_call_id = message.tool_call_id;
|
|
58
|
+
}
|
|
59
|
+
transformed.push(cleanMessage);
|
|
60
|
+
}
|
|
61
|
+
stats.filteredCount = transformed.length;
|
|
62
|
+
// Log transformation stats if enabled
|
|
63
|
+
if (options.logStats) {
|
|
64
|
+
logTransformStats(stats);
|
|
65
|
+
}
|
|
66
|
+
return { messages: transformed, stats };
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Log transformation statistics for debugging
|
|
70
|
+
*/
|
|
71
|
+
export function logTransformStats(stats) {
|
|
72
|
+
const filtered = stats.originalCount - stats.filteredCount;
|
|
73
|
+
if (filtered > 0 || stats.idsStripped > 0) {
|
|
74
|
+
logger.debug(`[Request Transform] Processed ${stats.originalCount} messages: ` +
|
|
75
|
+
`filtered ${stats.itemReferencesFiltered} item_reference(s), ` +
|
|
76
|
+
`stripped ${stats.idsStripped} ID(s)`);
|
|
77
|
+
if (stats.strippedIds.length > 0 && stats.strippedIds.length <= 10) {
|
|
78
|
+
logger.debug(`[Request Transform] Stripped IDs: ${stats.strippedIds.join(", ")}`);
|
|
79
|
+
}
|
|
80
|
+
else if (stats.strippedIds.length > 10) {
|
|
81
|
+
logger.debug(`[Request Transform] Stripped IDs: ${stats.strippedIds.slice(0, 10).join(", ")} ` +
|
|
82
|
+
`... and ${stats.strippedIds.length - 10} more`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Check if a message contains multimodal content
|
|
88
|
+
*/
|
|
89
|
+
export function hasMultimodalContent(content) {
|
|
90
|
+
if (typeof content === "string" || content === null) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
return content.some((part) => part.type === "image_url");
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Extract text content from potentially multimodal content
|
|
97
|
+
*/
|
|
98
|
+
export function extractTextContent(content) {
|
|
99
|
+
if (content === null) {
|
|
100
|
+
return "";
|
|
101
|
+
}
|
|
102
|
+
if (typeof content === "string") {
|
|
103
|
+
return content;
|
|
104
|
+
}
|
|
105
|
+
// Handle array of content parts
|
|
106
|
+
const textParts = content
|
|
107
|
+
.filter((part) => part.type === "text")
|
|
108
|
+
.map((part) => part.text);
|
|
109
|
+
return textParts.join("\n");
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Extract image content from multimodal content
|
|
113
|
+
*/
|
|
114
|
+
export function extractImageContent(content) {
|
|
115
|
+
if (content === null || typeof content === "string") {
|
|
116
|
+
return [];
|
|
117
|
+
}
|
|
118
|
+
return content
|
|
119
|
+
.filter((part) => part.type === "image_url")
|
|
120
|
+
.map((part) => {
|
|
121
|
+
const imagePart = part;
|
|
122
|
+
return {
|
|
123
|
+
url: imagePart.image_url.url,
|
|
124
|
+
detail: imagePart.image_url.detail,
|
|
125
|
+
};
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Validate that messages are compatible with target model capabilities
|
|
130
|
+
*
|
|
131
|
+
* @param messages - Messages to validate
|
|
132
|
+
* @param supportsVision - Whether the model supports vision/images
|
|
133
|
+
* @returns Validation result with any warnings
|
|
134
|
+
*/
|
|
135
|
+
export function validateMessagesForModel(messages, supportsVision) {
|
|
136
|
+
const warnings = [];
|
|
137
|
+
for (let i = 0; i < messages.length; i++) {
|
|
138
|
+
const message = messages[i];
|
|
139
|
+
// Skip if message is undefined (shouldn't happen, but TypeScript safety)
|
|
140
|
+
if (!message) {
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
// Check for image content in non-vision models
|
|
144
|
+
if (!supportsVision && hasMultimodalContent(message.content)) {
|
|
145
|
+
warnings.push(`Message ${i + 1} contains image content but the model does not support vision. ` +
|
|
146
|
+
`Images will be ignored.`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return {
|
|
150
|
+
valid: warnings.length === 0,
|
|
151
|
+
warnings,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
//# sourceMappingURL=request-transformer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-transformer.js","sourceRoot":"","sources":["../../../src/lib/utils/request-transformer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
export type ModelProvider = "openai" | "anthropic" | "gemini" | "unknown";
|
|
2
|
+
export declare function detectModelProvider(model: string): ModelProvider;
|
|
3
|
+
export declare function countOpenAITokens(text: string): number;
|
|
4
|
+
export declare function countAnthropicTokens(text: string): number;
|
|
5
|
+
export declare function countGeminiTokens(text: string): number;
|
|
6
|
+
export declare function estimateTokens(text: string): number;
|
|
7
|
+
export declare function countTokens(text: string, model: string): number;
|
|
8
|
+
export declare function isWithinTokenLimit(text: string, limit: number, model: string): number | false;
|
|
9
|
+
export interface TokenUsage {
|
|
10
|
+
prompt_tokens: number;
|
|
11
|
+
completion_tokens: number;
|
|
12
|
+
total_tokens: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function calculateTokenUsage(prompt: string, completion: string, model: string): TokenUsage;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { encode as encodeGpt, isWithinTokenLimit as gptIsWithinLimit } from "gpt-tokenizer";
|
|
2
|
+
import { countTokens as countClaudeTokens } from "@anthropic-ai/tokenizer";
|
|
3
|
+
const OPENAI_PATTERNS = [
|
|
4
|
+
/^gpt-/i,
|
|
5
|
+
/^o[1-4]-/i,
|
|
6
|
+
/^text-davinci/i,
|
|
7
|
+
/^davinci/i,
|
|
8
|
+
/^curie/i,
|
|
9
|
+
/^babbage/i,
|
|
10
|
+
/^ada/i,
|
|
11
|
+
];
|
|
12
|
+
const ANTHROPIC_PATTERNS = [
|
|
13
|
+
/claude/i,
|
|
14
|
+
/sonnet/i,
|
|
15
|
+
/opus/i,
|
|
16
|
+
/haiku/i,
|
|
17
|
+
];
|
|
18
|
+
const GEMINI_PATTERNS = [
|
|
19
|
+
/gemini/i,
|
|
20
|
+
];
|
|
21
|
+
export function detectModelProvider(model) {
|
|
22
|
+
const normalizedModel = model.toLowerCase();
|
|
23
|
+
if (ANTHROPIC_PATTERNS.some(p => p.test(normalizedModel))) {
|
|
24
|
+
return "anthropic";
|
|
25
|
+
}
|
|
26
|
+
if (OPENAI_PATTERNS.some(p => p.test(normalizedModel))) {
|
|
27
|
+
return "openai";
|
|
28
|
+
}
|
|
29
|
+
if (GEMINI_PATTERNS.some(p => p.test(normalizedModel))) {
|
|
30
|
+
return "gemini";
|
|
31
|
+
}
|
|
32
|
+
return "unknown";
|
|
33
|
+
}
|
|
34
|
+
export function countOpenAITokens(text) {
|
|
35
|
+
return encodeGpt(text).length;
|
|
36
|
+
}
|
|
37
|
+
export function countAnthropicTokens(text) {
|
|
38
|
+
return countClaudeTokens(text);
|
|
39
|
+
}
|
|
40
|
+
export function countGeminiTokens(text) {
|
|
41
|
+
return encodeGpt(text).length;
|
|
42
|
+
}
|
|
43
|
+
export function estimateTokens(text) {
|
|
44
|
+
return Math.ceil(text.length / 4);
|
|
45
|
+
}
|
|
46
|
+
export function countTokens(text, model) {
|
|
47
|
+
const provider = detectModelProvider(model);
|
|
48
|
+
switch (provider) {
|
|
49
|
+
case "openai":
|
|
50
|
+
return countOpenAITokens(text);
|
|
51
|
+
case "anthropic":
|
|
52
|
+
return countAnthropicTokens(text);
|
|
53
|
+
case "gemini":
|
|
54
|
+
return countGeminiTokens(text);
|
|
55
|
+
default:
|
|
56
|
+
return countOpenAITokens(text);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export function isWithinTokenLimit(text, limit, model) {
|
|
60
|
+
const provider = detectModelProvider(model);
|
|
61
|
+
if (provider === "openai" || provider === "gemini") {
|
|
62
|
+
return gptIsWithinLimit(text, limit);
|
|
63
|
+
}
|
|
64
|
+
const count = countTokens(text, model);
|
|
65
|
+
return count <= limit ? count : false;
|
|
66
|
+
}
|
|
67
|
+
export function calculateTokenUsage(prompt, completion, model) {
|
|
68
|
+
const promptTokens = countTokens(prompt, model);
|
|
69
|
+
const completionTokens = countTokens(completion, model);
|
|
70
|
+
return {
|
|
71
|
+
prompt_tokens: promptTokens,
|
|
72
|
+
completion_tokens: completionTokens,
|
|
73
|
+
total_tokens: promptTokens + completionTokens,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=tokenizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tokenizer.js","sourceRoot":"","sources":["../../../src/lib/utils/tokenizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,SAAS,EAAE,kBAAkB,IAAI,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAC5F,OAAO,EAAE,WAAW,IAAI,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAE3E,MAAM,eAAe,GAAG;IACtB,QAAQ;IACR,WAAW;IACX,gBAAgB;IAChB,WAAW;IACX,SAAS;IACT,WAAW;IACX,OAAO;CACR,CAAC;AAEF,MAAM,kBAAkB,GAAG;IACzB,SAAS;IACT,SAAS;IACT,OAAO;IACP,QAAQ;CACT,CAAC;AAEF,MAAM,eAAe,GAAG;IACtB,SAAS;CACV,CAAC;AAIF,MAAM,UAAU,mBAAmB,CAAC,KAAa;IAC/C,MAAM,eAAe,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;IAE5C,IAAI,kBAAkB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,EAAE,CAAC;QAC1D,OAAO,WAAW,CAAC;IACrB,CAAC;IAED,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,EAAE,CAAC;QACvD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,IAAI,eAAe,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,EAAE,CAAC;QACvD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,IAAY;IAC/C,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC;AACjC,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,IAAY;IAC5C,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,IAAY;IACzC,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,IAAY,EAAE,KAAa;IACrD,MAAM,QAAQ,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;IAE5C,QAAQ,QAAQ,EAAE,CAAC;QACjB,KAAK,QAAQ;YACX,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC;QACjC,KAAK,WAAW;YACd,OAAO,oBAAoB,CAAC,IAAI,CAAC,CAAC;QACpC,KAAK,QAAQ;YACX,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC;QACjC;YACE,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC;IACnC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,IAAY,EAAE,KAAa,EAAE,KAAa;IAC3E,MAAM,QAAQ,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;IAE5C,IAAI,QAAQ,KAAK,QAAQ,IAAI,QAAQ,KAAK,QAAQ,EAAE,CAAC;QACnD,OAAO,gBAAgB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACvC,CAAC;IAED,MAAM,KAAK,GAAG,WAAW,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACvC,OAAO,KAAK,IAAI,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;AACxC,CAAC;AAQD,MAAM,UAAU,mBAAmB,CACjC,MAAc,EACd,UAAkB,EAClB,KAAa;IAEb,MAAM,YAAY,GAAG,WAAW,CAAC,MAAM,EAAE,KAAK,CAAC,CAAC;IAChD,MAAM,gBAAgB,GAAG,WAAW,CAAC,UAAU,EAAE,KAAK,CAAC,CAAC;IAExD,OAAO;QACL,aAAa,EAAE,YAAY;QAC3B,iBAAiB,EAAE,gBAAgB;QACnC,YAAY,EAAE,YAAY,GAAG,gBAAgB;KAC9C,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode Cursor Auth Plugin
|
|
3
|
+
*
|
|
4
|
+
* Entry point for the OpenCode plugin interface.
|
|
5
|
+
*/
|
|
6
|
+
export { CursorOAuthPlugin, CURSOR_PROVIDER_ID, } from "./plugin.js";
|
|
7
|
+
export type { PluginContext, PluginResult, PluginClient, GetAuth, Provider, ProviderModel, LoaderResult, AuthDetails, OAuthAuthDetails, ApiKeyAuthDetails, TokenAuthDetails, AuthMethod, OAuthAuthMethod, ApiKeyAuthMethod, TokenExchangeResult, TokenExchangeSuccess, TokenExchangeFailure, CursorAuthRecord, } from "./types";
|
|
8
|
+
export { CursorOAuthPlugin as default } from "./plugin.js";
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode Cursor Auth Plugin
|
|
3
|
+
*
|
|
4
|
+
* Entry point for the OpenCode plugin interface.
|
|
5
|
+
*/
|
|
6
|
+
export { CursorOAuthPlugin, CURSOR_PROVIDER_ID, } from "./plugin.js";
|
|
7
|
+
// Default export is the main plugin factory
|
|
8
|
+
export { CursorOAuthPlugin as default } from "./plugin.js";
|
|
9
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/plugin/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EACL,iBAAiB,EACjB,kBAAkB,GACnB,MAAM,aAAa,CAAC;AAuBrB,4CAA4C;AAC5C,OAAO,EAAE,iBAAiB,IAAI,OAAO,EAAE,MAAM,aAAa,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenCode Cursor Auth Plugin
|
|
3
|
+
*
|
|
4
|
+
* An OpenCode plugin that provides OAuth authentication for Cursor's AI backend,
|
|
5
|
+
* following the architecture established by opencode-gemini-auth.
|
|
6
|
+
*
|
|
7
|
+
* This plugin uses a custom fetch function to intercept OpenAI API requests
|
|
8
|
+
* and route them through Cursor's Agent API.
|
|
9
|
+
*/
|
|
10
|
+
import type { PluginContext, PluginResult } from "./types";
|
|
11
|
+
export declare const CURSOR_PROVIDER_ID = "cursor";
|
|
12
|
+
/**
|
|
13
|
+
* Cursor OAuth Plugin for OpenCode
|
|
14
|
+
*
|
|
15
|
+
* Provides authentication for Cursor's AI backend using:
|
|
16
|
+
* - Browser-based OAuth flow with PKCE
|
|
17
|
+
* - API key authentication
|
|
18
|
+
* - Automatic token refresh
|
|
19
|
+
* - Custom fetch function (no proxy server needed)
|
|
20
|
+
*/
|
|
21
|
+
export declare const CursorOAuthPlugin: ({ client, }: PluginContext) => Promise<PluginResult>;
|