@triflux/remote 10.86.1 → 10.88.0

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.
@@ -0,0 +1,134 @@
1
+ import {
2
+ AGY_FAMILY_PROFILES,
3
+ CODEX_FAMILY_PROFILES,
4
+ readCodexModelCatalog,
5
+ resolveModelFamilies,
6
+ } from "./model-family-resolver.mjs";
7
+
8
+ export const EFFORT_ORDER = Object.freeze([
9
+ "none",
10
+ "low",
11
+ "medium",
12
+ "high",
13
+ "xhigh",
14
+ "max",
15
+ "ultra",
16
+ ]);
17
+
18
+ const LEGACY_CANONICAL_PROFILE_ALIASES = Object.freeze({
19
+ gpt56_sol_xhigh: "astra_xhigh",
20
+ gpt56_sol_max: "astra_max",
21
+ gpt56_sol_ultra: "astra_ultra",
22
+ gpt56_terra_high: "sol_high",
23
+ gpt56_terra_med: "sol_med",
24
+ });
25
+
26
+ export function normalizeCodexProfileName(profileName) {
27
+ const normalized = String(profileName ?? "").trim();
28
+ if (Object.hasOwn(LEGACY_CANONICAL_PROFILE_ALIASES, normalized))
29
+ return LEGACY_CANONICAL_PROFILE_ALIASES[normalized];
30
+ const familyName = normalized.replace(/^gpt\d+_(sol|astra|luna)_/, "$1_");
31
+ return Object.hasOwn(CODEX_FAMILY_PROFILES, familyName)
32
+ ? familyName
33
+ : normalized;
34
+ }
35
+
36
+ export function normalizeGeminiProfileName(name) {
37
+ const raw = String(name ?? "").trim();
38
+ // 옛 Flash 이름은 effort와 상관없이 Argon Low로 보낸다.
39
+ return raw.replace(/^flash\d*(?:_(?:low|high))?$/, "argon_low");
40
+ }
41
+
42
+ export function modelEffortFromProfile(cli, profile, effort) {
43
+ if (effort !== undefined) {
44
+ const definitions =
45
+ cli === "codex"
46
+ ? CODEX_FAMILY_PROFILES
47
+ : cli === "antigravity" || cli === "agy"
48
+ ? AGY_FAMILY_PROFILES
49
+ : {};
50
+ return EFFORT_ORDER.includes(effort) &&
51
+ Object.values(definitions).some(({ family }) => family === profile)
52
+ ? { family: profile, effort }
53
+ : null;
54
+ }
55
+ let definition;
56
+ if (cli === "codex") {
57
+ const normalized = normalizeCodexProfileName(profile);
58
+ const name =
59
+ normalized === "max" || normalized === "ultra"
60
+ ? `astra_${normalized}`
61
+ : normalized;
62
+ if (Object.hasOwn(CODEX_FAMILY_PROFILES, name))
63
+ definition = CODEX_FAMILY_PROFILES[name];
64
+ } else if (cli === "antigravity" || cli === "agy") {
65
+ const name = normalizeGeminiProfileName(profile);
66
+ if (Object.hasOwn(AGY_FAMILY_PROFILES, name))
67
+ definition = AGY_FAMILY_PROFILES[name];
68
+ }
69
+ return definition
70
+ ? { family: definition.family, effort: definition.effort }
71
+ : null;
72
+ }
73
+
74
+ export function profileFromModelEffort(cli, { family, effort }) {
75
+ const profiles =
76
+ cli === "codex"
77
+ ? CODEX_FAMILY_PROFILES
78
+ : cli === "antigravity" || cli === "agy"
79
+ ? AGY_FAMILY_PROFILES
80
+ : {};
81
+ return (
82
+ Object.entries(profiles).find(
83
+ ([, value]) => value.family === family && value.effort === effort,
84
+ )?.[0] || null
85
+ );
86
+ }
87
+
88
+ export function resolveCodexModelEffort({ family, effort }, opts = {}) {
89
+ const { families } = resolveModelFamilies("codex", {
90
+ ...opts,
91
+ allowCommand: false,
92
+ persist: false,
93
+ });
94
+ if (!Object.hasOwn(families, family) || !EFFORT_ORDER.includes(effort)) {
95
+ throw new Error(`알 수 없는 Codex 가족 또는 effort: ${family}/${effort}`);
96
+ }
97
+ const model = families[family].model;
98
+ const supported = readCodexModelCatalog(opts)?.models?.find(
99
+ (entry) => entry?.slug === model,
100
+ )?.supported_reasoning_levels;
101
+ if (!Array.isArray(supported)) return { family, model, effort };
102
+ const levels = supported.map((level) => level?.effort ?? level);
103
+ const selected = EFFORT_ORDER.slice(0, EFFORT_ORDER.indexOf(effort) + 1)
104
+ .filter((level) => levels.includes(level))
105
+ .at(-1);
106
+ if (!selected) {
107
+ const error = new Error(
108
+ `${model}: ${effort} 이하의 지원 effort가 없습니다`,
109
+ );
110
+ error.code = "unsupported-effort";
111
+ throw error;
112
+ }
113
+ return { family, model, effort: selected };
114
+ }
115
+
116
+ export function resolveAgyModelEffort({ family, effort }, opts = {}) {
117
+ const { families } = resolveModelFamilies("agy", opts);
118
+ if (!Object.hasOwn(families, family) || !EFFORT_ORDER.includes(effort)) {
119
+ throw new Error(`알 수 없는 agy 가족 또는 effort: ${family}/${effort}`);
120
+ }
121
+ const { model, supportedEfforts } = families[family];
122
+ if (!supportedEfforts) return { family, model, effort };
123
+ const selected = EFFORT_ORDER.slice(0, EFFORT_ORDER.indexOf(effort) + 1)
124
+ .filter((level) => supportedEfforts.includes(level))
125
+ .at(-1);
126
+ if (!selected) {
127
+ const error = new Error(
128
+ `${model}: ${effort} 이하의 지원 effort가 없습니다`,
129
+ );
130
+ error.code = "unsupported-effort";
131
+ throw error;
132
+ }
133
+ return { family, model, effort: selected };
134
+ }
@@ -39,7 +39,8 @@ export const AGY_FAMILY_PROFILES = Object.freeze({
39
39
  });
