@mmmbuto/nexuscrew 0.9.30 → 0.9.31
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 +11 -0
- package/frontend/dist/assets/index-DaAL7P-m.js +167 -0
- package/frontend/dist/index.html +1 -1
- package/frontend/dist/version.json +1 -1
- package/lib/cli/doctor.js +31 -2
- package/lib/decks/routes.js +11 -1
- package/lib/fleet/builtin.js +13 -4
- package/lib/fleet/definitions.js +11 -3
- package/lib/fleet/managed.js +11 -1
- package/lib/fleet/runtime.js +2 -2
- package/package.json +1 -1
- package/frontend/dist/assets/index-BbcjoZS0.js +0 -167
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-DaAL7P-m.js"></script>
|
|
15
15
|
<link rel="stylesheet" crossorigin href="/assets/index-DqCfZFGi.css">
|
|
16
16
|
</head>
|
|
17
17
|
<body>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"0.9.
|
|
1
|
+
{"version":"0.9.31"}
|
package/lib/cli/doctor.js
CHANGED
|
@@ -435,8 +435,37 @@ function checkTmuxServerTermuxPreload(runtimeEnv, execImpl, opts = {}) {
|
|
|
435
435
|
let raw = '';
|
|
436
436
|
try {
|
|
437
437
|
raw = String(execImpl(opts.tmuxBin || 'tmux', ['show-environment', '-g', 'LD_PRELOAD'], { encoding: 'utf8' }) || '').trim();
|
|
438
|
-
} catch (
|
|
439
|
-
|
|
438
|
+
} catch (err) {
|
|
439
|
+
// Con il server ATTIVO `tmux show-environment -g <var>` esce 1 scrivendo
|
|
440
|
+
// "unknown variable" su stderr: execFileSync lo rigetta come errore, come
|
|
441
|
+
// fa per un server assente ("no server running"), ma i due esiti non sono
|
|
442
|
+
// la stessa cosa. L'assenza della variabile merita un WARN col rimedio,
|
|
443
|
+
// non l'esito «server non attivo».
|
|
444
|
+
const msg = String((err && (err.stderr || err.message)) || '');
|
|
445
|
+
if (/unknown variable/i.test(msg)) {
|
|
446
|
+
return {
|
|
447
|
+
name: 'tmux server termux-exec', ok: true, warn: true,
|
|
448
|
+
detail: 'server tmux attivo ma LD_PRELOAD non impostato (tmux: unknown variable); al primo avvio verra\' impostata, oppure: tmux set-environment -g LD_PRELOAD <libtermux-exec-ld-preload.so>',
|
|
449
|
+
};
|
|
450
|
+
}
|
|
451
|
+
// Classificazione dell'errore tmux: solo il «no server running» autentico
|
|
452
|
+
// dice che il server non c'è. Un «error connecting ... (Permission denied)»
|
|
453
|
+
// o qualunque altro fallimento lasciano la domanda aperta — il socket
|
|
454
|
+
// esiste ma questo utente non può leggerlo — e diventano «probe non
|
|
455
|
+
// verificabile» con lo stderr nel dettaglio, non un falso «server assente».
|
|
456
|
+
if (/permission denied/i.test(msg)) {
|
|
457
|
+
return {
|
|
458
|
+
name: 'tmux server termux-exec', ok: true, warn: true,
|
|
459
|
+
detail: `probe non verificabile: il socket tmux risponde "${msg}"; il server potrebbe essere attivo ma non leggibile con questo utente`,
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
if (/no server running|error connecting/i.test(msg)) {
|
|
463
|
+
return { name: 'tmux server termux-exec', ok: true, warn: true, detail: 'server tmux non attivo; probe rinviato al primo avvio' };
|
|
464
|
+
}
|
|
465
|
+
return {
|
|
466
|
+
name: 'tmux server termux-exec', ok: true, warn: true,
|
|
467
|
+
detail: `probe non verificabile: tmux ha risposto "${msg}"; stato del server non determinabile da qui`,
|
|
468
|
+
};
|
|
440
469
|
}
|
|
441
470
|
const match = raw.match(/^LD_PRELOAD=(.+)$/);
|
|
442
471
|
const trusted = match && trustedTermuxPreload({ ...env, LD_PRELOAD: match[1] }, { platform, home: opts.home });
|
package/lib/decks/routes.js
CHANGED
|
@@ -29,7 +29,17 @@ function decksRoutes({ cfg = {}, decksPath }) {
|
|
|
29
29
|
};
|
|
30
30
|
const find = (st, name) => st.decks.find((d) => d.name === name) || null;
|
|
31
31
|
const checkRevision = (deck, rev) => {
|
|
32
|
-
if (deck.revision !== rev)
|
|
32
|
+
if (deck.revision !== rev) {
|
|
33
|
+
// Traccia il conflitto di revisione con i campi che servono a
|
|
34
|
+
// capire chi lo ha causato (nessun merge lato server in questo ciclo).
|
|
35
|
+
console.log(JSON.stringify({
|
|
36
|
+
event: 'deck-revision-conflict',
|
|
37
|
+
name: deck.name,
|
|
38
|
+
expectedRevision: rev,
|
|
39
|
+
currentRevision: deck.revision,
|
|
40
|
+
}));
|
|
41
|
+
throw httpError(409, 'deck modificato da un’altra finestra', { current: deck });
|
|
42
|
+
}
|
|
33
43
|
};
|
|
34
44
|
|
|
35
45
|
r.get('/', (_req, res) => {
|
package/lib/fleet/builtin.js
CHANGED
|
@@ -37,7 +37,7 @@ const {
|
|
|
37
37
|
} = require('./definitions.js');
|
|
38
38
|
const {
|
|
39
39
|
publicCatalog, describeManaged, describeCatalogCredential, defaultShellEngine, defaultAgyEngine, defaultKimiEngine, defaultGrokEngine, defaultVlEngine,
|
|
40
|
-
CATALOG, credential: resolveProfileCredential, extraModelsFrom, knownMcpServerNames,
|
|
40
|
+
CATALOG, credential: resolveProfileCredential, extraModelsFrom, knownMcpServerNames, profileFor,
|
|
41
41
|
} = require('./managed.js');
|
|
42
42
|
const { probeModel } = require('./model-probe.js');
|
|
43
43
|
const { validEnvKey } = require('./env-key.js');
|
|
@@ -874,9 +874,18 @@ async function createBuiltinFleet(cfg = {}) {
|
|
|
874
874
|
}
|
|
875
875
|
// Se un engine sta USANDO questo modello, toglierlo lo renderebbe invalido
|
|
876
876
|
// al prossimo avvio — e il messaggio arriverebbe a chi non sa cosa e'
|
|
877
|
-
// cambiato. Meglio rifiutare adesso, dicendo chi lo usa.
|
|
877
|
+
// cambiato. Meglio rifiutare adesso, dicendo chi lo usa. Un engine risolve
|
|
878
|
+
// il modello dalla dichiarazione fatta sul SUO id o sul suo profilo
|
|
879
|
+
// (declaredFor cerca in [engineId, profileId]): il ritiro di
|
|
880
|
+
// {wantedId, wantedEngine} lo rompe solo se wantedEngine e' una di quelle
|
|
881
|
+
// chiavi — un altro engine con la propria dichiarazione non c'entra.
|
|
878
882
|
const usato = (defs.engines || [])
|
|
879
|
-
.filter((e) =>
|
|
883
|
+
.filter((e) => {
|
|
884
|
+
if (!e.managed || e.managed.model !== wantedId) return false;
|
|
885
|
+
if (e.id === wantedEngine) return true;
|
|
886
|
+
const profile = profileFor(e.managed.client, e.managed.provider, e.managed.credentialProfile || '');
|
|
887
|
+
return !!(profile && profile.id === wantedEngine);
|
|
888
|
+
})
|
|
880
889
|
.map((e) => e.id);
|
|
881
890
|
if (usato.length) {
|
|
882
891
|
throw httpError(409, `modello in uso: ${usato.join(', ')}`);
|
|
@@ -1264,7 +1273,7 @@ async function createBuiltinFleet(cfg = {}) {
|
|
|
1264
1273
|
engines: defs.engines.map((e) => {
|
|
1265
1274
|
const out = { ...e, envKeys: Object.keys(e.env || {}).sort() };
|
|
1266
1275
|
delete out.env;
|
|
1267
|
-
if (e.managed) out.managedInfo = describeManaged(e.managed, { ...cfg, home, extraModels: extraModelsFrom(defs) });
|
|
1276
|
+
if (e.managed) out.managedInfo = describeManaged(e.managed, { ...cfg, home, extraModels: extraModelsFrom(defs), engineId: e.id });
|
|
1268
1277
|
return out;
|
|
1269
1278
|
}),
|
|
1270
1279
|
cells: defs.cells.map((c) => {
|
package/lib/fleet/definitions.js
CHANGED
|
@@ -170,7 +170,7 @@ function cellIdFromTmuxSession(session) {
|
|
|
170
170
|
//
|
|
171
171
|
// Un modello dichiarato NON aggira il controllo: lo estende in modo esplicito.
|
|
172
172
|
// Un id mai dichiarato resta rifiutato.
|
|
173
|
-
const MAX_MODELS =
|
|
173
|
+
const MAX_MODELS = 128;
|
|
174
174
|
const MODEL_ID_MAX = 128;
|
|
175
175
|
|
|
176
176
|
function parseModel(m) {
|
|
@@ -245,7 +245,15 @@ function parseDefinitions(raw, { allowLegacyTmuxNames = true, onReject = null, o
|
|
|
245
245
|
const models = [];
|
|
246
246
|
const extraModels = new Map();
|
|
247
247
|
if (d.models !== undefined) {
|
|
248
|
-
if (!Array.isArray(d.models)
|
|
248
|
+
if (!Array.isArray(d.models)) return null;
|
|
249
|
+
if (d.models.length > MAX_MODELS) {
|
|
250
|
+
// Come per gli engine: il rifiuto NOMINA il limite, altrimenti chi
|
|
251
|
+
// dichiara si ritrova un 400 generico senza sapere che il cap era
|
|
252
|
+
// quello. `onIssue` porta campo/valore/regola al messaggio del comando
|
|
253
|
+
// (400 di una scrittura), `reject` alla lettura del file.
|
|
254
|
+
if (typeof onIssue === 'function') onIssue({ field: 'models', value: d.models.length, rule: `troppi modelli dichiarati, cap ${MAX_MODELS}` });
|
|
255
|
+
return reject(`fleet.json rifiutato: ${d.models.length} modelli dichiarati, cap ${MAX_MODELS}; riduci i modelli a ${MAX_MODELS} o meno`);
|
|
256
|
+
}
|
|
249
257
|
const seen = new Set();
|
|
250
258
|
for (const m of d.models) {
|
|
251
259
|
const model = parseModel(m);
|
|
@@ -1081,7 +1089,7 @@ function atomicWrite(p, data) {
|
|
|
1081
1089
|
}
|
|
1082
1090
|
|
|
1083
1091
|
const CAPS = {
|
|
1084
|
-
SCHEMA_VERSION, MAX_ENGINES, MAX_CELLS, MAX_ARGS, MAX_ARG_LEN,
|
|
1092
|
+
SCHEMA_VERSION, MAX_ENGINES, MAX_MODELS, MAX_CELLS, MAX_ARGS, MAX_ARG_LEN,
|
|
1085
1093
|
MAX_ENV_KEYS, MAX_ENV_KEY_LEN, MAX_ENV_VAL_LEN, MAX_LABEL_LEN,
|
|
1086
1094
|
MAX_COMMAND_LEN, MAX_CWD_LEN, MAX_MODEL_FLAG_LEN, MAX_MODEL_VAL_LEN,
|
|
1087
1095
|
MAX_PROMPTFLAG_LEN, MAX_PROMPT_LEN, MAX_CELL_COMMAND_LEN, MAX_TMUXSESSION_LEN,
|
package/lib/fleet/managed.js
CHANGED
|
@@ -1267,7 +1267,17 @@ function describeManaged(spec, cfg = {}) {
|
|
|
1267
1267
|
// (source 'unreadable', authConfigured false) non viene collassata in
|
|
1268
1268
|
// 'missing' — il discriminante e' CHI ha fallito, non il esito grezzo.
|
|
1269
1269
|
credentialSource: cred.source,
|
|
1270
|
-
configured,
|
|
1270
|
+
configured,
|
|
1271
|
+
// Catalogo builtin del profilo PIU' i modelli dichiarati in
|
|
1272
|
+
// `definitions.models[]` per questo engine (o, legacy, per il profilo).
|
|
1273
|
+
// Senza l'unione un profilo custom — che catalogo non ha — elencava `[]`
|
|
1274
|
+
// anche con due modelli dichiarati: il boot li accettava (declaredFor) ma
|
|
1275
|
+
// la finestra, che legge da qui, non li mostrava.
|
|
1276
|
+
models: [...new Set([
|
|
1277
|
+
...(profile.models || []),
|
|
1278
|
+
...declaredModelsFor(extraModels, profile.id, cfg.engineId || null).map((m) => m.id),
|
|
1279
|
+
])],
|
|
1280
|
+
defaultModel: profile.model || '',
|
|
1271
1281
|
binary: binary || '', displayName: normalized.displayName || profile.label,
|
|
1272
1282
|
reason,
|
|
1273
1283
|
};
|
package/lib/fleet/runtime.js
CHANGED
|
@@ -175,7 +175,7 @@ function createBuiltinRuntime(ctx) {
|
|
|
175
175
|
needsPi ? discoverPiModels({ ...cfg, home }) : {},
|
|
176
176
|
]);
|
|
177
177
|
const engines = cache.defs.engines.map((e) => {
|
|
178
|
-
const managed = e.managed ? describeManaged(e.managed, { ...cfg, home, extraModels: extraModelsFrom(cache.defs) }) : null;
|
|
178
|
+
const managed = e.managed ? describeManaged(e.managed, { ...cfg, home, extraModels: extraModelsFrom(cache.defs), engineId: e.id }) : null;
|
|
179
179
|
return {
|
|
180
180
|
id: e.id, label: e.label, rc: !!e.rc,
|
|
181
181
|
...(managed ? {
|
|
@@ -184,7 +184,7 @@ function createBuiltinRuntime(ctx) {
|
|
|
184
184
|
models: managed.provider === 'ollama-cloud' ? ollamaModels
|
|
185
185
|
: (managed.client === 'pi'
|
|
186
186
|
? (e.managed.provider === 'custom'
|
|
187
|
-
? [e.managed.model]
|
|
187
|
+
? [...new Set([e.managed.model, ...managed.models].filter(Boolean))]
|
|
188
188
|
: (e.managed.provider === 'native'
|
|
189
189
|
? [...new Set(Object.values(piModels).flat())]
|
|
190
190
|
: (piModels[e.managed.provider] || [])))
|