@ex-machina/opencode-anthropic-auth 1.4.0 → 1.5.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.
package/README.md CHANGED
@@ -21,7 +21,7 @@ Add the plugin to your OpenCode configuration:
21
21
 
22
22
  ```json
23
23
  {
24
- "plugin": ["@ex-machina/opencode-anthropic-auth@1.3.0"]
24
+ "plugin": ["@ex-machina/opencode-anthropic-auth@1.4.0"]
25
25
  }
26
26
  ```
27
27
 
package/dist/cch.d.ts ADDED
@@ -0,0 +1,24 @@
1
+ type Message = {
2
+ role?: string;
3
+ content?: string | Array<{
4
+ type?: string;
5
+ text?: string;
6
+ }>;
7
+ };
8
+ /**
9
+ * Extract text from the first user message's first text block.
10
+ */
11
+ export declare function extractFirstUserMessageText(messages: Message[]): string;
12
+ /**
13
+ * Compute cch: first 5 hex characters of SHA-256(messageText).
14
+ */
15
+ export declare function computeCCH(messageText: string): string;
16
+ /**
17
+ * Compute the 3-char version suffix from the sampled message characters.
18
+ */
19
+ export declare function computeVersionSuffix(messageText: string, version?: string): string;
20
+ /**
21
+ * Build the complete billing header string for insertion into system[0].
22
+ */
23
+ export declare function buildBillingHeaderValue(messages: Message[], version: string | undefined, entrypoint: string): string;
24
+ export {};
package/dist/cch.js ADDED
@@ -0,0 +1,47 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { CCH_POSITIONS, CCH_SALT, CLAUDE_CODE_VERSION } from './constants';
3
+ /**
4
+ * Extract text from the first user message's first text block.
5
+ */
6
+ export function extractFirstUserMessageText(messages) {
7
+ const userMsg = messages.find((message) => message.role === 'user');
8
+ if (!userMsg)
9
+ return '';
10
+ const { content } = userMsg;
11
+ if (typeof content === 'string')
12
+ return content;
13
+ if (Array.isArray(content)) {
14
+ const textBlock = content.find((block) => block.type === 'text');
15
+ if (textBlock?.text)
16
+ return textBlock.text;
17
+ }
18
+ return '';
19
+ }
20
+ /**
21
+ * Compute cch: first 5 hex characters of SHA-256(messageText).
22
+ */
23
+ export function computeCCH(messageText) {
24
+ return createHash('sha256').update(messageText).digest('hex').slice(0, 5);
25
+ }
26
+ /**
27
+ * Compute the 3-char version suffix from the sampled message characters.
28
+ */
29
+ export function computeVersionSuffix(messageText, version = CLAUDE_CODE_VERSION) {
30
+ const chars = CCH_POSITIONS.map((index) => messageText[index] || '0').join('');
31
+ return createHash('sha256')
32
+ .update(`${CCH_SALT}${chars}${version}`)
33
+ .digest('hex')
34
+ .slice(0, 3);
35
+ }
36
+ /**
37
+ * Build the complete billing header string for insertion into system[0].
38
+ */
39
+ export function buildBillingHeaderValue(messages, version = CLAUDE_CODE_VERSION, entrypoint) {
40
+ const text = extractFirstUserMessageText(messages);
41
+ const suffix = computeVersionSuffix(text, version);
42
+ const cch = computeCCH(text);
43
+ return ('x-anthropic-billing-header: ' +
44
+ `cc_version=${version}.${suffix}; ` +
45
+ `cc_entrypoint=${entrypoint}; ` +
46
+ `cch=${cch};`);
47
+ }
@@ -10,7 +10,11 @@ export declare const TOOL_PREFIX = "mcp_";
10
10
  export declare const REQUIRED_BETAS: string[];
11
11
  export declare const OPENCODE_IDENTITY = "You are OpenCode, the best coding agent on the planet.";
12
12
  export declare const CLAUDE_CODE_IDENTITY = "You are a Claude agent, built on Anthropic's Claude Agent SDK.";
