@melaya/runner 1.0.74 → 1.0.76

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/detect.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export interface DetectedModel {
2
- provider: "lmstudio" | "ollama" | "claude_code";
2
+ provider: "lmstudio" | "ollama" | "claude_code" | "codex";
3
3
  name: string;
4
4
  }
5
5
  export declare function detectModels(): Promise<DetectedModel[]>;
package/dist/detect.js CHANGED
@@ -8,6 +8,64 @@ import { readFile } from "fs/promises";
8
8
  import { homedir } from "os";
9
9
  import { join } from "path";
10
10
  const execFileAsync = promisify(execFile);
11
+ /** Fallbacks used ONLY when live discovery fails (offline runner, transient
12
+ * error, missing cache). The REAL lists are fetched dynamically below so a new
13
+ * claude/codex release is available on Melaya automatically — no code change,
14
+ * no redeploy. Kept minimal + in step with the registry `staticModels`. */
15
+ const CODEX_MODELS_FALLBACK = ["gpt-5.5", "gpt-5.4", "gpt-5.4-mini"];
16
+ const CLAUDE_ALIASES_FALLBACK = ["sonnet", "opus", "haiku"];
17
+ /** Discover the claude model FAMILIES this subscription serves, so a new
18
+ * family (a future tier) appears on Melaya automatically. Hits the same
19
+ * GET /v1/models the runtime resolver uses (model.py::_resolve_claude_model_id)
20
+ * with the OAuth token, then reduces concrete ids (claude-<family>-<ver>-<date>)
21
+ * to their family aliases (opus/sonnet/haiku/…). Each alias resolves to the
22
+ * NEWEST model of its family at call time, so within-family releases are picked
23
+ * up for free too. Best-effort: any failure falls back to the known aliases. */
24
+ async function fetchClaudeFamilies(token) {
25
+ try {
26
+ const r = await fetch("https://api.anthropic.com/v1/models?limit=100", {
27
+ headers: {
28
+ Authorization: `Bearer ${token}`,
29
+ "anthropic-version": "2023-06-01",
30
+ "anthropic-beta": "oauth-2025-04-20",
31
+ "x-app": "cli",
32
+ "User-Agent": "claude-code/2.1.81",
33
+ },
34
+ signal: AbortSignal.timeout(6000),
35
+ });
36
+ if (r.ok) {
37
+ const data = (await r.json());
38
+ const fams = new Set();
39
+ for (const m of data.data ?? []) {
40
+ const mm = /^claude-([a-z]+)-/.exec(m?.id ?? "");
41
+ if (mm)
42
+ fams.add(mm[1]);
43
+ }
44
+ if (fams.size)
45
+ return [...fams];
46
+ }
47
+ }
48
+ catch { /* offline / transient → fallback */ }
49
+ return CLAUDE_ALIASES_FALLBACK;
50
+ }
51
+ /** Read the codex model catalog from the CLI's OWN auto-refreshed cache
52
+ * (~/.codex/models_cache.json, written by codex when it lists models), so a
53
+ * new codex release appears on Melaya automatically. File-based like auth.json
54
+ * — no API call, no subprocess/terminal. Filters non-chat helper slugs
55
+ * (e.g. codex-auto-review). Falls back to a known set. */
56
+ async function readCodexModels() {
57
+ try {
58
+ const raw = await readFile(join(homedir(), ".codex", "models_cache.json"), "utf8");
59
+ const j = JSON.parse(raw);
60
+ const slugs = (j.models ?? [])
61
+ .map((m) => m?.slug ?? "")
62
+ .filter((s) => s && !/(^|-)(auto|review|embed|moderation)(-|$)/i.test(s));
63
+ if (slugs.length)
64
+ return slugs;
65
+ }
66
+ catch { /* no cache / malformed → fallback */ }
67
+ return CODEX_MODELS_FALLBACK;
68
+ }
11
69
  /** Claude Code is a subscription CLI — when it's logged in on this machine,
12
70
  * report its model aliases so the platform's model picker shows TRUE
13
71
  * availability for local runs (claude_code pipelines execute HERE).
@@ -41,12 +99,43 @@ async function detectClaudeCode() {
41
99
  const oauth = (JSON.parse(raw)?.claudeAiOauth ?? {});
42
100
  const expiresAt = Number(oauth.expiresAt ?? 0);
43
101
  if (oauth.accessToken && (!expiresAt || Date.now() < expiresAt)) {
44
- return ["sonnet", "opus", "haiku"].map((name) => ({ provider: "claude_code", name }));
102
+ const names = await fetchClaudeFamilies(oauth.accessToken);
103
+ return names.map((name) => ({ provider: "claude_code", name }));
45
104
  }
46
105
  }
47
106
  catch { /* malformed creds → treat as logged out */ }
48
107
  return []; // not logged in / expired → don't advertise models
49
108
  }
109
+ /** OpenAI Codex CLI is a subscription CLI (ChatGPT Plus/Pro) — when it's
110
+ * logged in on this machine, advertise its models so the platform's picker
111
+ * shows TRUE availability for local runs (codex pipelines execute HERE).
112
+ *
113
+ * Same file-based approach as `detectClaudeCode`: read `~/.codex/auth.json`
114
+ * directly rather than spawning `codex login status`. The subprocess flashed
115
+ * a console window on Windows on every runner start (`windowsHide` is
116
+ * unreliable through the `.cmd` shell wrapper). The file is the source of
117
+ * truth cross-platform and mirrors what the runtime (model.py) authenticates
118
+ * against. A ChatGPT login stores `tokens.access_token`; an API-key login
119
+ * stores `OPENAI_API_KEY`. Either means "logged in". */
120
+ async function detectCodex() {
121
+ let raw = null;
122
+ try {
123
+ raw = await readFile(join(homedir(), ".codex", "auth.json"), "utf8");
124
+ }
125
+ catch {
126
+ return [];
127
+ }
128
+ try {
129
+ const auth = JSON.parse(raw);
130
+ const loggedIn = !!(auth?.tokens?.access_token || auth?.OPENAI_API_KEY);
131
+ if (loggedIn) {
132
+ const names = await readCodexModels();
133
+ return names.map((name) => ({ provider: "codex", name }));
134
+ }
135
+ }
136
+ catch { /* malformed creds → treat as logged out */ }
137
+ return []; // not logged in → don't advertise models
138
+ }
50
139
  export async function detectModels() {
51
140
  const models = [];
52
141
  // LM Studio — OpenAI-compatible at :1234
@@ -73,5 +162,7 @@ export async function detectModels() {
73
162
  catch { /* not running */ }
74
163
  // Claude Code CLI — subscription auth on THIS machine
75
164
  models.push(...await detectClaudeCode());
165
+ // OpenAI Codex CLI — subscription auth on THIS machine
166
+ models.push(...await detectCodex());
76
167
  return models;
77
168
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@melaya/runner",
3
- "version": "1.0.74",
3
+ "version": "1.0.76",
4
4
  "description": "Run Melaya AI pipelines locally with your own LM Studio or Ollama models",
5
5
  "license": "UNLICENSED",
6
6
  "private": false,