opencode-aicodewith-auth 0.1.3 → 0.1.7

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/dist/index.js CHANGED
@@ -59,9 +59,9 @@ function createAicodewith(options = {}) {
59
59
  var aicodewith = createAicodewith();
60
60
 
61
61
  // index.ts
62
- import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "fs/promises";
63
- import path from "path";
64
- import os from "os";
62
+ import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2, access } from "fs/promises";
63
+ import path4 from "path";
64
+ import os3 from "os";
65
65
 
66
66
  // lib/constants.ts
67
67
  var PLUGIN_NAME = "opencode-aicodewith-auth";
@@ -993,82 +993,539 @@ async function handleSuccessResponse(response, isStreaming) {
993
993
  });
994
994
  }
995
995
 
996
+ // lib/hooks/auto-update/index.ts
997
+ import { spawn } from "child_process";
998
+
999
+ // lib/hooks/auto-update/checker.ts
1000
+ import * as fs2 from "fs";
1001
+ import * as path2 from "path";
1002
+ import { fileURLToPath as fileURLToPath2 } from "url";
1003
+
1004
+ // lib/hooks/auto-update/constants.ts
1005
+ import * as path from "path";
1006
+ import * as os from "os";
1007
+ import * as fs from "fs";
1008
+ var PACKAGE_NAME = "opencode-aicodewith-auth";
1009
+ var NPM_REGISTRY_URL = `https://registry.npmjs.org/-/package/${PACKAGE_NAME}/dist-tags`;
1010
+ var NPM_FETCH_TIMEOUT = 5000;
1011
+ function getCacheDir() {
1012
+ if (process.platform === "win32") {
1013
+ return path.join(process.env.LOCALAPPDATA ?? os.homedir(), "opencode");
1014
+ }
1015
+ return path.join(os.homedir(), ".cache", "opencode");
1016
+ }
1017
+ var CACHE_DIR3 = getCacheDir();
1018
+ var INSTALLED_PACKAGE_JSON = path.join(CACHE_DIR3, "node_modules", PACKAGE_NAME, "package.json");
1019
+ function getUserConfigDir() {
1020
+ if (process.platform === "win32") {
1021
+ const crossPlatformDir = path.join(os.homedir(), ".config");
1022
+ const appdataDir = process.env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming");
1023
+ const crossPlatformConfig = path.join(crossPlatformDir, "opencode", "opencode.json");
1024
+ const crossPlatformConfigJsonc = path.join(crossPlatformDir, "opencode", "opencode.jsonc");
1025
+ if (fs.existsSync(crossPlatformConfig) || fs.existsSync(crossPlatformConfigJsonc)) {
1026
+ return crossPlatformDir;
1027
+ }
1028
+ return appdataDir;
1029
+ }
1030
+ return process.env.XDG_CONFIG_HOME ?? path.join(os.homedir(), ".config");
1031
+ }
1032
+ function getWindowsAppdataDir() {
1033
+ if (process.platform !== "win32")
1034
+ return null;
1035
+ return process.env.APPDATA ?? path.join(os.homedir(), "AppData", "Roaming");
1036
+ }
1037
+ var USER_CONFIG_DIR = getUserConfigDir();
1038
+ var USER_OPENCODE_CONFIG = path.join(USER_CONFIG_DIR, "opencode", "opencode.json");
1039
+ var USER_OPENCODE_CONFIG_JSONC = path.join(USER_CONFIG_DIR, "opencode", "opencode.jsonc");
1040
+
1041
+ // lib/hooks/auto-update/checker.ts
1042
+ import * as os2 from "os";
1043
+ var LOG_PREFIX = `[${PACKAGE_NAME}:auto-update]`;
1044
+ function log(...args) {
1045
+ if (process.env.DEBUG?.includes(PACKAGE_NAME) || process.env.DEBUG?.includes("auto-update")) {
1046
+ console.log(LOG_PREFIX, ...args);
1047
+ }
1048
+ }
1049
+ function stripJsonComments(json) {
1050
+ return json.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (m, g) => g ? "" : m).replace(/,(\s*[}\]])/g, "$1");
1051
+ }
1052
+ function getConfigPaths(directory) {
1053
+ const paths = [
1054
+ path2.join(directory, ".opencode", "opencode.json"),
1055
+ path2.join(directory, ".opencode", "opencode.jsonc"),
1056
+ USER_OPENCODE_CONFIG,
1057
+ USER_OPENCODE_CONFIG_JSONC
1058
+ ];
1059
+ if (process.platform === "win32") {
1060
+ const crossPlatformDir = path2.join(os2.homedir(), ".config");
1061
+ const appdataDir = getWindowsAppdataDir();
1062
+ if (appdataDir) {
1063
+ const alternateDir = USER_CONFIG_DIR === crossPlatformDir ? appdataDir : crossPlatformDir;
1064
+ const alternateConfig = path2.join(alternateDir, "opencode", "opencode.json");
1065
+ const alternateConfigJsonc = path2.join(alternateDir, "opencode", "opencode.jsonc");
1066
+ if (!paths.includes(alternateConfig)) {
1067
+ paths.push(alternateConfig);
1068
+ }
1069
+ if (!paths.includes(alternateConfigJsonc)) {
1070
+ paths.push(alternateConfigJsonc);
1071
+ }
1072
+ }
1073
+ }
1074
+ return paths;
1075
+ }
1076
+ function getLocalDevPath(directory) {
1077
+ for (const configPath of getConfigPaths(directory)) {
1078
+ try {
1079
+ if (!fs2.existsSync(configPath))
1080
+ continue;
1081
+ const content = fs2.readFileSync(configPath, "utf-8");
1082
+ const config = JSON.parse(stripJsonComments(content));
1083
+ const plugins = config.plugin ?? [];
1084
+ for (const entry of plugins) {
1085
+ if (entry.startsWith("file://") && entry.includes(PACKAGE_NAME)) {
1086
+ try {
1087
+ return fileURLToPath2(entry);
1088
+ } catch {
1089
+ return entry.replace("file://", "");
1090
+ }
1091
+ }
1092
+ }
1093
+ } catch {
1094
+ continue;
1095
+ }
1096
+ }
1097
+ return null;
1098
+ }
1099
+ function findPackageJsonUp(startPath) {
1100
+ try {
1101
+ const stat = fs2.statSync(startPath);
1102
+ let dir = stat.isDirectory() ? startPath : path2.dirname(startPath);
1103
+ for (let i = 0;i < 10; i++) {
1104
+ const pkgPath = path2.join(dir, "package.json");
1105
+ if (fs2.existsSync(pkgPath)) {
1106
+ try {
1107
+ const content = fs2.readFileSync(pkgPath, "utf-8");
1108
+ const pkg = JSON.parse(content);
1109
+ if (pkg.name === PACKAGE_NAME)
1110
+ return pkgPath;
1111
+ } catch {}
1112
+ }
1113
+ const parent = path2.dirname(dir);
1114
+ if (parent === dir)
1115
+ break;
1116
+ dir = parent;
1117
+ }
1118
+ } catch {}
1119
+ return null;
1120
+ }
1121
+ function getLocalDevVersion(directory) {
1122
+ const localPath = getLocalDevPath(directory);
1123
+ if (!localPath)
1124
+ return null;
1125
+ try {
1126
+ const pkgPath = findPackageJsonUp(localPath);
1127
+ if (!pkgPath)
1128
+ return null;
1129
+ const content = fs2.readFileSync(pkgPath, "utf-8");
1130
+ const pkg = JSON.parse(content);
1131
+ return pkg.version ?? null;
1132
+ } catch {
1133
+ return null;
1134
+ }
1135
+ }
1136
+ function findPluginEntry(directory) {
1137
+ for (const configPath of getConfigPaths(directory)) {
1138
+ try {
1139
+ if (!fs2.existsSync(configPath))
1140
+ continue;
1141
+ const content = fs2.readFileSync(configPath, "utf-8");
1142
+ const config = JSON.parse(stripJsonComments(content));
1143
+ const plugins = config.plugin ?? [];
1144
+ for (const entry of plugins) {
1145
+ if (entry === PACKAGE_NAME) {
1146
+ return { entry, isPinned: false, pinnedVersion: null, configPath };
1147
+ }
1148
+ if (entry.startsWith(`${PACKAGE_NAME}@`)) {
1149
+ const pinnedVersion = entry.slice(PACKAGE_NAME.length + 1);
1150
+ const isPinned = pinnedVersion !== "latest";
1151
+ return { entry, isPinned, pinnedVersion: isPinned ? pinnedVersion : null, configPath };
1152
+ }
1153
+ }
1154
+ } catch {
1155
+ continue;
1156
+ }
1157
+ }
1158
+ return null;
1159
+ }
1160
+ function getCachedVersion() {
1161
+ try {
1162
+ if (fs2.existsSync(INSTALLED_PACKAGE_JSON)) {
1163
+ const content = fs2.readFileSync(INSTALLED_PACKAGE_JSON, "utf-8");
1164
+ const pkg = JSON.parse(content);
1165
+ if (pkg.version)
1166
+ return pkg.version;
1167
+ }
1168
+ } catch {}
1169
+ try {
1170
+ const currentDir = path2.dirname(fileURLToPath2(import.meta.url));
1171
+ const pkgPath = findPackageJsonUp(currentDir);
1172
+ if (pkgPath) {
1173
+ const content = fs2.readFileSync(pkgPath, "utf-8");
1174
+ const pkg = JSON.parse(content);
1175
+ if (pkg.version)
1176
+ return pkg.version;
1177
+ }
1178
+ } catch (err) {
1179
+ log("Failed to resolve version from current directory:", err);
1180
+ }
1181
+ return null;
1182
+ }
1183
+ function updatePinnedVersion(configPath, oldEntry, newVersion) {
1184
+ try {
1185
+ const content = fs2.readFileSync(configPath, "utf-8");
1186
+ const newEntry = `${PACKAGE_NAME}@${newVersion}`;
1187
+ const pluginMatch = content.match(/"plugin"\s*:\s*\[/);
1188
+ if (!pluginMatch || pluginMatch.index === undefined) {
1189
+ log(`No "plugin" array found in ${configPath}`);
1190
+ return false;
1191
+ }
1192
+ const startIdx = pluginMatch.index + pluginMatch[0].length;
1193
+ let bracketCount = 1;
1194
+ let endIdx = startIdx;
1195
+ for (let i = startIdx;i < content.length && bracketCount > 0; i++) {
1196
+ if (content[i] === "[")
1197
+ bracketCount++;
1198
+ else if (content[i] === "]")
1199
+ bracketCount--;
1200
+ endIdx = i;
1201
+ }
1202
+ const before = content.slice(0, startIdx);
1203
+ const pluginArrayContent = content.slice(startIdx, endIdx);
1204
+ const after = content.slice(endIdx);
1205
+ const escapedOldEntry = oldEntry.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1206
+ const regex = new RegExp(`["']${escapedOldEntry}["']`);
1207
+ if (!regex.test(pluginArrayContent)) {
1208
+ log(`Entry "${oldEntry}" not found in plugin array of ${configPath}`);
1209
+ return false;
1210
+ }
1211
+ const updatedPluginArray = pluginArrayContent.replace(regex, `"${newEntry}"`);
1212
+ const updatedContent = before + updatedPluginArray + after;
1213
+ if (updatedContent === content) {
1214
+ log(`No changes made to ${configPath}`);
1215
+ return false;
1216
+ }
1217
+ fs2.writeFileSync(configPath, updatedContent, "utf-8");
1218
+ log(`Updated ${configPath}: ${oldEntry} \u2192 ${newEntry}`);
1219
+ return true;
1220
+ } catch (err) {
1221
+ log(`Failed to update config file ${configPath}:`, err);
1222
+ return false;
1223
+ }
1224
+ }
1225
+ async function getLatestVersion() {
1226
+ const controller = new AbortController;
1227
+ const timeoutId = setTimeout(() => controller.abort(), NPM_FETCH_TIMEOUT);
1228
+ try {
1229
+ const response = await fetch(NPM_REGISTRY_URL, {
1230
+ signal: controller.signal,
1231
+ headers: { Accept: "application/json" }
1232
+ });
1233
+ if (!response.ok)
1234
+ return null;
1235
+ const data = await response.json();
1236
+ return data.latest ?? null;
1237
+ } catch {
1238
+ return null;
1239
+ } finally {
1240
+ clearTimeout(timeoutId);
1241
+ }
1242
+ }
1243
+
1244
+ // lib/hooks/auto-update/cache.ts
1245
+ import * as fs3 from "fs";
1246
+ import * as path3 from "path";
1247
+ function stripTrailingCommas(json) {
1248
+ return json.replace(/,(\s*[}\]])/g, "$1");
1249
+ }
1250
+ function removeFromBunLock(packageName) {
1251
+ const lockPath = path3.join(CACHE_DIR3, "bun.lock");
1252
+ if (!fs3.existsSync(lockPath))
1253
+ return false;
1254
+ try {
1255
+ const content = fs3.readFileSync(lockPath, "utf-8");
1256
+ const lock = JSON.parse(stripTrailingCommas(content));
1257
+ let modified = false;
1258
+ if (lock.workspaces?.[""]?.dependencies?.[packageName]) {
1259
+ delete lock.workspaces[""].dependencies[packageName];
1260
+ modified = true;
1261
+ }
1262
+ if (lock.packages?.[packageName]) {
1263
+ delete lock.packages[packageName];
1264
+ modified = true;
1265
+ }
1266
+ if (modified) {
1267
+ fs3.writeFileSync(lockPath, JSON.stringify(lock, null, 2));
1268
+ log(`Removed from bun.lock: ${packageName}`);
1269
+ }
1270
+ return modified;
1271
+ } catch {
1272
+ return false;
1273
+ }
1274
+ }
1275
+ function invalidatePackage(packageName = PACKAGE_NAME) {
1276
+ try {
1277
+ const pkgDir = path3.join(CACHE_DIR3, "node_modules", packageName);
1278
+ const pkgJsonPath = path3.join(CACHE_DIR3, "package.json");
1279
+ let packageRemoved = false;
1280
+ let dependencyRemoved = false;
1281
+ let lockRemoved = false;
1282
+ if (fs3.existsSync(pkgDir)) {
1283
+ fs3.rmSync(pkgDir, { recursive: true, force: true });
1284
+ log(`Package removed: ${pkgDir}`);
1285
+ packageRemoved = true;
1286
+ }
1287
+ if (fs3.existsSync(pkgJsonPath)) {
1288
+ const content = fs3.readFileSync(pkgJsonPath, "utf-8");
1289
+ const pkgJson = JSON.parse(content);
1290
+ if (pkgJson.dependencies?.[packageName]) {
1291
+ delete pkgJson.dependencies[packageName];
1292
+ fs3.writeFileSync(pkgJsonPath, JSON.stringify(pkgJson, null, 2));
1293
+ log(`Dependency removed from package.json: ${packageName}`);
1294
+ dependencyRemoved = true;
1295
+ }
1296
+ }
1297
+ lockRemoved = removeFromBunLock(packageName);
1298
+ if (!packageRemoved && !dependencyRemoved && !lockRemoved) {
1299
+ log(`Package not found, nothing to invalidate: ${packageName}`);
1300
+ return false;
1301
+ }
1302
+ return true;
1303
+ } catch (err) {
1304
+ log("Failed to invalidate package:", err);
1305
+ return false;
1306
+ }
1307
+ }
1308
+
1309
+ // lib/hooks/auto-update/index.ts
1310
+ function createAutoUpdateHook(ctx, options = {}) {
1311
+ const { autoUpdate = true } = options;
1312
+ let hasChecked = false;
1313
+ return {
1314
+ event: async ({ event }) => {
1315
+ if (event.type !== "session.created")
1316
+ return;
1317
+ if (hasChecked)
1318
+ return;
1319
+ const props = event.properties;
1320
+ if (props?.info?.parentID)
1321
+ return;
1322
+ hasChecked = true;
1323
+ const localDevVersion = getLocalDevVersion(ctx.directory);
1324
+ if (localDevVersion) {
1325
+ log("Local development mode, skipping update check");
1326
+ return;
1327
+ }
1328
+ runBackgroundUpdateCheck(ctx, autoUpdate).catch((err) => {
1329
+ log("Background update check failed:", err);
1330
+ });
1331
+ }
1332
+ };
1333
+ }
1334
+ async function runBackgroundUpdateCheck(ctx, autoUpdate) {
1335
+ const pluginInfo = findPluginEntry(ctx.directory);
1336
+ if (!pluginInfo) {
1337
+ log("Plugin not found in config");
1338
+ return;
1339
+ }
1340
+ const cachedVersion = getCachedVersion();
1341
+ const currentVersion = cachedVersion ?? pluginInfo.pinnedVersion;
1342
+ if (!currentVersion) {
1343
+ log("No version found (cached or pinned)");
1344
+ return;
1345
+ }
1346
+ const latestVersion = await getLatestVersion();
1347
+ if (!latestVersion) {
1348
+ log("Failed to fetch latest version");
1349
+ return;
1350
+ }
1351
+ if (currentVersion === latestVersion) {
1352
+ log("Already on latest version");
1353
+ return;
1354
+ }
1355
+ log(`Update available: ${currentVersion} \u2192 ${latestVersion}`);
1356
+ if (!autoUpdate) {
1357
+ await showUpdateAvailableToast(ctx, currentVersion, latestVersion);
1358
+ log("Auto-update disabled, notification only");
1359
+ return;
1360
+ }
1361
+ if (pluginInfo.isPinned) {
1362
+ const updated = updatePinnedVersion(pluginInfo.configPath, pluginInfo.entry, latestVersion);
1363
+ if (!updated) {
1364
+ await showUpdateAvailableToast(ctx, currentVersion, latestVersion);
1365
+ log("Failed to update pinned version in config");
1366
+ return;
1367
+ }
1368
+ log(`Config updated: ${pluginInfo.entry} \u2192 ${PACKAGE_NAME}@${latestVersion}`);
1369
+ }
1370
+ invalidatePackage(PACKAGE_NAME);
1371
+ const installSuccess = await runBunInstallSafe();
1372
+ if (installSuccess) {
1373
+ await showAutoUpdatedToast(ctx, currentVersion, latestVersion);
1374
+ log(`Update installed: ${currentVersion} \u2192 ${latestVersion}`);
1375
+ } else {
1376
+ await showUpdateAvailableToast(ctx, currentVersion, latestVersion);
1377
+ log("bun install failed; update not installed (falling back to notification-only)");
1378
+ }
1379
+ }
1380
+ async function runBunInstallSafe() {
1381
+ return new Promise((resolve) => {
1382
+ try {
1383
+ const child = spawn("bun", ["install"], {
1384
+ cwd: CACHE_DIR3,
1385
+ stdio: "ignore",
1386
+ detached: true
1387
+ });
1388
+ child.unref();
1389
+ child.on("close", (code) => {
1390
+ resolve(code === 0);
1391
+ });
1392
+ child.on("error", () => {
1393
+ resolve(false);
1394
+ });
1395
+ setTimeout(() => resolve(false), 30000);
1396
+ } catch (err) {
1397
+ log("bun install error:", err);
1398
+ resolve(false);
1399
+ }
1400
+ });
1401
+ }
1402
+ async function showUpdateAvailableToast(ctx, currentVersion, latestVersion) {
1403
+ await ctx.client.tui.showToast({
1404
+ body: {
1405
+ title: `${PACKAGE_NAME} Update Available`,
1406
+ message: `v${currentVersion} \u2192 v${latestVersion}
1407
+ Restart OpenCode to apply.`,
1408
+ variant: "info",
1409
+ duration: 8000
1410
+ }
1411
+ }).catch(() => {});
1412
+ log(`Update available toast shown: v${latestVersion}`);
1413
+ }
1414
+ async function showAutoUpdatedToast(ctx, oldVersion, newVersion) {
1415
+ await ctx.client.tui.showToast({
1416
+ body: {
1417
+ title: `${PACKAGE_NAME} Updated!`,
1418
+ message: `v${oldVersion} \u2192 v${newVersion}
1419
+ Restart OpenCode to apply.`,
1420
+ variant: "success",
1421
+ duration: 8000
1422
+ }
1423
+ }).catch(() => {});
1424
+ log(`Auto-updated toast shown: v${oldVersion} \u2192 v${newVersion}`);
1425
+ }
1426
+ // lib/provider-config.json
1427
+ var provider_config_default = {
1428
+ name: "AICodewith",
1429
+ env: ["AICODEWITH_API_KEY"],
1430
+ api: "https://api.openai.com/v1",
1431
+ models: {
1432
+ "gpt-5.2-codex": {
1433
+ name: "GPT-5.2 Codex",
1434
+ modalities: { input: ["text", "image"], output: ["text"] }
1435
+ },
1436
+ "gpt-5.2": {
1437
+ name: "GPT-5.2",
1438
+ modalities: { input: ["text", "image"], output: ["text"] }
1439
+ },
1440
+ "claude-sonnet-4-5-20250929": {
1441
+ name: "Claude Sonnet 4.5",
1442
+ modalities: { input: ["text", "image"], output: ["text"] }
1443
+ },
1444
+ "claude-opus-4-5-20251101": {
1445
+ name: "Claude Opus 4.5",
1446
+ modalities: { input: ["text", "image"], output: ["text"] }
1447
+ },
1448
+ "gemini-3-pro-high": {
1449
+ name: "Gemini 3 Pro",
1450
+ modalities: { input: ["text", "image"], output: ["text"] }
1451
+ }
1452
+ }
1453
+ };
1454
+
996
1455
  // index.ts
