openai-ws-opencode 0.1.4 → 0.1.5
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 +1 -1
- package/bin/setup.js +143 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -101,7 +101,7 @@ Then point OpenCode at the packed tarball through the plugin array:
|
|
|
101
101
|
|
|
102
102
|
```jsonc
|
|
103
103
|
{
|
|
104
|
-
"plugin": ["openai-ws-opencode@file:/absolute/path/openai-ws-opencode-0.1.
|
|
104
|
+
"plugin": ["openai-ws-opencode@file:/absolute/path/openai-ws-opencode-0.1.5.tgz"]
|
|
105
105
|
}
|
|
106
106
|
```
|
|
107
107
|
|
package/bin/setup.js
CHANGED
|
@@ -166,20 +166,28 @@ function applyJsonPatch(text, jsonPath, value) {
|
|
|
166
166
|
}));
|
|
167
167
|
}
|
|
168
168
|
function pluginPackageName(specifier) {
|
|
169
|
-
if (specifier.startsWith("file
|
|
170
|
-
|
|
169
|
+
if (specifier.startsWith("file:")) {
|
|
170
|
+
const filePath = specifier.startsWith("file://") ? new URL(specifier).pathname : specifier.slice("file:".length);
|
|
171
|
+
const base = path.basename(filePath);
|
|
172
|
+
if (base === PACKAGE_NAME || base.startsWith(`${PACKAGE_NAME}-`) || base.startsWith(`${PACKAGE_NAME}@`))
|
|
173
|
+
return PACKAGE_NAME;
|
|
174
|
+
return base.replace(/(?:\.tgz|\.[cm]?[jt]s)$/, "");
|
|
175
|
+
}
|
|
171
176
|
const lastAt = specifier.lastIndexOf("@");
|
|
172
177
|
return lastAt > 0 ? specifier.slice(0, lastAt) : specifier;
|
|
173
178
|
}
|
|
174
|
-
function patchConfigText(input, pluginSpec = DEFAULT_PLUGIN_SPEC) {
|
|
179
|
+
function patchConfigText(input, pluginSpec = DEFAULT_PLUGIN_SPEC, replacePluginSpec = false) {
|
|
175
180
|
let text = input.trim() ? input : "{}";
|
|
176
181
|
const config = parse(text) ?? {};
|
|
177
182
|
if (!config.$schema)
|
|
178
183
|
text = applyJsonPatch(text, ["$schema"], SCHEMA);
|
|
179
184
|
const current = (parse(text) ?? {}).plugin;
|
|
180
185
|
const plugins = Array.isArray(current) ? [...current] : [];
|
|
181
|
-
|
|
186
|
+
const existingPluginIndex = plugins.findIndex((plugin) => typeof plugin === "string" && pluginPackageName(plugin) === PACKAGE_NAME);
|
|
187
|
+
if (existingPluginIndex === -1) {
|
|
182
188
|
plugins.push(pluginSpec);
|
|
189
|
+
} else if (replacePluginSpec) {
|
|
190
|
+
plugins[existingPluginIndex] = pluginSpec;
|
|
183
191
|
}
|
|
184
192
|
text = applyJsonPatch(text, ["plugin"], plugins);
|
|
185
193
|
const next = parse(text) ?? {};
|
|
@@ -192,6 +200,123 @@ function patchConfigText(input, pluginSpec = DEFAULT_PLUGIN_SPEC) {
|
|
|
192
200
|
`
|
|
193
201
|
}));
|
|
194
202
|
}
|
|
203
|
+
function isNotFound(error) {
|
|
204
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
205
|
+
}
|
|
206
|
+
function cacheHomePath(cacheHome = process.env.XDG_CACHE_HOME ?? path.join(os.homedir(), ".cache")) {
|
|
207
|
+
return cacheHome;
|
|
208
|
+
}
|
|
209
|
+
function openCodeLatestCachePath(cacheHome) {
|
|
210
|
+
return path.join(cacheHomePath(cacheHome), "opencode", "packages", `${PACKAGE_NAME}@latest`);
|
|
211
|
+
}
|
|
212
|
+
async function pathExists(file) {
|
|
213
|
+
try {
|
|
214
|
+
await fs.access(file);
|
|
215
|
+
return true;
|
|
216
|
+
} catch (error) {
|
|
217
|
+
if (isNotFound(error))
|
|
218
|
+
return false;
|
|
219
|
+
throw error;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
async function readPackageVersion(packageJsonPath) {
|
|
223
|
+
try {
|
|
224
|
+
const text = await fs.readFile(packageJsonPath, "utf8");
|
|
225
|
+
const parsed = JSON.parse(text);
|
|
226
|
+
return typeof parsed.version === "string" ? parsed.version : undefined;
|
|
227
|
+
} catch (error) {
|
|
228
|
+
if (isNotFound(error))
|
|
229
|
+
return;
|
|
230
|
+
throw error;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
async function setupPackageVersion() {
|
|
234
|
+
const packageJsonPath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "package.json");
|
|
235
|
+
const version = await readPackageVersion(packageJsonPath);
|
|
236
|
+
if (!version)
|
|
237
|
+
throw new Error(`Unable to read setup package version from ${packageJsonPath}`);
|
|
238
|
+
return version;
|
|
239
|
+
}
|
|
240
|
+
function semverParts(version) {
|
|
241
|
+
const match = /^(\d+)\.(\d+)\.(\d+)/.exec(version);
|
|
242
|
+
if (!match)
|
|
243
|
+
return;
|
|
244
|
+
return [Number(match[1]), Number(match[2]), Number(match[3])];
|
|
245
|
+
}
|
|
246
|
+
function compareSemver(left, right) {
|
|
247
|
+
const leftParts = semverParts(left);
|
|
248
|
+
const rightParts = semverParts(right);
|
|
249
|
+
if (!leftParts || !rightParts)
|
|
250
|
+
return 0;
|
|
251
|
+
for (let index = 0;index < leftParts.length; index++) {
|
|
252
|
+
if (leftParts[index] !== rightParts[index])
|
|
253
|
+
return leftParts[index] < rightParts[index] ? -1 : 1;
|
|
254
|
+
}
|
|
255
|
+
return 0;
|
|
256
|
+
}
|
|
257
|
+
var SOURCE_EXTENSIONS = new Set([".cjs", ".js", ".mjs", ".ts"]);
|
|
258
|
+
var BAD_OAUTH_PORT = /\bOAUTH_PORT\b\s*[:=]\s*1456\b/;
|
|
259
|
+
var BAD_CODEX_ORIGINATOR = /\bCODEX_ORIGINATOR\b\s*[:=]\s*["']codex_cli_rs["']/;
|
|
260
|
+
async function hasKnownBadConstants(packageRoot) {
|
|
261
|
+
const directories = [packageRoot];
|
|
262
|
+
let checkedFiles = 0;
|
|
263
|
+
while (directories.length > 0 && checkedFiles < 500) {
|
|
264
|
+
const current = directories.pop();
|
|
265
|
+
let entries;
|
|
266
|
+
try {
|
|
267
|
+
entries = await fs.readdir(current, { withFileTypes: true });
|
|
268
|
+
} catch (error) {
|
|
269
|
+
if (isNotFound(error))
|
|
270
|
+
continue;
|
|
271
|
+
throw error;
|
|
272
|
+
}
|
|
273
|
+
for (const entry of entries) {
|
|
274
|
+
const entryPath = path.join(current, entry.name);
|
|
275
|
+
if (entry.isDirectory()) {
|
|
276
|
+
if (entry.name !== "node_modules" && entry.name !== ".git")
|
|
277
|
+
directories.push(entryPath);
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
if (!entry.isFile() || !SOURCE_EXTENSIONS.has(path.extname(entry.name)))
|
|
281
|
+
continue;
|
|
282
|
+
checkedFiles += 1;
|
|
283
|
+
const source = await fs.readFile(entryPath, "utf8");
|
|
284
|
+
if (BAD_OAUTH_PORT.test(source) || BAD_CODEX_ORIGINATOR.test(source))
|
|
285
|
+
return true;
|
|
286
|
+
if (checkedFiles >= 500)
|
|
287
|
+
break;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
function timestampForPath(date) {
|
|
293
|
+
return date.toISOString().replace(/[:.]/g, "-");
|
|
294
|
+
}
|
|
295
|
+
async function staleDestination(cachePath, now) {
|
|
296
|
+
const base = `${cachePath}.stale-${timestampForPath(now)}`;
|
|
297
|
+
let candidate = base;
|
|
298
|
+
let suffix = 1;
|
|
299
|
+
while (await pathExists(candidate)) {
|
|
300
|
+
candidate = `${base}-${suffix}`;
|
|
301
|
+
suffix += 1;
|
|
302
|
+
}
|
|
303
|
+
return candidate;
|
|
304
|
+
}
|
|
305
|
+
async function repairStaleOpenCodeLatestCache(options = {}) {
|
|
306
|
+
const cachePath = openCodeLatestCachePath(options.cacheHome);
|
|
307
|
+
if (!await pathExists(cachePath))
|
|
308
|
+
return { cachePath, repaired: false };
|
|
309
|
+
const packageRoot = path.join(cachePath, "node_modules", PACKAGE_NAME);
|
|
310
|
+
const cachedVersion = await readPackageVersion(path.join(packageRoot, "package.json"));
|
|
311
|
+
const currentVersion = options.packageVersion ?? await setupPackageVersion();
|
|
312
|
+
const staleVersion = cachedVersion ? compareSemver(cachedVersion, currentVersion) < 0 : false;
|
|
313
|
+
const staleConstants = await hasKnownBadConstants(packageRoot);
|
|
314
|
+
if (!staleVersion && !staleConstants)
|
|
315
|
+
return { cachePath, repaired: false };
|
|
316
|
+
const movedTo = await staleDestination(cachePath, options.now?.() ?? new Date);
|
|
317
|
+
await fs.rename(cachePath, movedTo);
|
|
318
|
+
return { cachePath, movedTo, repaired: true };
|
|
319
|
+
}
|
|
195
320
|
async function setupOpenCodeConfig(options = {}) {
|
|
196
321
|
const file = targetPath(options);
|
|
197
322
|
let existing = "";
|
|
@@ -201,11 +326,19 @@ async function setupOpenCodeConfig(options = {}) {
|
|
|
201
326
|
if (error?.code !== "ENOENT")
|
|
202
327
|
throw error;
|
|
203
328
|
}
|
|
204
|
-
const updated = patchConfigText(existing, options.pluginSpec ?? DEFAULT_PLUGIN_SPEC);
|
|
329
|
+
const updated = patchConfigText(existing, options.pluginSpec ?? DEFAULT_PLUGIN_SPEC, Boolean(options.pluginSpec));
|
|
205
330
|
await fs.mkdir(path.dirname(file), { recursive: true });
|
|
206
331
|
await fs.writeFile(file, updated.endsWith(`
|
|
207
332
|
`) ? updated : `${updated}
|
|
208
333
|
`, "utf8");
|
|
334
|
+
if (options.cacheRepair !== false) {
|
|
335
|
+
try {
|
|
336
|
+
await repairStaleOpenCodeLatestCache();
|
|
337
|
+
} catch (error) {
|
|
338
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
339
|
+
console.warn(`Warning: could not repair OpenCode cache at ${openCodeLatestCachePath()}. Remove it manually if OpenCode keeps loading stale plugin code. ${message}`);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
209
342
|
return file;
|
|
210
343
|
}
|
|
211
344
|
function parseArgs(argv) {
|
|
@@ -220,8 +353,10 @@ function parseArgs(argv) {
|
|
|
220
353
|
options.configPath = argv[++index];
|
|
221
354
|
else if (arg === "--plugin")
|
|
222
355
|
options.pluginSpec = argv[++index];
|
|
356
|
+
else if (arg === "--no-cache-repair")
|
|
357
|
+
options.cacheRepair = false;
|
|
223
358
|
else if (arg === "--help" || arg === "-h") {
|
|
224
|
-
console.log("Usage: openai-ws-opencode setup [--global|--project] [--path <opencode.json>] [--plugin <specifier>]");
|
|
359
|
+
console.log("Usage: openai-ws-opencode setup [--global|--project] [--path <opencode.json>] [--plugin <specifier>] [--no-cache-repair]");
|
|
225
360
|
process.exit(0);
|
|
226
361
|
}
|
|
227
362
|
}
|
|
@@ -247,6 +382,8 @@ if (isDirectExecution()) {
|
|
|
247
382
|
}
|
|
248
383
|
export {
|
|
249
384
|
setupOpenCodeConfig,
|
|
385
|
+
repairStaleOpenCodeLatestCache,
|
|
250
386
|
patchConfigText,
|
|
387
|
+
parseArgs,
|
|
251
388
|
isDirectExecution
|
|
252
389
|
};
|