40
40
  const DAY_MS = 86_400_000;
41
41
  const CODEX_SLUG = /^gpt-(\d+(?:\.\d+)*)-(sol|astra|terra|luna)$/;
42
- const AGY_SLUG = /^gemini-(\d+(?:\.\d+)*)-(argon)-(low|medium|high)$/;
42
+ const AGY_SLUG =
43
+ /^gemini-(\d+(?:\.\d+)*)-(argon)-(none|low|medium|high|xhigh|max|ultra)$/;
43
44
 
44
45
  function compareVersions(a, b) {
45
46
  const left = a.split(".").map(Number);
@@ -59,10 +60,11 @@ export function selectCodexModel(models, family, effort) {
59
60
  return (
60
61
  match?.[2] === family &&
61
62
  item.visibility === "list" &&
62
- Array.isArray(item.supported_reasoning_levels) &&
63
- item.supported_reasoning_levels.some(
64
- (level) => (level?.effort ?? level) === effort,
65
- )
63
+ (effort === undefined ||
64
+ (Array.isArray(item.supported_reasoning_levels) &&
65
+ item.supported_reasoning_levels.some(
66
+ (level) => (level?.effort ?? level) === effort,
67
+ )))
66
68
  );
67
69
  })
68
70
  .sort((a, b) =>
@@ -89,7 +91,7 @@ export function selectAgyModel(models, family, effort) {
89
91
  const match = AGY_SLUG.exec(item?.slug);
90
92
  return (
91
93
  match?.[2] === family &&
92
- match?.[3] === effort &&
94
+ (effort === undefined || match?.[3] === effort) &&
93
95
  typeof item.model === "string"
94
96
  );
95
97
  })