997
1456
  var CODEX_MODEL_PREFIXES = ["gpt-", "codex"];
998
- var PACKAGE_NAME = "opencode-aicodewith-auth";
999
- var PROVIDER_NAME = "AICodewith";
1457
+ var PACKAGE_NAME2 = "opencode-aicodewith-auth";
1000
1458
  var PLUGIN_ENTRY = import.meta.url;
1001
1459
  var PROVIDER_EXT = import.meta.url.endsWith(".ts") ? ".ts" : ".js";
1002
1460
  var PROVIDER_NPM = new URL(`./provider${PROVIDER_EXT}`, import.meta.url).href;
1003
- var DEFAULT_API = "https://api.openai.com/v1";
1004
- var DEFAULT_ENV = ["AICODEWITH_API_KEY"];
1005
1461
  var DEFAULT_OUTPUT_TOKEN_MAX = 32000;
1006
- var IMAGE_MODALITIES = { input: ["text", "image"], output: ["text"] };
1007
- var MODEL_CONFIGS = {
1008
- "gpt-5.2-codex": { name: "GPT-5.2 Codex", modalities: IMAGE_MODALITIES },
1009
- "gpt-5.2": { name: "GPT-5.2", modalities: IMAGE_MODALITIES },
1010
- "claude-sonnet-4-5-20250929": { name: "Claude Sonnet 4.5", modalities: IMAGE_MODALITIES },
1011
- "claude-opus-4-5-20251101": { name: "Claude Opus 4.5", modalities: IMAGE_MODALITIES },
1012
- "gemini-3-pro-high": { name: "Gemini 3 Pro", modalities: IMAGE_MODALITIES }
1013
- };
1014
- var ALLOWED_MODEL_IDS = Object.keys(MODEL_CONFIGS);
1015
- var ALLOWED_MODEL_SET = new Set(ALLOWED_MODEL_IDS);
1016
- var homeDir = process.env.OPENCODE_TEST_HOME || os.homedir();
1017
- var configRoot = process.env.XDG_CONFIG_HOME || path.join(homeDir, ".config");
1018
- var configDir = path.join(configRoot, "opencode");
1019
- var configPath = path.join(configDir, "opencode.json");
1462
+ var homeDir = process.env.OPENCODE_TEST_HOME || os3.homedir();
1463
+ var configRoot = process.env.XDG_CONFIG_HOME || path4.join(homeDir, ".config");
1464
+ var configDir = path4.join(configRoot, "opencode");
1465
+ var configPathJson = path4.join(configDir, "opencode.json");
1466
+ var configPathJsonc = path4.join(configDir, "opencode.jsonc");
1020
1467
  var ensureConfigPromise;