13
- export declare const USER_AGENT = "claude-cli/2.1.2 (external, cli)";
13
+ export declare const CCH_SALT = "59cf53e54c78";
14
+ export declare const CCH_POSITIONS: number[];
15
+ export declare const CLAUDE_CODE_VERSION = "2.1.87";
16
+ export declare const CLAUDE_CODE_ENTRYPOINT = "sdk-cli";
17
+ export declare const USER_AGENT = "claude-cli/2.1.87 (external, cli)";
14
18
  /**
15
19
  * Anchors that identify paragraphs to remove from the system prompt.
16
20
  * Any paragraph (text between blank lines) containing one of these
package/dist/constants.js CHANGED
@@ -20,7 +20,11 @@ export const REQUIRED_BETAS = [
20
20
  ];
21
21
  export const OPENCODE_IDENTITY = 'You are OpenCode, the best coding agent on the planet.';
22
22
  export const CLAUDE_CODE_IDENTITY = "You are a Claude agent, built on Anthropic's Claude Agent SDK.";
23
- export const USER_AGENT = 'claude-cli/2.1.2 (external, cli)';
23
+ export const CCH_SALT = '59cf53e54c78';
24
+ export const CCH_POSITIONS = [4, 7, 20];
25
+ export const CLAUDE_CODE_VERSION = '2.1.87';
26
+ export const CLAUDE_CODE_ENTRYPOINT = 'sdk-cli';
27
+ export const USER_AGENT = 'claude-cli/2.1.87 (external, cli)';
24
28
  /**
25
29
  * Anchors that identify paragraphs to remove from the system prompt.
26
30
  * Any paragraph (text between blank lines) containing one of these
package/dist/transform.js CHANGED
@@ -1,4 +1,5 @@
1
- import { CLAUDE_CODE_IDENTITY, OPENCODE_IDENTITY, PARAGRAPH_REMOVAL_ANCHORS, REQUIRED_BETAS, TEXT_REPLACEMENTS, TOOL_PREFIX, USER_AGENT, } from './constants';
1
+ import { buildBillingHeaderValue } from './cch';
2
+ import { CLAUDE_CODE_ENTRYPOINT, CLAUDE_CODE_IDENTITY, OPENCODE_IDENTITY, PARAGRAPH_REMOVAL_ANCHORS, REQUIRED_BETAS, TEXT_REPLACEMENTS, TOOL_PREFIX, USER_AGENT, } from './constants';
2
3
  /**
3
4
  * Merge headers from a Request object and/or a RequestInit headers value
4
5
  * into a single Headers instance.
@@ -265,6 +266,10 @@ export function prependClaudeCodeIdentity(system) {
265
266
  export function rewriteRequestBody(body) {
266
267
  try {
267
268
  const parsed = JSON.parse(body);
269
+ const billingHeader = Array.isArray(parsed.messages) &&
270
+ parsed.messages.some((message) => message.role === 'user')
271
+ ? buildBillingHeaderValue(parsed.messages, undefined, CLAUDE_CODE_ENTRYPOINT)
272
+ : null;
268
273
  // Sanitize system prompt and prepend Claude Code identity
269
274
  parsed.system = prependClaudeCodeIdentity(parsed.system);
270
275
  // --- Relocate non-core system entries to user messages ---
@@ -295,6 +300,12 @@ export function rewriteRequestBody(body) {
295
300
  }
296
301
  }
297
302
  }
303
+ const identityBlock = parsed.system[0];
304
+ if (billingHeader &&
305
+ identityBlock?.type === 'text' &&
306
+ identityBlock.text === CLAUDE_CODE_IDENTITY) {
307
+ identityBlock.text = `${billingHeader}\n\n${CLAUDE_CODE_IDENTITY}`;
308
+ }
298
309
  // Prefix tool names
299
310
  if (parsed.tools && Array.isArray(parsed.tools)) {
300
311
  parsed.tools = parsed.tools.map((tool) => ({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ex-machina/opencode-anthropic-auth",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/ex-machina-co/opencode-anthropic-auth"
@@ -18,21 +18,21 @@
18
18
  "extract": "bun scripts/extract-system-prompt.ts",
19
19
  "test": "bun test",
20
20
  "types": "tsc",
21
- "format": "biome format --write .",
21
+ "format": "biome check --write --unsafe",
22
22
  "format:check": "biome format .",
23
23
  "lint": "biome lint .",
24
24
  "change": "changeset",
25
25
  "release": "bun run build && bun change publish"
26
26
  },
27
27
  "devDependencies": {
28
- "@biomejs/biome": "2.4.10",
28
+ "@biomejs/biome": "2.4.11",
29
29
  "@changesets/changelog-github": "^0.6.0",
30
30
  "@changesets/cli": "^2.30.0",
31
- "@opencode-ai/plugin": "1.3.13",
31
+ "@opencode-ai/plugin": "1.4.3",
32
32
  "@tsconfig/bun": "1.0.10",
33
33
  "@types/bun": "1.3.11",
34
34
  "dedent": "^1.7.2",
35
- "lefthook": "2.1.4",
35
+ "lefthook": "2.1.5",
36
36
  "typescript": "6.0.2"
37
37
  }
38
38
  }