opencode-anthropic-fix 0.0.14
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/.github/workflows/publish.yml +31 -0
- package/.husky/pre-commit +2 -0
- package/.husky/pre-push +3 -0
- package/.planning/CLI-ENHANCEMENTS.md +221 -0
- package/.planning/codebase/ARCHITECTURE.md +201 -0
- package/.planning/codebase/CONCERNS.md +219 -0
- package/.planning/codebase/CONVENTIONS.md +335 -0
- package/.planning/codebase/INTEGRATIONS.md +215 -0
- package/.planning/codebase/STACK.md +160 -0
- package/.planning/codebase/STRUCTURE.md +244 -0
- package/.planning/codebase/TESTING.md +456 -0
- package/.prettierignore +3 -0
- package/.prettierrc +10 -0
- package/CONTRIBUTING.md +525 -0
- package/LICENSE +21 -0
- package/README.md +478 -0
- package/agents.md +22 -0
- package/bun.lock +43 -0
- package/cli.mjs +1773 -0
- package/cli.test.mjs +1797 -0
- package/docs/mimese-http-header-system-prompt.md +326 -0
- package/docs/plans/afcw-upstream-watcher.json +495 -0
- package/eslint.config.mjs +69 -0
- package/index.mjs +2209 -0
- package/index.test.mjs +2690 -0
- package/lib/accounts.mjs +670 -0
- package/lib/accounts.test.mjs +782 -0
- package/lib/backoff.mjs +217 -0
- package/lib/backoff.test.mjs +296 -0
- package/lib/config.mjs +366 -0
- package/lib/config.test.mjs +307 -0
- package/lib/oauth.mjs +103 -0
- package/lib/rotation.mjs +287 -0
- package/lib/rotation.test.mjs +346 -0
- package/lib/storage.mjs +265 -0
- package/lib/storage.test.mjs +470 -0
- package/nul +1 -0
- package/package.json +40 -0
- package/script/publish.ts +29 -0
- package/scripts/build.mjs +34 -0
- package/scripts/install.mjs +319 -0
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Installation script for opencode-anthropic-auth.
|
|
5
|
+
*
|
|
6
|
+
* Installs two things:
|
|
7
|
+
* 1. Plugin → ~/.config/opencode/plugin/opencode-anthropic-auth-plugin.js
|
|
8
|
+
* 2. CLI → ~/.local/bin/opencode-anthropic-auth
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* node scripts/install.mjs link Symlink both (development)
|
|
12
|
+
* node scripts/install.mjs copy Copy both (stable deployment)
|
|
13
|
+
* node scripts/install.mjs uninstall Remove both
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { existsSync, lstatSync, readlinkSync } from "node:fs";
|
|
17
|
+
import { mkdir, symlink, unlink, rm, copyFile, chmod } from "node:fs/promises";
|
|
18
|
+
import { join, resolve, dirname } from "node:path";
|
|
19
|
+
import { homedir } from "node:os";
|
|
20
|
+
|
|
21
|
+
import { fileURLToPath } from "node:url";
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Paths
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
28
|
+
const PROJECT_ROOT = resolve(dirname(__filename), "..");
|
|
29
|
+
const PLUGIN_NAME = "opencode-anthropic-auth";
|
|
30
|
+
const PLUGIN_ENTRY = "opencode-anthropic-auth-plugin.js";
|
|
31
|
+
const CLI_BIN_NAME = "opencode-anthropic-auth";
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Get the OpenCode plugin directory, respecting XDG_CONFIG_HOME.
|
|
35
|
+
* @returns {string}
|
|
36
|
+
*/
|
|
37
|
+
function getPluginDir() {
|
|
38
|
+
const configHome = process.env.XDG_CONFIG_HOME || join(homedir(), ".config");
|
|
39
|
+
return join(configHome, "opencode", "plugin");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Get the user bin directory (~/.local/bin).
|
|
44
|
+
* @returns {string}
|
|
45
|
+
*/
|
|
46
|
+
function getBinDir() {
|
|
47
|
+
return join(homedir(), ".local", "bin");
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const DIST_DIR = join(PROJECT_ROOT, "dist");
|
|
51
|
+
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
// Helpers
|
|
54
|
+
// ---------------------------------------------------------------------------
|
|
55
|
+
|
|
56
|
+
const bold = (t) => `\x1b[1m${t}\x1b[0m`;
|
|
57
|
+
const green = (t) => `\x1b[32m${t}\x1b[0m`;
|
|
58
|
+
const yellow = (t) => `\x1b[33m${t}\x1b[0m`;
|
|
59
|
+
const red = (t) => `\x1b[31m${t}\x1b[0m`;
|
|
60
|
+
const dim = (t) => `\x1b[2m${t}\x1b[0m`;
|
|
61
|
+
|
|
62
|
+
function printEnvReminder() {
|
|
63
|
+
console.log(yellow("\nNote: if using OpenCode v1.1.52 or earlier, set OPENCODE_DISABLE_DEFAULT_PLUGINS=1"));
|
|
64
|
+
console.log(dim("This is not needed on newer versions where user plugins take priority."));
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function shortPath(p) {
|
|
68
|
+
const home = homedir();
|
|
69
|
+
if (p.startsWith(home)) return "~" + p.slice(home.length);
|
|
70
|
+
return p;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Check what currently exists at a path.
|
|
75
|
+
* @param {string} path
|
|
76
|
+
* @returns {{ exists: boolean, isSymlink: boolean, target: string | null, isDir: boolean }}
|
|
77
|
+
*/
|
|
78
|
+
function checkExisting(path) {
|
|
79
|
+
if (!existsSync(path)) {
|
|
80
|
+
return { exists: false, isSymlink: false, target: null, isDir: false };
|
|
81
|
+
}
|
|
82
|
+
const stat = lstatSync(path);
|
|
83
|
+
const isSymlink = stat.isSymbolicLink();
|
|
84
|
+
let target = null;
|
|
85
|
+
if (isSymlink) {
|
|
86
|
+
try {
|
|
87
|
+
target = readlinkSync(path);
|
|
88
|
+
} catch {
|
|
89
|
+
// ignore
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return { exists: true, isSymlink, target, isDir: stat.isDirectory() };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Create or replace a symlink.
|
|
97
|
+
* @param {string} target - What the symlink points to
|
|
98
|
+
* @param {string} linkPath - Where the symlink lives
|
|
99
|
+
* @param {string} label - Human-readable name for logging
|
|
100
|
+
* @returns {Promise<boolean>} true if created, false if already correct
|
|
101
|
+
*/
|
|
102
|
+
async function ensureSymlink(target, linkPath, label) {
|
|
103
|
+
await mkdir(dirname(linkPath), { recursive: true });
|
|
104
|
+
|
|
105
|
+
const existing = checkExisting(linkPath);
|
|
106
|
+
|
|
107
|
+
if (existing.exists) {
|
|
108
|
+
if (existing.isSymlink && existing.target === target) {
|
|
109
|
+
console.log(green(`${label}: already linked.`));
|
|
110
|
+
console.log(dim(` ${shortPath(linkPath)} -> ${shortPath(target)}`));
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (existing.isSymlink) {
|
|
115
|
+
console.log(yellow(`${label}: replacing symlink (was -> ${existing.target})`));
|
|
116
|
+
} else if (existing.isDir) {
|
|
117
|
+
console.log(yellow(`${label}: replacing directory`));
|
|
118
|
+
await rm(linkPath, { recursive: true, force: true });
|
|
119
|
+
} else {
|
|
120
|
+
console.log(yellow(`${label}: replacing existing file`));
|
|
121
|
+
}
|
|
122
|
+
if (!existing.isDir) await unlink(linkPath);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
await symlink(target, linkPath);
|
|
126
|
+
console.log(green(`${label}: linked.`));
|
|
127
|
+
console.log(dim(` ${shortPath(linkPath)} -> ${shortPath(target)}`));
|
|
128
|
+
return true;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Remove a path (symlink, file, or directory).
|
|
133
|
+
* @param {string} path
|
|
134
|
+
* @param {string} label
|
|
135
|
+
* @returns {Promise<boolean>} true if something was removed
|
|
136
|
+
*/
|
|
137
|
+
async function removePath(path, label) {
|
|
138
|
+
// Use lstatSync to detect broken symlinks (existsSync returns false for them)
|
|
139
|
+
let stat;
|
|
140
|
+
try {
|
|
141
|
+
stat = lstatSync(path);
|
|
142
|
+
} catch {
|
|
143
|
+
return false;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (stat.isDirectory()) {
|
|
147
|
+
await rm(path, { recursive: true, force: true });
|
|
148
|
+
console.log(green(`${label}: removed directory ${shortPath(path)}/`));
|
|
149
|
+
} else {
|
|
150
|
+
await unlink(path);
|
|
151
|
+
const kind = stat.isSymbolicLink() ? "symlink" : "file";
|
|
152
|
+
console.log(green(`${label}: removed ${kind} ${shortPath(path)}`));
|
|
153
|
+
}
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// ---------------------------------------------------------------------------
|
|
158
|
+
// Commands
|
|
159
|
+
// ---------------------------------------------------------------------------
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Symlink both plugin and CLI for development.
|
|
163
|
+
*/
|
|
164
|
+
async function cmdLink() {
|
|
165
|
+
console.log(bold("Linking opencode-anthropic-auth...\n"));
|
|
166
|
+
|
|
167
|
+
// Plugin: symlink entry point
|
|
168
|
+
const pluginDir = getPluginDir();
|
|
169
|
+
const pluginEntry = join(pluginDir, PLUGIN_ENTRY);
|
|
170
|
+
const pluginTarget = join(PROJECT_ROOT, "index.mjs");
|
|
171
|
+
|
|
172
|
+
// Clean up old-named entry if present
|
|
173
|
+
const oldEntry = join(pluginDir, "opencode-anthropic-auth.js");
|
|
174
|
+
if (existsSync(oldEntry) && oldEntry !== pluginEntry) {
|
|
175
|
+
await unlink(oldEntry);
|
|
176
|
+
console.log(dim("Plugin: removed old opencode-anthropic-auth.js"));
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
await ensureSymlink(pluginTarget, pluginEntry, "Plugin");
|
|
180
|
+
|
|
181
|
+
// CLI: symlink to cli.mjs
|
|
182
|
+
const binDir = getBinDir();
|
|
183
|
+
const cliBin = join(binDir, CLI_BIN_NAME);
|
|
184
|
+
const cliTarget = join(PROJECT_ROOT, "cli.mjs");
|
|
185
|
+
await ensureSymlink(cliTarget, cliBin, "CLI");
|
|
186
|
+
|
|
187
|
+
console.log(dim("\nEdits to source files take effect immediately."));
|
|
188
|
+
|
|
189
|
+
// Check if ~/.local/bin is on PATH
|
|
190
|
+
const pathDirs = (process.env.PATH || "").split(":");
|
|
191
|
+
if (!pathDirs.includes(binDir)) {
|
|
192
|
+
console.log(yellow(`\nNote: ${shortPath(binDir)} is not on your PATH.`));
|
|
193
|
+
console.log(dim(`Add to your shell profile: export PATH="${shortPath(binDir)}:$PATH"`));
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
printEnvReminder();
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Copy bundled plugin and CLI for stable deployment.
|
|
201
|
+
* Requires `npm run build` first (install:copy runs it automatically).
|
|
202
|
+
*/
|
|
203
|
+
async function cmdCopy() {
|
|
204
|
+
console.log(bold("Copying opencode-anthropic-auth...\n"));
|
|
205
|
+
|
|
206
|
+
const pluginSrc = join(DIST_DIR, "opencode-anthropic-auth-plugin.js");
|
|
207
|
+
const cliSrc = join(DIST_DIR, "opencode-anthropic-auth-cli.mjs");
|
|
208
|
+
|
|
209
|
+
if (!existsSync(pluginSrc) || !existsSync(cliSrc)) {
|
|
210
|
+
console.error(red("dist/ not found. Run `npm run build` first."));
|
|
211
|
+
process.exit(1);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// --- Plugin: single file copy ---
|
|
215
|
+
const pluginDir = getPluginDir();
|
|
216
|
+
const pluginDest = join(pluginDir, PLUGIN_ENTRY);
|
|
217
|
+
|
|
218
|
+
await mkdir(pluginDir, { recursive: true });
|
|
219
|
+
|
|
220
|
+
// Clean up old multi-file copy directory if present
|
|
221
|
+
const oldCopyDir = join(pluginDir, PLUGIN_NAME);
|
|
222
|
+
if (existsSync(oldCopyDir)) {
|
|
223
|
+
await rm(oldCopyDir, { recursive: true, force: true });
|
|
224
|
+
console.log(dim("Plugin: removed old copy directory."));
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (existsSync(pluginDest)) await unlink(pluginDest);
|
|
228
|
+
await copyFile(pluginSrc, pluginDest);
|
|
229
|
+
|
|
230
|
+
console.log(green("Plugin: copied."));
|
|
231
|
+
console.log(dim(` ${shortPath(pluginDest)}`));
|
|
232
|
+
|
|
233
|
+
// --- CLI: single file copy ---
|
|
234
|
+
const binDir = getBinDir();
|
|
235
|
+
const cliBin = join(binDir, CLI_BIN_NAME);
|
|
236
|
+
|
|
237
|
+
await mkdir(binDir, { recursive: true });
|
|
238
|
+
if (existsSync(cliBin)) await unlink(cliBin);
|
|
239
|
+
await copyFile(cliSrc, cliBin);
|
|
240
|
+
await chmod(cliBin, 0o755);
|
|
241
|
+
|
|
242
|
+
console.log(green("CLI: copied."));
|
|
243
|
+
console.log(dim(` ${shortPath(cliBin)}`));
|
|
244
|
+
|
|
245
|
+
console.log(dim("\nThis is a snapshot. Re-run to update."));
|
|
246
|
+
|
|
247
|
+
// Check if ~/.local/bin is on PATH
|
|
248
|
+
const pathDirs = (process.env.PATH || "").split(":");
|
|
249
|
+
if (!pathDirs.includes(binDir)) {
|
|
250
|
+
console.log(yellow(`\nNote: ${shortPath(binDir)} is not on your PATH.`));
|
|
251
|
+
console.log(dim(`Add to your shell profile: export PATH="${shortPath(binDir)}:$PATH"`));
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Remove both plugin and CLI.
|
|
257
|
+
*/
|
|
258
|
+
async function cmdUninstall() {
|
|
259
|
+
console.log(bold("Uninstalling opencode-anthropic-auth...\n"));
|
|
260
|
+
|
|
261
|
+
let removed = false;
|
|
262
|
+
|
|
263
|
+
// Plugin entry point (current and old name)
|
|
264
|
+
const pluginDir = getPluginDir();
|
|
265
|
+
const pluginEntry = join(pluginDir, PLUGIN_ENTRY);
|
|
266
|
+
if (await removePath(pluginEntry, "Plugin")) removed = true;
|
|
267
|
+
const oldEntry = join(pluginDir, "opencode-anthropic-auth.js");
|
|
268
|
+
if (await removePath(oldEntry, "Plugin (old name)")) removed = true;
|
|
269
|
+
|
|
270
|
+
// Plugin copy directory
|
|
271
|
+
const copyDir = join(pluginDir, PLUGIN_NAME);
|
|
272
|
+
if (await removePath(copyDir, "Plugin")) removed = true;
|
|
273
|
+
|
|
274
|
+
// CLI binary
|
|
275
|
+
const binDir = getBinDir();
|
|
276
|
+
const cliBin = join(binDir, CLI_BIN_NAME);
|
|
277
|
+
if (await removePath(cliBin, "CLI")) removed = true;
|
|
278
|
+
|
|
279
|
+
// Also clean up any old npm link global install
|
|
280
|
+
const npmGlobal = join("/opt/homebrew/bin", "anthropic-auth");
|
|
281
|
+
if (await removePath(npmGlobal, "CLI (old npm link)")) removed = true;
|
|
282
|
+
|
|
283
|
+
if (!removed) {
|
|
284
|
+
console.log(dim("Nothing to remove. Not installed."));
|
|
285
|
+
} else {
|
|
286
|
+
console.log(dim("\nUninstalled."));
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// ---------------------------------------------------------------------------
|
|
291
|
+
// Main
|
|
292
|
+
// ---------------------------------------------------------------------------
|
|
293
|
+
|
|
294
|
+
const command = process.argv[2];
|
|
295
|
+
|
|
296
|
+
switch (command) {
|
|
297
|
+
case "link":
|
|
298
|
+
await cmdLink();
|
|
299
|
+
break;
|
|
300
|
+
case "copy":
|
|
301
|
+
await cmdCopy();
|
|
302
|
+
break;
|
|
303
|
+
case "uninstall":
|
|
304
|
+
await cmdUninstall();
|
|
305
|
+
break;
|
|
306
|
+
default:
|
|
307
|
+
console.log(`${bold("Installer for opencode-anthropic-auth")}
|
|
308
|
+
|
|
309
|
+
${dim("Installs:")}
|
|
310
|
+
Plugin ${dim("->")} ~/.config/opencode/plugin/${PLUGIN_ENTRY}
|
|
311
|
+
CLI ${dim("->")} ~/.local/bin/${CLI_BIN_NAME}
|
|
312
|
+
|
|
313
|
+
${dim("Usage:")}
|
|
314
|
+
node scripts/install.mjs ${bold("link")} Symlink both (development)
|
|
315
|
+
node scripts/install.mjs ${bold("copy")} Copy both (stable deployment)
|
|
316
|
+
node scripts/install.mjs ${bold("uninstall")} Remove both
|
|
317
|
+
`);
|
|
318
|
+
process.exit(command ? 1 : 0);
|
|
319
|
+
}
|