1021
- var toModelMap = (ids, existing = {}) => ids.reduce((acc, id) => {
1022
- const existingConfig = Object.prototype.hasOwnProperty.call(existing, id) ? existing[id] : {};
1023
- const defaultConfig = MODEL_CONFIGS[id] ?? {};
1024
- acc[id] = { ...defaultConfig, ...typeof existingConfig === "object" ? existingConfig : {} };
1025
- return acc;
1026
- }, {});
1027
- var readJson = async (filePath) => {
1468
+ var fileExists = async (filePath) => {
1469
+ try {
1470
+ await access(filePath);
1471
+ return true;
1472
+ } catch {
1473
+ return false;
1474
+ }
1475
+ };
1476
+ var stripJsonComments2 = (content) => {
1477
+ return content.replace(/\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g, (m, g) => g ? "" : m).replace(/,(\s*[}\]])/g, "$1");
1478
+ };
1479
+ var readJsonOrJsonc = async (filePath) => {
1028
1480
  try {
1029
1481
  const text = await readFile2(filePath, "utf-8");
1030
- return JSON.parse(text);
1482
+ const stripped = filePath.endsWith(".jsonc") ? stripJsonComments2(text) : text;
1483
+ return JSON.parse(stripped);
1031
1484
  } catch {
1032
1485
  return;
1033
1486
  }
1034
1487
  };