@@ -203,7 +205,7 @@ export function resolveModelFamilies(provider, opts = {}) {
203
205
  if (provider === "codex") {
204
206
  const cachePath = join(paths.codexHome, "models_cache.json");
205
207
  sourceSignature = signature(cachePath);
206
- if (sourceSignature !== previous.sourceSignature)
208
+ if (sourceSignature !== previous.sourceSignature || !previous.families)
207
209
  catalog = readCodexModelCatalog(opts);
208
210
  if (
209
211
  sourceSignature.endsWith(":missing") &&
@@ -266,6 +268,8 @@ export function resolveModelFamilies(provider, opts = {}) {
266
268
  }
267
269
  const profiles = {};
268
270
  const selectedProfiles = {};
271
+ const families = {};
272
+ const selectedFamilies = {};
269
273
  const pins = readJson(paths.pinsPath) || {};
270
274
  for (const [name, definition] of Object.entries(definitions)) {
271
275
  const saved = previous.profiles?.[name];
@@ -329,13 +333,74 @@ export function resolveModelFamilies(provider, opts = {}) {
329
333
  fetched_at: null,
330
334
  };
331
335
  }
336
+ if (provider === "codex" && !Object.hasOwn(families, definition.family)) {
337
+ const latest = selectCodexModel(catalog?.models, definition.family);
338
+ const lastFamily =
339
+ previous.families?.[definition.family] || profiles[name];
340
+ families[definition.family] =
341
+ latest &&
342
+ (!lastFamily.fetched_at ||
343
+ !catalog.fetched_at ||
344
+ Date.parse(catalog.fetched_at) >= Date.parse(lastFamily.fetched_at))
345
+ ? { model: latest.slug, fetched_at: catalog.fetched_at || fetchedAt }
346
+ : { model: lastFamily.model, fetched_at: lastFamily.fetched_at };
347
+ selectedFamilies[definition.family] =
348
+ typeof pin === "string" &&
349
+ CODEX_SLUG.exec(pin)?.[2] === definition.family
350
+ ? { model: pin, source: paths.pinsPath, fetched_at: null }
351
+ : { ...families[definition.family] };
352
+ }
353
+ }
354
+ if (provider === "agy") {
355
+ const latest = selectAgyModel(catalog?.models, "argon");
356
+ // 옛 기록에는 가족 ID가 없으므로 마지막 성공 프로필의 표시 이름에서 복원한다.
357
+ const savedModels = [
358
+ ...Object.values(AGY_FAMILY_PROFILES),
359
+ ...Object.values(profiles),
360
+ ].flatMap((profile) => {
361
+ const match = /^Gemini (\d+(?:\.\d+)*) Argon \(/i.exec(profile.model);
362
+ return match
363
+ ? [{ ...profile, slug: `gemini-${match[1]}-argon-${profile.effort}` }]
364
+ : [];
365
+ });
366
+ const saved = selectAgyModel(savedModels, "argon");
367
+ const last = previous.families?.argon || {
368
+ model: saved.slug.replace(/-[^-]+$/, ""),
369
+ fetched_at: saved.fetched_at,
370
+ };
371
+ families.argon =
372
+ latest &&
373
+ (!last.fetched_at ||
374
+ !catalog.fetched_at ||
375
+ Date.parse(catalog.fetched_at) >= Date.parse(last.fetched_at))
376
+ ? {
377
+ model: latest.slug.replace(/-[^-]+$/, ""),
378
+ fetched_at: catalog.fetched_at || fetchedAt,
379
+ }
380
+ : last;
381
+ const pin = pins.argon;
382
+ selectedFamilies.argon =
383
+ typeof pin === "string" && AGY_SLUG.test(`${pin}-low`)
384
+ ? { model: pin, source: paths.pinsPath, fetched_at: null }
385
+ : { ...families.argon };
386
+ const supported = catalog?.models
387
+ ?.filter((item) =>
388
+ item.slug.startsWith(`${selectedFamilies.argon.model}-`),
389
+ )
390
+ .map((item) => AGY_SLUG.exec(item.slug)?.[3])
391
+ .filter(Boolean);
392
+ if (supported?.length) selectedFamilies.argon.supportedEfforts = supported;
332
393
  }
333
394
  const record = {
334
395
  sourceSignature,
335
396
  checked_at: checkedAt || null,
336
397
  profiles,
337
398
  selectedProfiles,
399
+ families,
338
400
  };
339
401
  if (opts.persist !== false) saveJson(recordPath, record);
340
- return { profiles: selectedProfiles };
402
+ return {
403
+ profiles: selectedProfiles,
404
+ families: selectedFamilies,
405
+ };
341
406
  }