@cortexkit/opencode-anthropic-auth 1.1.3 → 1.2.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/README.md CHANGED
@@ -481,6 +481,25 @@ bun run lint
481
481
  bun run format:check
482
482
  ```
483
483
 
484
+ ### Build modes
485
+
486
+ Two build modes are available:
487
+
488
+ ```bash
489
+ bun run build # Deploy: bun build → bundled dist/ with all deps inlined (core + xxhash-wasm)
490
+ bun run build:dev # Dev: tsc → individual dist/*.js files (requires workspace node_modules)
491
+ ```
492
+
493
+ The default `build` uses `bun build` to bundle `@cortexkit/anthropic-auth-core` and all transitive dependencies into self-contained output files. No `node_modules/` needed at runtime — the plugin works via `file://` path in OpenCode config:
494
+
495
+ ```json
496
+ {
497
+ "plugin": ["file:///path/to/anthropic-auth/packages/opencode"]
498
+ }
499
+ ```
500
+
501
+ `@opencode-ai/plugin` remains external (peer dep provided by OpenCode).
502
+
484
503
  Inspect package contents:
485
504
 
486
505
  ```bash
package/dist/cli.js CHANGED
@@ -1,260 +1,12 @@
1
1
  #!/usr/bin/env node
2
- import { stdin as input, stdout as output } from 'node:process';
3
- import { createInterface } from 'node:readline/promises';
4
- import { authorize, exchange, generateRelayToken, getAccountStoragePath, loadAccounts, saveAccounts, WORKER_SCRIPT, } from '@cortexkit/anthropic-auth-core';
5
- function defaultStorage() {
6
- return {
7
- version: 1,
8
- main: { type: 'opencode', provider: 'anthropic' },
9
- fallbackOn: [401, 403, 429],
10
- refresh: {
11
- enabled: true,
12
- intervalMinutes: 10,
13
- refreshBeforeExpiryMinutes: 240,
14
- },
15
- quota: {
16
- enabled: true,
17
- checkIntervalMinutes: 5,
18
- minimumRemaining: {
19
- five_hour: 10,
20
- seven_day: 20,
21
- },
22
- failClosedOnUnknownQuota: true,
23
- },
24
- accounts: [],
25
- };
26
- }
27
- function usage() {
28
- console.log(`Usage:
2
+ import{l as y,m as w,o as l,p as u,q as m,ra as b,ta as k}from"./index-16nba65t.js";import{stdin as A,stdout as $}from"node:process";import{createInterface as R}from"node:readline/promises";function v(){return{version:1,main:{type:"opencode",provider:"anthropic"},fallbackOn:[401,403,429],refresh:{enabled:!0,intervalMinutes:10,refreshBeforeExpiryMinutes:240},quota:{enabled:!0,checkIntervalMinutes:5,minimumRemaining:{five_hour:10,seven_day:20},failClosedOnUnknownQuota:!0},accounts:[]}}function h(){console.log(`Usage:
29
3
  opencode-anthropic-auth login [label]
30
4
  opencode-anthropic-auth list
31
5
  opencode-anthropic-auth relay setup
32
6
 
33
7
  Fallback accounts are stored in:
34
- ${getAccountStoragePath()}`);
35
- }
36
- function requireText(value, name) {
37
- const trimmed = value?.trim();
38
- if (!trimmed)
39
- throw new Error(`${name} is required`);
40
- return trimmed;
41
- }
42
- async function cloudflareRequest(options) {
43
- const response = await fetch(`https://api.cloudflare.com/client/v4${options.path}`, {
44
- method: options.method,
45
- headers: {
46
- authorization: `Bearer ${options.token}`,
47
- ...(options.body instanceof FormData
48
- ? {}
49
- : { 'content-type': 'application/json' }),
50
- ...options.headers,
51
- },
52
- body: options.body,
53
- });
54
- const text = await response.text();
55
- let data;
56
- try {
57
- data = JSON.parse(text);
58
- }
59
- catch {
60
- throw new Error(`Cloudflare API returned ${response.status}: ${text}`);
61
- }
62
- if (!response.ok || data.success === false) {
63
- const message = data.errors
64
- ?.map((error) => error.message)
65
- .filter(Boolean)
66
- .join('; ');
67
- throw new Error(message || `Cloudflare API returned ${response.status}`);
68
- }
69
- return data.result;
70
- }
71
- async function createKvNamespace(token, accountId, title) {
72
- return cloudflareRequest({
73
- token,
74
- method: 'POST',
75
- path: `/accounts/${accountId}/storage/kv/namespaces`,
76
- body: JSON.stringify({ title }),
77
- });
78
- }
79
- async function uploadRelayWorker(options) {
80
- const metadata = {
81
- main_module: 'worker.js',
82
- compatibility_date: '2026-04-28',
83
- bindings: [
84
- {
85
- type: 'kv_namespace',
86
- name: 'RELAY_STATE',
87
- namespace_id: options.kvNamespaceId,
88
- },
89
- {
90
- type: 'secret_text',
91
- name: 'RELAY_TOKEN',
92
- text: options.relayToken,
93
- },
94
- ],
95
- };
96
- const form = new FormData();
97
- form.set('metadata', JSON.stringify(metadata));
98
- form.set('worker.js', new Blob([WORKER_SCRIPT], { type: 'application/javascript+module' }), 'worker.js');
99
- return cloudflareRequest({
100
- token: options.token,
101
- method: 'PUT',
102
- path: `/accounts/${options.accountId}/workers/scripts/${options.scriptName}`,
103
- body: form,
104
- });
105
- }
106
- async function enableWorkersDev(token, accountId, scriptName) {
107
- await cloudflareRequest({
108
- token,
109
- method: 'POST',
110
- path: `/accounts/${accountId}/workers/scripts/${scriptName}/subdomain`,
111
- body: JSON.stringify({ enabled: true, previews_enabled: false }),
112
- });
113
- }
114
- async function getWorkersSubdomain(token, accountId) {
115
- return cloudflareRequest({
116
- token,
117
- method: 'GET',
118
- path: `/accounts/${accountId}/workers/subdomain`,
119
- }).catch(() => null);
120
- }
121
- async function relaySetup() {
122
- const storage = (await loadAccounts()) ?? defaultStorage();
123
- const token = requireText(process.env.CLOUDFLARE_API_TOKEN?.trim() ||
124
- (await prompt('Cloudflare API token: ')), 'Cloudflare API token');
125
- const accountId = requireText(process.env.CLOUDFLARE_ACCOUNT_ID ||
126
- (await prompt('Cloudflare account ID: ')), 'Cloudflare account ID');
127
- const scriptName = (await prompt('Worker name [opencode-anthropic-relay]: ')) ||
128
- 'opencode-anthropic-relay';
129
- const kvTitle = `${scriptName}-state`;
130
- const relayToken = generateRelayToken();
131
- console.log('Creating Cloudflare KV namespace...');
132
- const namespace = await createKvNamespace(token, accountId, kvTitle);
133
- console.log('Uploading relay Worker...');
134
- await uploadRelayWorker({
135
- token,
136
- accountId,
137
- scriptName,
138
- kvNamespaceId: namespace.id,
139
- relayToken,
140
- });
141
- await enableWorkersDev(token, accountId, scriptName).catch((error) => {
142
- console.warn(`Could not enable workers.dev automatically: ${error instanceof Error ? error.message : String(error)}`);
143
- });
144
- const subdomain = await getWorkersSubdomain(token, accountId);
145
- const defaultUrl = subdomain?.subdomain
146
- ? `https://${scriptName}.${subdomain.subdomain}.workers.dev`
147
- : '';
148
- const url = defaultUrl ||
149
- requireText(await prompt('Relay Worker URL: '), 'Relay Worker URL');
150
- storage.relay = {
151
- enabled: true,
152
- url,
153
- token: relayToken,
154
- fallbackToDirect: true,
155
- transport: 'http',
156
- };
157
- await saveAccounts(storage);
158
- console.log(`Relay enabled at ${url}`);
159
- console.log(`Config saved to ${getAccountStoragePath()}.`);
160
- }
161
- async function prompt(message) {
162
- const rl = createInterface({ input, output });
163
- try {
164
- return (await rl.question(message)).trim();
165
- }
166
- finally {
167
- rl.close();
168
- }
169
- }
170
- function upsertAccount(storage, account) {
171
- const index = storage.accounts.findIndex((candidate) => candidate.id === account.id ||
172
- (account.label && candidate.label === account.label));
173
- if (index >= 0) {
174
- storage.accounts[index] = {
175
- ...storage.accounts[index],
176
- ...account,
177
- addedAt: storage.accounts[index]?.addedAt ?? account.addedAt,
178
- quota: account.quota,
179
- lastRefreshedAt: account.lastRefreshedAt,
180
- lastRefreshError: account.lastRefreshError,
181
- lastQuotaRefreshError: account.lastQuotaRefreshError,
182
- };
183
- return;
184
- }
185
- storage.accounts.push(account);
186
- }
187
- async function login(labelArg) {
188
- const storage = (await loadAccounts()) ?? defaultStorage();
189
- const label = labelArg?.trim() || (await prompt('Fallback account label (optional): '));
190
- const authorization = await authorize('max');
191
- console.log('\nOpen this URL in your browser and complete Claude sign-in:\n');
192
- console.log(`${authorization.url}\n`);
193
- const code = await prompt('Paste the full callback URL or authorization code here: ');
194
- const result = await exchange(code, authorization.verifier, authorization.redirectUri, authorization.state);
195
- if (result.type === 'failed') {
196
- throw new Error('Authentication failed');
197
- }
198
- const now = Date.now();
199
- upsertAccount(storage, {
200
- id: label || crypto.randomUUID(),
201
- label: label || undefined,
202
- type: 'oauth',
203
- access: result.access,
204
- refresh: result.refresh,
205
- expires: result.expires,
206
- enabled: true,
207
- addedAt: now,
208
- lastUsed: now,
209
- });
210
- await saveAccounts(storage);
211
- console.log(`\nSaved fallback account${label ? ` "${label}"` : ''}.`);
212
- }
213
- async function listAccounts() {
214
- const storage = await loadAccounts();
215
- if (!storage?.accounts.length) {
216
- console.log(`No fallback accounts found at ${getAccountStoragePath()}.`);
217
- return;
218
- }
219
- for (const [index, account] of storage.accounts.entries()) {
220
- const label = account.label || account.id;
221
- const status = account.enabled === false ? 'disabled' : 'enabled';
222
- const fiveHour = account.quota?.five_hour?.remainingPercent;
223
- const sevenDay = account.quota?.seven_day?.remainingPercent;
224
- const quota = fiveHour === undefined && sevenDay === undefined
225
- ? 'quota unknown'
226
- : `5h ${fiveHour ?? '?'}%, 1w ${sevenDay ?? '?'}% remaining`;
227
- console.log(`${index + 1}. ${label} (${status}) — ${quota}`);
228
- }
229
- }
230
- async function main() {
231
- const [command, label] = process.argv.slice(2);
232
- if (!command ||
233
- command === 'help' ||
234
- command === '--help' ||
235
- command === '-h') {
236
- usage();
237
- return;
238
- }
239
- if (command === 'login') {
240
- await login(label);
241
- return;
242
- }
243
- if (command === 'list') {
244
- await listAccounts();
245
- return;
246
- }
247
- if (command === 'relay' && label === 'setup') {
248
- await relaySetup();
249
- return;
250
- }
251
- usage();
252
- process.exitCode = 1;
253
- }
254
- try {
255
- await main();
256
- }
257
- catch (error) {
258
- console.error(error instanceof Error ? error.message : String(error));
259
- process.exitCode = 1;
260
- }
8
+ ${l()}`)}function p(e,t){let a=e?.trim();if(!a)throw Error(`${t} is required`);return a}async function d(e){let t=await fetch(`https://api.cloudflare.com/client/v4${e.path}`,{method:e.method,headers:{authorization:`Bearer ${e.token}`,...e.body instanceof FormData?{}:{"content-type":"application/json"},...e.headers},body:e.body}),a=await t.text(),n;try{n=JSON.parse(a)}catch{throw Error(`Cloudflare API returned ${t.status}: ${a}`)}if(!t.ok||n.success===!1){let r=n.errors?.map((o)=>o.message).filter(Boolean).join("; ");throw Error(r||`Cloudflare API returned ${t.status}`)}return n.result}async function E(e,t,a){return d({token:e,method:"POST",path:`/accounts/${t}/storage/kv/namespaces`,body:JSON.stringify({title:a})})}async function I(e){let t={main_module:"worker.js",compatibility_date:"2026-04-28",bindings:[{type:"kv_namespace",name:"RELAY_STATE",namespace_id:e.kvNamespaceId},{type:"secret_text",name:"RELAY_TOKEN",text:e.relayToken}]},a=new FormData;return a.set("metadata",JSON.stringify(t)),a.set("worker.js",new Blob([k],{type:"application/javascript+module"}),"worker.js"),d({token:e.token,method:"PUT",path:`/accounts/${e.accountId}/workers/scripts/${e.scriptName}`,body:a})}async function T(e,t,a){await d({token:e,method:"POST",path:`/accounts/${t}/workers/scripts/${a}/subdomain`,body:JSON.stringify({enabled:!0,previews_enabled:!1})})}async function C(e,t){return d({token:e,method:"GET",path:`/accounts/${t}/workers/subdomain`}).catch(()=>null)}async function S(){let e=await u()??v(),t=p(process.env.CLOUDFLARE_API_TOKEN?.trim()||await c("Cloudflare API token: "),"Cloudflare API token"),a=p(process.env.CLOUDFLARE_ACCOUNT_ID||await c("Cloudflare account ID: "),"Cloudflare account ID"),n=await c("Worker name [opencode-anthropic-relay]: ")||"opencode-anthropic-relay",r=`${n}-state`,o=b();console.log("Creating Cloudflare KV namespace...");let s=await E(t,a,r);console.log("Uploading relay Worker..."),await I({token:t,accountId:a,scriptName:n,kvNamespaceId:s.id,relayToken:o}),await T(t,a,n).catch((f)=>{console.warn(`Could not enable workers.dev automatically: ${f instanceof Error?f.message:String(f)}`)});let i=await C(t,a),g=(i?.subdomain?`https://${n}.${i.subdomain}.workers.dev`:"")||p(await c("Relay Worker URL: "),"Relay Worker URL");e.relay={enabled:!0,url:g,token:o,fallbackToDirect:!0,transport:"http"},await m(e),console.log(`Relay enabled at ${g}`),console.log(`Config saved to ${l()}.`)}async function c(e){let t=R({input:A,output:$});try{return(await t.question(e)).trim()}finally{t.close()}}function O(e,t){let a=e.accounts.findIndex((n)=>n.id===t.id||t.label&&n.label===t.label);if(a>=0){e.accounts[a]={...e.accounts[a],...t,addedAt:e.accounts[a]?.addedAt??t.addedAt,quota:t.quota,lastRefreshedAt:t.lastRefreshedAt,lastRefreshError:t.lastRefreshError,lastQuotaRefreshError:t.lastQuotaRefreshError};return}e.accounts.push(t)}async function U(e){let t=await u()??v(),a=e?.trim()||await c("Fallback account label (optional): "),n=await y("max");console.log(`
9
+ Open this URL in your browser and complete Claude sign-in:
10
+ `),console.log(`${n.url}
11
+ `);let r=await c("Paste the full callback URL or authorization code here: "),o=await w(r,n.verifier,n.redirectUri,n.state);if(o.type==="failed")throw Error("Authentication failed");let s=Date.now();O(t,{id:a||crypto.randomUUID(),label:a||void 0,type:"oauth",access:o.access,refresh:o.refresh,expires:o.expires,enabled:!0,addedAt:s,lastUsed:s}),await m(t),console.log(`
12
+ Saved fallback account${a?` "${a}"`:""}.`)}async function _(){let e=await u();if(!e?.accounts.length){console.log(`No fallback accounts found at ${l()}.`);return}for(let[t,a]of e.accounts.entries()){let n=a.label||a.id,r=a.enabled===!1?"disabled":"enabled",o=a.quota?.five_hour?.remainingPercent,s=a.quota?.seven_day?.remainingPercent,i=o===void 0&&s===void 0?"quota unknown":`5h ${o??"?"}%, 1w ${s??"?"}% remaining`;console.log(`${t+1}. ${n} (${r}) — ${i}`)}}async function x(){let[e,t]=process.argv.slice(2);if(!e||e==="help"||e==="--help"||e==="-h"){h();return}if(e==="login"){await U(t);return}if(e==="list"){await _();return}if(e==="relay"&&t==="setup"){await S();return}h(),process.exitCode=1}try{await x()}catch(e){console.error(e instanceof Error?e.message:String(e)),process.exitCode=1}