1035
- var isPackageEntry = (value) => value === PACKAGE_NAME || value.startsWith(`${PACKAGE_NAME}@`);
1488
+ var deepEqual = (a, b) => {
1489
+ if (a === b)
1490
+ return true;
1491
+ if (typeof a !== typeof b)
1492
+ return false;
1493
+ if (a === null || b === null)
1494
+ return a === b;
1495
+ if (typeof a !== "object")
1496
+ return false;
1497
+ const aObj = a;
1498
+ const bObj = b;
1499
+ const aKeys = Object.keys(aObj);
1500
+ const bKeys = Object.keys(bObj);
1501
+ if (aKeys.length !== bKeys.length)
1502
+ return false;
1503
+ for (const key of aKeys) {
1504
+ if (!Object.prototype.hasOwnProperty.call(bObj, key))
1505
+ return false;
1506
+ if (!deepEqual(aObj[key], bObj[key]))
1507
+ return false;
1508
+ }
1509
+ return true;
1510
+ };
1511
+ var isPackageEntry = (value) => value === PACKAGE_NAME2 || value.startsWith(`${PACKAGE_NAME2}@`);
1036
1512
  var ensurePluginEntry = (list) => {
1037
1513
  if (!Array.isArray(list))
1038
1514
  return [PLUGIN_ENTRY];
1039
1515
  const hasPlugin = list.some((entry) => typeof entry === "string" && (entry === PLUGIN_ENTRY || isPackageEntry(entry)));
1040
1516
  return hasPlugin ? list : [...list, PLUGIN_ENTRY];
1041
1517
  };
