@cortexkit/opencode-anthropic-auth 1.1.2 → 1.2.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 +20 -1
- package/dist/cli.js +6 -254
- package/dist/index-sebye7nt.js +318 -0
- package/dist/index.js +5 -1027
- package/package.json +5 -4
- package/dist/prompt-context.js +0 -98
- package/dist/transform.js +0 -498
package/README.md
CHANGED
|
@@ -158,7 +158,7 @@ Example:
|
|
|
158
158
|
"refresh": {
|
|
159
159
|
"enabled": true,
|
|
160
160
|
"intervalMinutes": 10,
|
|
161
|
-
"refreshBeforeExpiryMinutes":
|
|
161
|
+
"refreshBeforeExpiryMinutes": 240
|
|
162
162
|
},
|
|
163
163
|
"quota": {
|
|
164
164
|
"enabled": true,
|
|
@@ -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
|
|
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: 30,
|
|
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-sebye7nt.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
|
-
${
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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}
|