@mmmbuto/nexuscrew 0.9.76 → 0.9.77
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/CHANGELOG.md +5 -0
- package/frontend/dist/assets/{index-BURtPwUi.js → index-Cgd3OQws.js} +29 -29
- package/frontend/dist/index.html +1 -1
- package/frontend/dist/version.json +1 -1
- package/lib/fleet/builtin.js +63 -28
- package/lib/fleet/managed.js +43 -23
- package/lib/fleet/model-discovery.js +21 -6
- package/package.json +1 -1
package/frontend/dist/index.html
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<meta name="apple-mobile-web-app-title" content="NexusCrew" />
|
|
12
12
|
<link rel="manifest" href="/manifest.json" />
|
|
13
13
|
<title>NexusCrew</title>
|
|
14
|
-
<script type="module" crossorigin src="/assets/index-
|
|
14
|
+
<script type="module" crossorigin src="/assets/index-Cgd3OQws.js"></script>
|
|
15
15
|
<link rel="stylesheet" crossorigin href="/assets/index-B9GW2LHm.css">
|
|
16
16
|
</head>
|
|
17
17
|
<body>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"0.9.
|
|
1
|
+
{"version":"0.9.77"}
|
package/lib/fleet/builtin.js
CHANGED
|
@@ -41,7 +41,7 @@ const {
|
|
|
41
41
|
CATALOG, CLIENT_LABELS, CLIENT_ORDER, engineClient, ordinaEngine, isStandardClient,
|
|
42
42
|
credential: resolveProfileCredential, extraModelsFrom, knownMcpServerNames, profileFor,
|
|
43
43
|
normalizeManagedSpec, codexSurfaceNames,
|
|
44
|
-
engineModelDiscoveryPlan, ollamaCloudCatalogModels, declaredModelsFor, ALIBABA_PI_MODELS,
|
|
44
|
+
engineModelDiscoveryPlan, modelListingCredential, ollamaCloudCatalogModels, declaredModelsFor, ALIBABA_PI_MODELS,
|
|
45
45
|
} = require('./managed.js');
|
|
46
46
|
const { probeModel, probeCustomEndpoint } = require('./model-probe.js');
|
|
47
47
|
const { createModelDiscovery, DISCOVERY_PROPOSAL_CAP } = require('./model-discovery.js');
|
|
@@ -1823,6 +1823,23 @@ async function createBuiltinFleet(cfg = {}) {
|
|
|
1823
1823
|
...(cause ? { cause } : {}),
|
|
1824
1824
|
};
|
|
1825
1825
|
}
|
|
1826
|
+
// Backup proposals for fields no layer could fill: the missing-data dialog
|
|
1827
|
+
// offers what the internal catalog knows, and the operator's confirmation
|
|
1828
|
+
// is what turns a proposal into a user value.
|
|
1829
|
+
function withBackupDefaults(records, profile) {
|
|
1830
|
+
const internalIndex = internalModelIndex(profile);
|
|
1831
|
+
return (Array.isArray(records) ? records : []).map((m) => {
|
|
1832
|
+
const backupValues = {};
|
|
1833
|
+
for (const field of ['contextWindow', 'maxTokens']) {
|
|
1834
|
+
if (m[field] === undefined) {
|
|
1835
|
+
const known = internalValueFor(internalIndex, m.id);
|
|
1836
|
+
if (known && known[field] !== undefined) backupValues[field] = known[field];
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
return Object.keys(backupValues).length ? { ...m, backupValues } : m;
|
|
1840
|
+
});
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1826
1843
|
async function discoverEngineModels(engineId, opts = {}) {
|
|
1827
1844
|
const id = String(engineId || '');
|
|
1828
1845
|
if (!id || id.length > 64 || !/^[A-Za-z0-9._-]+$/.test(id)) {
|
|
@@ -1843,16 +1860,10 @@ async function createBuiltinFleet(cfg = {}) {
|
|
|
1843
1860
|
spec = profile ? { client: profile.client, provider: profile.provider, ...(profile.credentialProfile ? { credentialProfile: profile.credentialProfile } : {}) } : null;
|
|
1844
1861
|
}
|
|
1845
1862
|
if (!profile) { const e = new Error(`engine sconosciuto: ${id}`); e.status = 404; throw e; }
|
|
1846
|
-
const plan = engineModelDiscoveryPlan(profile, spec, cfg, home, id);
|
|
1847
|
-
if (!plan) {
|
|
1848
|
-
// No HTTP listing to interrogate (login/native flows, local providers
|
|
1849
|
-
// without a base URL): an honest empty answer, never an error, so the
|
|
1850
|
-
// editor stays usable. The UI hides the section on `supported: false`.
|
|
1851
|
-
return { engine: id, supported: false, source: 'unsupported', models: [] };
|
|
1852
|
-
}
|
|
1853
1863
|
// User layer: models declared in fleet.json for this engine (or its
|
|
1854
1864
|
// profile) — the values the operator chose, always on top and never
|
|
1855
|
-
// rewritten by discovery.
|
|
1865
|
+
// rewritten by discovery. Built BEFORE the plan: an engine without a
|
|
1866
|
+
// listing still has ids worth enriching.
|
|
1856
1867
|
const extraModels = extraModelsFrom(defs);
|
|
1857
1868
|
const userModels = [];
|
|
1858
1869
|
const seenUser = new Set();
|
|
@@ -1876,33 +1887,57 @@ async function createBuiltinFleet(cfg = {}) {
|
|
|
1876
1887
|
}
|
|
1877
1888
|
const backupModels = backupModelsFor(profile);
|
|
1878
1889
|
const wanted = [...new Set([...userModels, ...backupModels].map((m) => m.id))].slice(0, 32);
|
|
1890
|
+
// The plan is the HTTP listing, when the profile has one. When it does
|
|
1891
|
+
// not, the same composition still runs with the API layer explicitly
|
|
1892
|
+
// unavailable: known ids are enriched from the public snapshot, with no
|
|
1893
|
+
// invented collector, request or auth attempt.
|
|
1894
|
+
const plan = engineModelDiscoveryPlan(profile, spec, cfg, home, id);
|
|
1895
|
+
// The active model is configuration the operator already chose. It counts
|
|
1896
|
+
// as a known id only when it is the ONLY one: with a declared model or a
|
|
1897
|
+
// profile backup the proposal already has its ids, and adding it would
|
|
1898
|
+
// duplicate a row or change the order.
|
|
1899
|
+
const activeModel = spec && typeof spec.model === 'string' ? spec.model.trim() : '';
|
|
1900
|
+
const knownIds = !userModels.length && !backupModels.length && activeModel ? [activeModel] : [];
|
|
1879
1901
|
const result = await engineModelDiscovery.discoverEngine({
|
|
1880
|
-
...plan,
|
|
1902
|
+
...(plan || {}),
|
|
1903
|
+
engine: id,
|
|
1881
1904
|
home,
|
|
1882
1905
|
userModels, backupModels,
|
|
1906
|
+
knownIds,
|
|
1883
1907
|
...(wanted.length ? { detailIds: wanted } : {}),
|
|
1884
|
-
publicCatalog: {
|
|
1908
|
+
publicCatalog: {
|
|
1909
|
+
provider: profile.provider,
|
|
1910
|
+
...(plan && plan.baseUrl ? { baseUrl: plan.baseUrl } : {}),
|
|
1911
|
+
...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),
|
|
1912
|
+
},
|
|
1885
1913
|
...(opts.fetchImpl ? { fetchImpl: opts.fetchImpl } : {}),
|
|
1886
1914
|
...(opts.refresh === true ? { noCache: true } : {}),
|
|
1915
|
+
...(plan ? {} : { apiAvailable: false }),
|
|
1887
1916
|
});
|
|
1888
1917
|
// Curated response: `plan` (baseUrl, apiKey) stays out; apiModels is
|
|
1889
|
-
// already merged (with provenance) into `models`.
|
|
1890
|
-
//
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1918
|
+
// already merged (with provenance) into `models`. `backupValues` rides
|
|
1919
|
+
// along only for fields NO layer could fill.
|
|
1920
|
+
const models = withBackupDefaults(result.models, profile);
|
|
1921
|
+
if (!plan) {
|
|
1922
|
+
const cred = modelListingCredential(profile, spec, cfg, home);
|
|
1923
|
+
return {
|
|
1924
|
+
engine: id,
|
|
1925
|
+
supported: false,
|
|
1926
|
+
source: 'unsupported',
|
|
1927
|
+
status: result.status,
|
|
1928
|
+
stale: false,
|
|
1929
|
+
error: '',
|
|
1930
|
+
auth: {
|
|
1931
|
+
credentialState: cred.credentialState,
|
|
1932
|
+
...(cred.envKey ? { envKey: cred.envKey } : {}),
|
|
1933
|
+
attempted: false,
|
|
1934
|
+
credentialSent: false,
|
|
1935
|
+
},
|
|
1936
|
+
...(result.publicCatalog ? { publicCatalog: result.publicCatalog } : {}),
|
|
1937
|
+
models,
|
|
1938
|
+
cap: DISCOVERY_PROPOSAL_CAP,
|
|
1939
|
+
};
|
|
1940
|
+
}
|
|
1906
1941
|
return {
|
|
1907
1942
|
engine: id,
|
|
1908
1943
|
supported: true,
|
package/lib/fleet/managed.js
CHANGED
|
@@ -1313,52 +1313,72 @@ const sharedModelDiscovery = createModelDiscovery();
|
|
|
1313
1313
|
// (catalog engines) the profile id IS the engine id.
|
|
1314
1314
|
function engineModelDiscoveryPlan(profile, spec, cfg, home, engineId) {
|
|
1315
1315
|
if (!profile) return null;
|
|
1316
|
-
const
|
|
1317
|
-
|
|
1316
|
+
const httpUrl = (value) => (typeof value === 'string' && /^https?:\/\//i.test(value) ? value : '');
|
|
1317
|
+
// Endpoint precedence follows the launcher: the normalized spec, the
|
|
1318
|
+
// generated provider extension, then the profile's own endpoint. A native
|
|
1319
|
+
// provider has no explicit endpoint here, and is never pointed at another
|
|
1320
|
+
// client's profile.
|
|
1321
|
+
const specBase = httpUrl(spec && spec.baseUrl);
|
|
1322
|
+
const extensionBase = httpUrl(profile.piExtension && profile.piExtension.baseUrl);
|
|
1323
|
+
const profileBase = httpUrl(profile.endpoint);
|
|
1324
|
+
// The wire name may come hyphenated (`openai-completions`, the Pi form) or
|
|
1325
|
+
// underscored (the normalized form). It is the same transport.
|
|
1326
|
+
const rawWire = spec && typeof spec.protocol === 'string' && spec.protocol
|
|
1327
|
+
? spec.protocol : (typeof profile.protocol === 'string' ? profile.protocol : '');
|
|
1328
|
+
const wire = rawWire.replace(/-/g, '_');
|
|
1318
1329
|
let collector = null;
|
|
1319
1330
|
let baseUrl = '';
|
|
1320
1331
|
if (profile.provider === 'ollama' || profile.provider === 'ollama-cloud') {
|
|
1332
|
+
// Ollama speaks its own listing API, whatever wire a client uses for chat.
|
|
1321
1333
|
collector = 'ollama';
|
|
1322
|
-
baseUrl = specBase || profileBase || 'http://127.0.0.1:11434';
|
|
1334
|
+
baseUrl = specBase || extensionBase || profileBase || 'http://127.0.0.1:11434';
|
|
1323
1335
|
} else if (profile.provider === 'lmstudio') {
|
|
1324
1336
|
collector = 'lmstudio';
|
|
1325
|
-
baseUrl = specBase || 'http://127.0.0.1:1234';
|
|
1326
|
-
} else if (
|
|
1337
|
+
baseUrl = specBase || extensionBase || 'http://127.0.0.1:1234';
|
|
1338
|
+
} else if (wire === 'anthropic_messages') {
|
|
1327
1339
|
collector = 'anthropic';
|
|
1328
|
-
baseUrl = specBase || profileBase;
|
|
1329
|
-
} else if (
|
|
1340
|
+
baseUrl = specBase || extensionBase || profileBase;
|
|
1341
|
+
} else if (wire === 'openai_responses' || wire === 'openai_completions') {
|
|
1330
1342
|
collector = 'openai-compat';
|
|
1331
|
-
baseUrl = specBase || profileBase;
|
|
1343
|
+
baseUrl = specBase || extensionBase || profileBase;
|
|
1332
1344
|
} else {
|
|
1345
|
+
// `pi_native`, login flows and any other client-owned wire: no explicit
|
|
1346
|
+
// listing contract, so no collector. An explicit endpoint alone is not
|
|
1347
|
+
// proof of a listing API.
|
|
1333
1348
|
return null;
|
|
1334
1349
|
}
|
|
1335
1350
|
if (!baseUrl) return null;
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1351
|
+
return { engine: engineId || profile.id, collector, baseUrl, ...modelListingCredential(profile, spec, cfg, home) };
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
// Credential resolution for a model listing, from the same path the launcher
|
|
1355
|
+
// uses (the caller never re-reads the credential sources): none / configured /
|
|
1356
|
+
// missing / unavailable, plus the env NAME when one exists and the editor
|
|
1357
|
+
// already shows it. A file that exists but cannot be read is "unavailable",
|
|
1358
|
+
// not "missing" — the same distinction describeManaged makes for launching.
|
|
1359
|
+
// The value, its provenance paths/hashes and any exception text stay here:
|
|
1360
|
+
// the value is handed to a collector in memory and nothing else leaves. An
|
|
1361
|
+
// engine with no listing still gets the STATE: the diagnosis refers to the
|
|
1362
|
+
// listing only, never to the client's own login.
|
|
1363
|
+
function modelListingCredential(profile, spec, cfg, home) {
|
|
1364
|
+
const out = { credentialState: 'unavailable' };
|
|
1345
1365
|
try {
|
|
1346
1366
|
const cred = credential(profile, spec || {}, cfg, home, { blocked: [] });
|
|
1347
|
-
|
|
1367
|
+
out.credentialState = !cred ? 'unavailable'
|
|
1348
1368
|
: cred.value ? 'configured'
|
|
1349
1369
|
: cred.source === 'unreadable' ? 'unavailable'
|
|
1350
1370
|
: cred.source === 'missing' ? 'missing'
|
|
1351
1371
|
: 'none';
|
|
1352
|
-
if (cred && cred.value)
|
|
1372
|
+
if (cred && cred.value) out.apiKey = cred.value;
|
|
1353
1373
|
const envKey = cred && typeof cred.envKey === 'string' ? cred.envKey : '';
|
|
1354
1374
|
// 'login'/'none' resolve to a pseudo key name: never invent one for them.
|
|
1355
|
-
if (
|
|
1375
|
+
if (out.credentialState !== 'none' && ENV_KEY_RE.test(envKey || '')) out.envKey = envKey;
|
|
1356
1376
|
} catch (_) {
|
|
1357
1377
|
// Resolution itself failed: the key is not known to be missing — it is
|
|
1358
1378
|
// unavailable, and an anonymous rejection will say exactly that.
|
|
1359
|
-
|
|
1379
|
+
out.credentialState = 'unavailable';
|
|
1360
1380
|
}
|
|
1361
|
-
return
|
|
1381
|
+
return out;
|
|
1362
1382
|
}
|
|
1363
1383
|
async function discoverOllamaModels(opts = {}) {
|
|
1364
1384
|
const now = Date.now(); const ttl = opts.ttlMs === undefined ? 30000 : opts.ttlMs;
|
|
@@ -3165,7 +3185,7 @@ module.exports = {
|
|
|
3165
3185
|
OPENCODE_GO_MESSAGES_MODELS, OPENCODE_GO_RESPONSES_MODELS, OPENCODE_GO_CHAT_MODELS, OPENCODE_GO_LIMITS,
|
|
3166
3186
|
OPENCODE_GO_ANTHROPIC_ROOT, OPENCODE_GO_API_BASE,
|
|
3167
3187
|
CLIENT_LABELS, CLIENT_ORDER, engineClient, ordinaEngine, isStandardClient, normalizeManagedSpec, profileFor,
|
|
3168
|
-
defaultDefinitions, defaultShellEngine, defaultAgyEngine, defaultKimiEngine, defaultGrokEngine, defaultVlEngine, describeManaged, describeCatalogCredential, discoverOllamaModels, engineModelDiscoveryPlan, resolveManagedEngine, needsExplicitNode,
|
|
3188
|
+
defaultDefinitions, defaultShellEngine, defaultAgyEngine, defaultKimiEngine, defaultGrokEngine, defaultVlEngine, describeManaged, describeCatalogCredential, discoverOllamaModels, engineModelDiscoveryPlan, modelListingCredential, resolveManagedEngine, needsExplicitNode,
|
|
3169
3189
|
discoverPiModels, EXTERNAL_DISCOVERY_TIMEOUT_MS, parseEnvFile, parseProviderShellFile, findBinary, publicCatalog, writePiProviderExtension, customCatalogFor,
|
|
3170
3190
|
codexSurfaceNames, writeCellCodexProfile,
|
|
3171
3191
|
writeVlCellPrompt, vlSystemAppendGate,
|
|
@@ -44,7 +44,9 @@ const CACHE_VERSION = 1;
|
|
|
44
44
|
const MAX_SHOW_CALLS = 32; // Ollama /api/show: only listed ids the caller asked about
|
|
45
45
|
const ANTHROPIC_MAX_PAGES = 5; // pagination safety bound
|
|
46
46
|
const ANTHROPIC_VERSION = '2023-06-01';
|
|
47
|
-
|
|
47
|
+
// Bracketed markers such as `[1m]` are part of the model id the fleet file
|
|
48
|
+
// accepts; they are kept intact and never stripped for a basename match.
|
|
49
|
+
const MODEL_ID_RE = /^[A-Za-z0-9][A-Za-z0-9._:/\[\]-]{0,127}$/;
|
|
48
50
|
const ENGINE_KEY_RE = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
|
49
51
|
const MERGE_FIELDS = Object.freeze(['contextWindow', 'maxTokens', 'tools', 'vision', 'reasoning', 'deprecated']);
|
|
50
52
|
|
|
@@ -986,24 +988,37 @@ function createModelDiscovery({ fetchImpl: defaultFetchImpl = globalThis.fetch,
|
|
|
986
988
|
async function discoverEngine(opts = {}) {
|
|
987
989
|
const {
|
|
988
990
|
userModels = [], backupModels = [], publicCatalogModels, publicCatalog,
|
|
989
|
-
cap = DISCOVERY_PROPOSAL_CAP,
|
|
991
|
+
cap = DISCOVERY_PROPOSAL_CAP, apiAvailable = true, knownIds = [],
|
|
990
992
|
} = opts;
|
|
991
|
-
|
|
993
|
+
// An engine with no listing endpoint still has ids worth enriching: the
|
|
994
|
+
// declared models and the profile backup. There is no API layer at all —
|
|
995
|
+
// not a fake collector and not a fake fetch — so the merge runs with the
|
|
996
|
+
// API explicitly unavailable and never claims an API answer.
|
|
997
|
+
const layer = apiAvailable
|
|
998
|
+
? await fetchEngineModels(opts)
|
|
999
|
+
: { status: 'unsupported', fetchedAt: 0, apiModels: [], stale: false, error: '', disappeared: [] };
|
|
992
1000
|
let publicModels = Array.isArray(publicCatalogModels) ? publicCatalogModels : [];
|
|
993
1001
|
let publicLayer = null;
|
|
1002
|
+
// Ids that are known from configuration but are not a user declaration
|
|
1003
|
+
// (for example the engine's own active model). They join the public layer
|
|
1004
|
+
// as bare ids: they can be enriched, never invented with values, and they
|
|
1005
|
+
// stay after the declared ids in the proposal order.
|
|
1006
|
+
const configuredIds = (Array.isArray(knownIds) ? knownIds : [])
|
|
1007
|
+
.filter((id) => typeof id === 'string' && id);
|
|
1008
|
+
const knownLayerIds = [...(layer.disappeared || []), ...configuredIds];
|
|
994
1009
|
if (!Array.isArray(publicCatalogModels) && publicCatalog) {
|
|
995
1010
|
publicLayer = await resolvePublicCatalogLayer(
|
|
996
|
-
opts, layer.apiModels, userModels, backupModels,
|
|
1011
|
+
opts, layer.apiModels, userModels, backupModels, knownLayerIds
|
|
997
1012
|
);
|
|
998
1013
|
publicModels = publicLayer.records;
|
|
999
1014
|
}
|
|
1000
|
-
const apiKnown = layer.status !== 'backup';
|
|
1015
|
+
const apiKnown = apiAvailable && layer.status !== 'backup';
|
|
1001
1016
|
const models = mergeModelRecords({
|
|
1002
1017
|
userModels,
|
|
1003
1018
|
apiModels: layer.apiModels,
|
|
1004
1019
|
publicCatalogModels: publicModels,
|
|
1005
1020
|
backupModels,
|
|
1006
|
-
knownModels:
|
|
1021
|
+
knownModels: knownLayerIds.map((id) => ({ id })),
|
|
1007
1022
|
apiKnown,
|
|
1008
1023
|
fetchedAt: layer.fetchedAt || now(),
|
|
1009
1024
|
cap,
|