1518
+ var buildStandardProviderConfig = () => ({
1519
+ ...provider_config_default,
1520
+ npm: PROVIDER_NPM
1521
+ });
1042
1522
  var applyProviderConfig = (config) => {
1043
1523
  let changed = false;
1044
1524
  const providerMap = config.provider && typeof config.provider === "object" ? config.provider : {};
1045
- const existing = providerMap[PROVIDER_ID] && typeof providerMap[PROVIDER_ID] === "object" ? providerMap[PROVIDER_ID] : {};
1046
- const existingModels = existing.models && typeof existing.models === "object" ? existing.models : {};
1047
- const next = { ...existing };
1048
- if (!next.name) {
1049
- next.name = PROVIDER_NAME;
1050
- changed = true;
1051
- }
1052
- if (!Array.isArray(next.env)) {
1053
- next.env = DEFAULT_ENV;
1054
- changed = true;
1055
- }
1056
- if (!next.npm || typeof next.npm === "string" && isPackageEntry(next.npm)) {
1057
- next.npm = PROVIDER_NPM;
1058
- changed = true;
1059
- }
1060
- if (!next.api) {
1061
- next.api = DEFAULT_API;
1062
- changed = true;
1063
- }
1064
- const hasExtraModels = Object.keys(existingModels).some((id) => !ALLOWED_MODEL_SET.has(id));
1065
- const hasMissingModels = ALLOWED_MODEL_IDS.some((id) => !Object.prototype.hasOwnProperty.call(existingModels, id));
1066
- if (!next.models || hasExtraModels || hasMissingModels) {
1067
- next.models = toModelMap(ALLOWED_MODEL_IDS, existingModels);
1068
- changed = true;
1069
- }
1070
- providerMap[PROVIDER_ID] = next;
1071
- if (config.provider !== providerMap) {
1525
+ const existingProvider = providerMap[PROVIDER_ID];
1526
+ const standardProvider = buildStandardProviderConfig();
1527
+ if (!deepEqual(existingProvider, standardProvider)) {
1528
+ providerMap[PROVIDER_ID] = standardProvider;
1072
1529
  config.provider = providerMap;
1073
1530
  changed = true;
1074
1531
  }
@@ -1083,7 +1540,20 @@ var ensureConfigFile = async () => {
1083
1540
  if (ensureConfigPromise)
1084
1541
  return ensureConfigPromise;
1085
1542
  ensureConfigPromise = (async () => {
1086
- const config = await readJson(configPath) ?? {};
1543
+ const jsoncExists = await fileExists(configPathJsonc);
1544
+ const jsonExists = await fileExists(configPathJson);
1545
+ let configPath;
1546
+ let config;
1547
+ if (jsoncExists) {
1548
+ configPath = configPathJsonc;
1549
+ config = await readJsonOrJsonc(configPath) ?? {};
1550
+ } else if (jsonExists) {
1551
+ configPath = configPathJson;
1552
+ config = await readJsonOrJsonc(configPath) ?? {};
1553
+ } else {
1554
+ configPath = configPathJson;
1555
+ config = { $schema: "https://opencode.ai/config.json" };
1556
+ }
1087
1557
  if (!config || typeof config !== "object")
1088
1558
  return;
1089
1559
  const changed = applyProviderConfig(config);
@@ -1146,14 +1616,14 @@ var ensureGeminiSseParam = (url) => {
1146
1616
  };
1147
1617
  var buildGeminiUrl = (originalUrl, streaming) => {
1148
1618
  const original = new URL(originalUrl);
1149
- let path2 = original.pathname;
1150
- if (!path2.includes("/v1beta/") && !path2.includes("/v1/")) {
1151
- path2 = `/v1beta${path2.startsWith("/") ? "" : "/"}${path2}`;
1619
+ let path5 = original.pathname;
1620
+ if (!path5.includes("/v1beta/") && !path5.includes("/v1/")) {
1621
+ path5 = `/v1beta${path5.startsWith("/") ? "" : "/"}${path5}`;
1152
1622
  }
1153
1623
  const base = new URL(AICODEWITH_GEMINI_BASE_URL);
1154
1624
  const basePath = base.pathname.replace(/\/$/, "");
1155
1625
  const target = new URL(base.origin);
1156
- target.pathname = `${basePath}${path2}`;
1626
+ target.pathname = `${basePath}${path5}`;
1157
1627
  target.search = original.search;
1158
1628
  const url = target.toString();
1159
1629
  return streaming ? ensureGeminiSseParam(url) : url;
@@ -1188,10 +1658,11 @@ var getOutputTokenLimit = (input, output) => {
1188
1658
  }
1189
1659
  return DEFAULT_OUTPUT_TOKEN_MAX;
1190
1660
  };
1191
- var AicodewithCodexAuthPlugin = async (_ctx) => {
1661
+ var AicodewithCodexAuthPlugin = async (ctx) => {
1192
1662
  await ensureConfigFile().catch((error) => {
1193
- console.warn(`[${PACKAGE_NAME}] Failed to update opencode config: ${error instanceof Error ? error.message : error}`);
1663
+ console.warn(`[${PACKAGE_NAME2}] Failed to update opencode config: ${error instanceof Error ? error.message : error}`);
1194
1664
  });
1665
+ const autoUpdateHook = createAutoUpdateHook(ctx, { autoUpdate: true });
1195
1666
  const authHook = {
1196
1667
  provider: PROVIDER_ID,
1197
1668
  loader: async (getAuth, _provider) => {
@@ -1267,6 +1738,7 @@ var AicodewithCodexAuthPlugin = async (_ctx) => {
1267
1738
  config: async (config) => {
1268
1739
  applyProviderConfig(config);
1269
1740
  },
1741
+ event: autoUpdateHook.event,
1270
1742
  "chat.params": async (input, output) => {
1271
1743
  if (input.model.providerID !== PROVIDER_ID)
1272
1744
  return;
@@ -0,0 +1 @@
1
+ export declare function invalidatePackage(packageName?: string): boolean;
@@ -0,0 +1,10 @@
1
+ import type { PluginEntryInfo } from "./types";
2
+ declare function log(...args: unknown[]): void;
3
+ export declare function isLocalDevMode(directory: string): boolean;
4
+ export declare function getLocalDevPath(directory: string): string | null;
5
+ export declare function getLocalDevVersion(directory: string): string | null;
6
+ export declare function findPluginEntry(directory: string): PluginEntryInfo | null;
7
+ export declare function getCachedVersion(): string | null;
8
+ export declare function updatePinnedVersion(configPath: string, oldEntry: string, newVersion: string): boolean;
9
+ export declare function getLatestVersion(): Promise<string | null>;
10
+ export { log };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * @file constants.ts
3
+ * @input None
4
+ * @output Auto-update configuration constants
5
+ * @pos Constants for package paths, npm registry, cache directories
6
+ */
7
+ export declare const PACKAGE_NAME = "opencode-aicodewith-auth";
8
+ export declare const NPM_REGISTRY_URL = "https://registry.npmjs.org/-/package/opencode-aicodewith-auth/dist-tags";
9
+ export declare const NPM_FETCH_TIMEOUT = 5000;
10
+ export declare const CACHE_DIR: string;
11
+ export declare const INSTALLED_PACKAGE_JSON: string;
12
+ export declare function getWindowsAppdataDir(): string | null;
13
+ export declare const USER_CONFIG_DIR: string;
14
+ export declare const USER_OPENCODE_CONFIG: string;
15
+ export declare const USER_OPENCODE_CONFIG_JSONC: string;
@@ -0,0 +1,12 @@
1
+ import type { PluginInput } from "@opencode-ai/plugin";
2
+ import type { AutoUpdateOptions } from "./types";
3
+ export declare function createAutoUpdateHook(ctx: PluginInput, options?: AutoUpdateOptions): {
4
+ event: ({ event }: {
5
+ event: {
6
+ type: string;
7
+ properties?: unknown;
8
+ };
9
+ }) => Promise<void>;
10
+ };
11
+ export type { AutoUpdateOptions } from "./types";
12
+ export { invalidatePackage } from "./cache";
@@ -0,0 +1,29 @@
1
+ export interface NpmDistTags {
2
+ latest: string;
3
+ [key: string]: string;
4
+ }
5
+ export interface OpencodeConfig {
6
+ plugin?: string[];
7
+ [key: string]: unknown;
8
+ }
9
+ export interface PackageJson {
10
+ version: string;
11
+ name?: string;
12
+ [key: string]: unknown;
13
+ }
14
+ export interface UpdateCheckResult {
15
+ needsUpdate: boolean;
16
+ currentVersion: string | null;
17
+ latestVersion: string | null;
18
+ isLocalDev: boolean;
19
+ isPinned: boolean;
20
+ }
21
+ export interface AutoUpdateOptions {
22
+ autoUpdate?: boolean;
23
+ }
24
+ export interface PluginEntryInfo {
25
+ entry: string;
26
+ isPinned: boolean;
27
+ pinnedVersion: string | null;
28
+ configPath: string;
29
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-aicodewith-auth",
3
- "version": "0.1.3",
3
+ "version": "0.1.7",
4
4
  "description": "OpenCode plugin for AICodewith authentication - Access GPT-5.2, Claude, and Gemini models through AICodewith API",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",