@magpie-community/opencode-qoder-auth 0.2.9 → 0.2.11

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.
Files changed (3) hide show
  1. package/README.md +8 -3
  2. package/index.mjs +38 -12
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -200,9 +200,14 @@ in-flight read and caching it in memory for an hour. The same identity
200
200
  goes on the list, the claim and a device-token retry; it is not saved in
201
201
  the account's credentials.
202
202
 
203
- - Install Qoder desktop or run Qoder CLI to make its native runtime
204
- available. The plugin finds the desktop's standard Windows/macOS
205
- location or the CLI's matching `~/.qoder/.bin/umid-*` cache.
203
+ - Install Qoder desktop, or sign in to Qoder CLI once, to make its native
204
+ runtime available. The plugin finds the desktop's `resources/umid`
205
+ (`Qoder.app` on macOS, `%LOCALAPPDATA%\Programs\Qoder` on Windows,
206
+ `/opt/Qoder` on Linux) or the runtime the CLI caches in `~/.qoder/.bin`
207
+ (`runtime-info-<platform>-<arch>-*` on macOS and Linux,
208
+ `umid-win32-x64-*\runtime-info.exe` on Windows; `QODER_CONFIG_DIR`,
209
+ `QODER_CLI_HOME` and `QODER_CONFIG_DIR_NAME` move it as they move the
210
+ CLI's folder).
206
211
  - For another installation location, set `QODER_RUNTIME_INFO` to the
207
212
  absolute path of Qoder's installed `runtime-info` executable
208
213
  (`runtime-info.exe` on Windows), with its accompanying SDK files.
package/index.mjs CHANGED
@@ -1037,23 +1037,45 @@ const CAMPAIGN_MACHINE_TTL = 60 * 60 * 1000 // Qoder's desktop client renews its
1037
1037
  // campaignRuntime uses Qoder's own native device identity helper, installed
1038
1038
  // by its desktop client or cached by its CLI. A made-up token (including the
1039
1039
  // chat envelope's machine id and type 5) doesn't reveal international claims.
1040
- async function campaignRuntime({ home = homedir(), platform = process.platform, arch = process.arch, env = process.env } = {}) {
1040
+ //
1041
+ // Where each one keeps it (Qoder desktop 0.4.3's packages, qodercli 1.1.66):
1042
+ // - desktop: <install>/resources/umid/runtime-info(.exe): Qoder.app on
1043
+ // macOS, %LOCALAPPDATA%\Programs\Qoder (the user installer) on Windows,
1044
+ // /opt/Qoder (the .deb and the .rpm) on Linux;
1045
+ // - CLI: <config>/.bin, where <config> is QODER_CONFIG_DIR, else
1046
+ // QODER_CLI_HOME (or the home folder) joined with QODER_CONFIG_DIR_NAME
1047
+ // (or .qoder): a file runtime-info-<platform>-<arch>-<hash> on macOS and
1048
+ // Linux, a folder umid-win32-<arch>-<hash> holding runtime-info.exe on
1049
+ // Windows.
1050
+ async function campaignRuntime({ home = homedir(), platform = process.platform, arch = process.arch, env = process.env, opt = "/opt" } = {}) {
1041
1051
  if (env.QODER_RUNTIME_INFO) return resolve(env.QODER_RUNTIME_INFO)
1042
1052
  const name = platform === "win32" ? "runtime-info.exe" : "runtime-info"
1053
+ const isFile = async (path) => (await stat(path).catch(() => null))?.isFile() ?? false
1043
1054
  const paths = []
1044
1055
  if (platform === "win32") {
1045
1056
  const local = env.LOCALAPPDATA || join(home, "AppData", "Local")
1046
1057
  paths.push(join(local, "Programs", "Qoder", "resources", "umid", name))
1047
1058
  } else if (platform === "darwin") {
1048
1059
  for (const dir of ["/Applications", join(home, "Applications")]) paths.push(join(dir, "Qoder.app", "Contents", "Resources", "umid", name))
1060
+ } else if (platform === "linux") {
1061
+ paths.push(join(opt, "Qoder", "resources", "umid", name))
1062
+ }
1063
+ for (const path of paths) if (await isFile(path)) return path
1064
+ const config = env.QODER_CONFIG_DIR || join(env.QODER_CLI_HOME || env.GEMINI_CLI_HOME || home, env.QODER_CONFIG_DIR_NAME || ".qoder")
1065
+ const cache = join(config, ".bin")
1066
+ const entries = await readdir(cache, { withFileTypes: true }).catch(() => [])
1067
+ const found = []
1068
+ for (const d of entries) {
1069
+ let path
1070
+ if (platform === "win32") {
1071
+ if (d.isDirectory() && d.name.startsWith(`umid-win32-${arch}-`)) path = join(cache, d.name, name)
1072
+ } else if (d.isFile() && d.name.startsWith(`runtime-info-${platform}-${arch}-`) && !d.name.endsWith(".tmp")) {
1073
+ path = join(cache, d.name)
1074
+ }
1075
+ if (path && (await isFile(path))) found.push({ path, time: (await stat(path)).mtimeMs })
1049
1076
  }
1050
- for (const path of paths) if ((await stat(path).catch(() => null))?.isFile()) return path
1051
- const cache = join(home, ".qoder", ".bin")
1052
- const dirs = (await readdir(cache, { withFileTypes: true }).catch(() => []))
1053
- .filter((d) => d.isDirectory() && d.name.startsWith(`umid-${platform}-${arch}-`))
1054
- const cached = await Promise.all(dirs.map(async (d) => ({ path: join(cache, d.name, name), time: (await stat(join(cache, d.name))).mtimeMs })))
1055
- for (const { path } of cached.sort((a, b) => b.time - a.time)) if ((await stat(path).catch(() => null))?.isFile()) return path
1056
- throw new Error("Qoder check-in: device identity runtime not found; install Qoder desktop or run Qoder CLI, or set QODER_RUNTIME_INFO to its runtime-info executable")
1077
+ if (found.length) return found.sort((a, b) => b.time - a.time)[0].path
1078
+ throw new Error("Qoder check-in: device identity runtime not found; install Qoder desktop or sign in to Qoder CLI once, or set QODER_RUNTIME_INFO to its runtime-info executable")
1057
1079
  }
1058
1080
 
1059
1081
  // Read the SDK identity as Qoder does: environment 3 is international; on
@@ -1292,6 +1314,9 @@ const makePlugin = (site) => async ({ client }) => {
1292
1314
  }
1293
1315
  // each account's listing, the model configs a request carries
1294
1316
  const listings = new Map()
1317
+ // A public and a VPC account can name the same uid but have different
1318
+ // catalogs. Keep each deployment's listing apart.
1319
+ const listingKey = (cred) => `${cred.vpc ?? ""}\0${cred.uid}`
1295
1320
  // the accounts fresh renewed: the built-in took the lapse mark off on a
1296
1321
  // renewed job token (qoderPersist), whatever the request then met
1297
1322
  const renewals = new WeakSet()
@@ -1300,7 +1325,7 @@ const makePlugin = (site) => async ({ client }) => {
1300
1325
  // says "renewed" for it
1301
1326
  const unsaid = new Set()
1302
1327
  const renewed = (cred) => {
1303
- const was = unsaid.delete(cred.uid)
1328
+ const was = unsaid.delete(listingKey(cred))
1304
1329
  return renewals.has(cred) || was
1305
1330
  }
1306
1331
 
@@ -1321,10 +1346,11 @@ const makePlugin = (site) => async ({ client }) => {
1321
1346
  })
1322
1347
 
1323
1348
  const models = async (cred, again = false) => {
1324
- let l = listings.get(cred.uid)
1349
+ const key = listingKey(cred)
1350
+ let l = listings.get(key)
1325
1351
  if (!l || again) {
1326
1352
  l = modelInfos(await fetchListing(siteOf(site, cred), cred))
1327
- listings.set(cred.uid, l)
1353
+ listings.set(key, l)
1328
1354
  }
1329
1355
  return l
1330
1356
  }
@@ -1537,7 +1563,7 @@ const makePlugin = (site) => async ({ client }) => {
1537
1563
  if (e?.expired) throw Object.assign(new Error(e.message), { signIn: "expired" })
1538
1564
  return provider.models
1539
1565
  }
1540
- if (renewals.has(cred)) unsaid.add(cred.uid)
1566
+ if (renewals.has(cred)) unsaid.add(listingKey(cred))
1541
1567
  try {
1542
1568
  const ms = await models(cred, true)
1543
1569
  if (!ms.length) return provider.models
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@magpie-community/opencode-qoder-auth",
3
- "version": "0.2.9",
3
+ "version": "0.2.11",
4
4
  "description": "Qoder subscriptions in OpenCode and magpie: Qoder (qoder.com) and Qoder CN (qoder.cn)",
5
5
  "type": "module",
6
6
  "main": "./index.mjs",