@mmmbuto/nexuscrew 0.9.73 → 0.9.74
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/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-DRXfJssx.js"></script>
|
|
15
15
|
<link rel="stylesheet" crossorigin href="/assets/index-C_z6r985.css">
|
|
16
16
|
</head>
|
|
17
17
|
<body>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"0.9.
|
|
1
|
+
{"version":"0.9.74"}
|
package/lib/fleet/builtin.js
CHANGED
|
@@ -38,7 +38,8 @@ const {
|
|
|
38
38
|
} = require('./definitions.js');
|
|
39
39
|
const {
|
|
40
40
|
publicCatalog, describeManaged, describeCatalogCredential, defaultShellEngine, defaultAgyEngine, defaultKimiEngine, defaultGrokEngine, defaultVlEngine,
|
|
41
|
-
CATALOG,
|
|
41
|
+
CATALOG, CLIENT_LABELS, CLIENT_ORDER, engineClient, ordinaEngine, isStandardClient,
|
|
42
|
+
credential: resolveProfileCredential, extraModelsFrom, knownMcpServerNames, profileFor,
|
|
42
43
|
normalizeManagedSpec, codexSurfaceNames,
|
|
43
44
|
} = require('./managed.js');
|
|
44
45
|
const { probeModel, probeCustomEndpoint } = require('./model-probe.js');
|
|
@@ -696,10 +697,14 @@ async function createBuiltinFleet(cfg = {}) {
|
|
|
696
697
|
}
|
|
697
698
|
boot = persistito;
|
|
698
699
|
}
|
|
699
|
-
|
|
700
|
+
// L'ordine delle aggiunte segue CLIENT_ORDER (agy, grok, shell...) e poi i
|
|
701
|
+
// client fuori lista in ordine stabile (kimi, vl): un fleet.json backfillato
|
|
702
|
+
// nasce gia' nell'ordine che il payload mostra. Ogni backfill resta
|
|
703
|
+
// idempotente e indipendente: il riordino non cambia cosa viene aggiunto.
|
|
700
704
|
boot = backfillAgyEngine(defsPath, boot, cfg);
|
|
701
|
-
boot = backfillKimiEngine(defsPath, boot, cfg.log);
|
|
702
705
|
boot = backfillGrokEngine(defsPath, boot, cfg);
|
|
706
|
+
boot = backfillShellEngine(defsPath, boot, cfg.log);
|
|
707
|
+
boot = backfillKimiEngine(defsPath, boot, cfg.log);
|
|
703
708
|
boot = backfillVlEngine(defsPath, boot, cfg.log);
|
|
704
709
|
// Ripara (non aggiunge) un desktop.local gia' presente col comando relativo.
|
|
705
710
|
// Il logger va PASSATO: senza, il messaggio di fallimento si ferma a un
|
|
@@ -1204,7 +1209,10 @@ async function createBuiltinFleet(cfg = {}) {
|
|
|
1204
1209
|
// rifiutato se la dichiarazione non c'e' ancora — fail-closed corretto, ma
|
|
1205
1210
|
// che al ripristino si legge come «il backup era rotto».
|
|
1206
1211
|
const models = Array.isArray(opts.models) ? opts.models : [];
|
|
1207
|
-
|
|
1212
|
+
// Il tetto dei modelli accettati e' lo stesso della validazione del
|
|
1213
|
+
// fleet.json (CAPS.MAX_MODELS): un hardcode separato qui era gia'
|
|
1214
|
+
// divergente (64 contro 128) e rifiutava backup validi.
|
|
1215
|
+
if (models.length > CAPS.MAX_MODELS) throw httpError(400, `troppi modelli nel backup (max ${CAPS.MAX_MODELS})`);
|
|
1208
1216
|
for (const m of models) {
|
|
1209
1217
|
if (!m || typeof m !== 'object' || Array.isArray(m)) throw httpError(400, 'definizione modello non valida');
|
|
1210
1218
|
if (typeof m.id !== 'string' || !m.id.trim() || typeof m.engine !== 'string' || !m.engine.trim()) {
|
|
@@ -1378,9 +1386,20 @@ async function createBuiltinFleet(cfg = {}) {
|
|
|
1378
1386
|
const realHome = resolveCwd(home, home);
|
|
1379
1387
|
return {
|
|
1380
1388
|
schemaVersion: defs.schemaVersion,
|
|
1381
|
-
|
|
1389
|
+
// UNICO punto di ordinamento del payload: API, UI e tool MCP leggono
|
|
1390
|
+
// tutti da qui. ordinaEngine riordina in lettura: il fleet.json resta
|
|
1391
|
+
// com'e', mai riscritto.
|
|
1392
|
+
engines: ordinaEngine(defs.engines).map((e) => {
|
|
1382
1393
|
const out = { ...e, envKeys: Object.keys(e.env || {}).sort() };
|
|
1383
1394
|
delete out.env;
|
|
1395
|
+
// `standard`: calcolato dal client (CLIENT_ORDER), mai persistito nel
|
|
1396
|
+
// fleet.json. La UI ci divide i selettori in due gruppi senza
|
|
1397
|
+
// duplicare la lista dei client standard.
|
|
1398
|
+
out.standard = isStandardClient(engineClient(e));
|
|
1399
|
+
// Le etichette escono in minuscolo: vale per le label generate dai
|
|
1400
|
+
// client e anche per quelle scritte a mano nel fleet.json, che nel
|
|
1401
|
+
// file restano esattamente come le ha scritte l'operatore.
|
|
1402
|
+
if (typeof out.label === 'string' && out.label) out.label = out.label.toLowerCase();
|
|
1384
1403
|
if (e.managed) out.managedInfo = describeManaged(e.managed, {
|
|
1385
1404
|
...cfg, home, extraModels: extraModelsFrom(defs), engineId: e.id,
|
|
1386
1405
|
endpointProbe: cfg.endpointProbe || defaultEndpointProbe,
|
|
@@ -1541,7 +1560,9 @@ async function createBuiltinFleet(cfg = {}) {
|
|
|
1541
1560
|
rc: { type: 'boolean', required: false, default: false },
|
|
1542
1561
|
managed: {
|
|
1543
1562
|
type: 'object', requiredFor: 'managed',
|
|
1544
|
-
|
|
1563
|
+
// Enum allineato a CLIENT_ORDER (standard prima, nell'ordine voluto)
|
|
1564
|
+
// e poi i client noti fuori lista, in ordine stabile.
|
|
1565
|
+
client: { type: 'enum', values: [...CLIENT_ORDER, ...Object.keys(CLIENT_LABELS).filter((c) => !CLIENT_ORDER.includes(c))] },
|
|
1545
1566
|
provider: { type: 'catalog', source: 'managedCatalog' },
|
|
1546
1567
|
credentialProfile: { type: 'string', required: false, max: 32 },
|
|
1547
1568
|
model: { type: 'string', required: false, max: CAPS.MAX_MODEL_VAL_LEN },
|
package/lib/fleet/managed.js
CHANGED
|
@@ -446,7 +446,40 @@ const CREDENTIAL_SOURCE_VALUES = Object.freeze([
|
|
|
446
446
|
'providers-shell', 'keys-file', 'secure-file', 'legacy',
|
|
447
447
|
'compatibility', 'missing', 'unreadable',
|
|
448
448
|
]);
|
|
449
|
-
|
|
449
|
+
// Etichette dei client in minuscolo (decisione operatore 2026-10-17): il nome
|
|
450
|
+
// mostrato e' il client stesso. La normalizzazione vale in uscita (payload,
|
|
451
|
+
// UI, tool MCP); il testo scritto a mano nel fleet.json resta nel file cosi'
|
|
452
|
+
// com'e' e viene mostrato in minuscolo dal punto di uscita del payload.
|
|
453
|
+
const CLIENT_LABELS = Object.freeze({ claude: 'claude', codex: 'codex', 'codex-vl': 'codex-vl', grok: 'grok', vl: 'vl', pi: 'pi', opencode: 'opencode', agy: 'agy', kimi: 'kimi', shell: 'shell' });
|
|
454
|
+
// Client "standard" nell'ordine voluto dall'operatore (2026-10-17). E' LA
|
|
455
|
+
// lista: il campo calcolato `standard` del payload delle definizioni, il rango
|
|
456
|
+
// di ordinaEngine(), l'ordine del seed e l'enum client dello schema derivano
|
|
457
|
+
// tutti da qui. I client fuori lista (vl, kimi, custom...) sono supportati:
|
|
458
|
+
// chiudono l'elenco in ordine stabile, mai buttati via.
|
|
459
|
+
const CLIENT_ORDER = Object.freeze(['claude', 'codex-vl', 'codex', 'pi', 'opencode', 'agy', 'grok', 'shell']);
|
|
460
|
+
// Il client di un engine: managed.client quando c'e' (engine gestiti), primo
|
|
461
|
+
// segmento dell'id altrimenti (claude.hosted -> claude): cosi' i derivati
|
|
462
|
+
// restano attaccati al loro client anche quando nascono come id.
|
|
463
|
+
function engineClient(engine) {
|
|
464
|
+
if (engine && engine.managed && typeof engine.managed.client === 'string' && engine.managed.client) return engine.managed.client;
|
|
465
|
+
if (engine && typeof engine.id === 'string' && engine.id) return engine.id.split('.')[0];
|
|
466
|
+
return '';
|
|
467
|
+
}
|
|
468
|
+
// Ordine di SOLA LETTURA: nessun fleet.json viene riscritto. Rango del client
|
|
469
|
+
// secondo CLIENT_ORDER, a parita' di rango resta l'ordine di inserimento
|
|
470
|
+
// (Array#sort e' stabile da ES2019); i client fuori lista chiudono l'array.
|
|
471
|
+
function ordinaEngine(engines) {
|
|
472
|
+
if (!Array.isArray(engines)) return engines;
|
|
473
|
+
const rank = new Map(CLIENT_ORDER.map((client, index) => [client, index]));
|
|
474
|
+
return [...engines].sort((a, b) => {
|
|
475
|
+
const ra = rank.get(engineClient(a));
|
|
476
|
+
const rb = rank.get(engineClient(b));
|
|
477
|
+
return (ra === undefined ? CLIENT_ORDER.length : ra) - (rb === undefined ? CLIENT_ORDER.length : rb);
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
function isStandardClient(client) {
|
|
481
|
+
return typeof client === 'string' && CLIENT_ORDER.includes(client);
|
|
482
|
+
}
|
|
450
483
|
const PROVIDER_ID_RE = /^[a-z][a-z0-9_-]{0,31}$/;
|
|
451
484
|
|
|
452
485
|
function validBaseUrl(value) {
|
|
@@ -588,6 +621,23 @@ const CATALOG = Object.freeze([
|
|
|
588
621
|
{ id: 'pi.ollama', client: 'pi', provider: 'ollama', label: 'Ollama local', auth: 'none', protocol: 'openai-completions', piProvider: 'ollama', requiresModel: true, core: true, piExtension: { baseUrl: 'http://127.0.0.1:11434/v1', apiKey: 'ollama' } },
|
|
589
622
|
{ id: 'pi.custom', client: 'pi', provider: 'custom', label: 'Custom provider', auth: 'dynamic', protocol: 'openai-responses', protocols: ['openai-responses', 'anthropic-messages', 'openai-completions', 'google-generative-ai'], custom: true, core: true },
|
|
590
623
|
|
|
624
|
+
// OpenCode (client nativo, 0.9.75): TUI headless-friendly con gateway proprio
|
|
625
|
+
// («provider» integrato `opencode`) e login locale (`opencode auth login`).
|
|
626
|
+
// MISURATO su opencode 1.18.33 (zero credenziali salvate):
|
|
627
|
+
// i modelli *-free del gateway rispondono ANONYMOUS, quindi la cella parte
|
|
628
|
+
// anche prima del login; il set completo arriva con l'account. Per questo
|
|
629
|
+
// auth e' 'login' (delegato, mai chiavi gestite da NexusCrew) e NON c'e'
|
|
630
|
+
// l'array `models`: l'elenco dei modelli del gateway ruota lato opencode e
|
|
631
|
+
// un elenco statico qui marcirebbe in silenzio; il modello resta libero
|
|
632
|
+
// (niente strictModels) e la forma e' `<model>` (prefisso gateway aggiunto
|
|
633
|
+
// dal launcher) oppure `provider/model` esplicito per chi sa cosa chiede.
|
|
634
|
+
// I provider A CHIAVE del catalogo (zai, ollama-cloud, openrouter...) NON
|
|
635
|
+
// entrano in questo client oggi: opencode.jsonc accetta `apiKey` SOLO come
|
|
636
|
+
// stringa letterale (il riferimento {env:...} e' rifiutato, misurato), e
|
|
637
|
+
// scrivere una chiave su disco viola la regola del repo (mai segreti nei
|
|
638
|
+
// file generati). Residuo dichiarato nel referto del lotto.
|
|
639
|
+
{ id: 'opencode.native', client: 'opencode', provider: 'native', label: 'OpenCode account (gateway + local login)', auth: 'login', protocol: 'opencode_native', core: true, notice: 'opencode-native' },
|
|
640
|
+
|
|
591
641
|
// Agy: client gestito primario (Linux/macOS non-Termux). Auth delegata al
|
|
592
642
|
// login locale del client (nessuno store letto/copiato). Nessun remote-control
|
|
593
643
|
// NexusCrew; permission default 'standard' (unsafe aggiunge --dangerously-skip-
|
|
@@ -744,10 +794,13 @@ function normalizeManagedSpec(value, { extraModels = null, engineId = null, onIs
|
|
|
744
794
|
function defaultDefinitions() {
|
|
745
795
|
return {
|
|
746
796
|
schemaVersion: 1,
|
|
747
|
-
|
|
797
|
+
// Il seed segue CLIENT_ORDER: una flotta nuova nasce gia' nell'ordine che
|
|
798
|
+
// il payload mostra (ordinaEngine), senza dover contare sull'ordine con
|
|
799
|
+
// cui le voci default sono scritte nel CATALOG.
|
|
800
|
+
engines: ordinaEngine(CATALOG.filter((p) => p.default).map((p) => ({
|
|
748
801
|
id: p.id, label: CLIENT_LABELS[p.client], rc: !!p.rc,
|
|
749
802
|
managed: { client: p.client, provider: p.provider, model: p.model || '', permissionPolicy: p.client === 'claude' ? 'unsafe' : 'standard' },
|
|
750
|
-
})),
|
|
803
|
+
}))),
|
|
751
804
|
cells: [],
|
|
752
805
|
};
|
|
753
806
|
}
|
|
@@ -2422,6 +2475,9 @@ function resolveManagedEngine(engine, cell, cfg = {}) {
|
|
|
2422
2475
|
// autonomous, nessuna domanda) NON e' mappato: il contratto NexusCrew
|
|
2423
2476
|
// distingue solo standard/unsafe e il default resta interattivo.
|
|
2424
2477
|
if (spec.client === 'kimi') args.push('--yolo');
|
|
2478
|
+
// OpenCode: unsafe -> --auto (flag reale di `opencode --help` 1.18.33:
|
|
2479
|
+
// «auto-approve permissions that are not explicitly denied (dangerous!)»).
|
|
2480
|
+
if (spec.client === 'opencode') args.push('--auto');
|
|
2425
2481
|
}
|
|
2426
2482
|
let shellOneShot = false;
|
|
2427
2483
|
if (spec.client === 'shell') {
|
|
@@ -2772,6 +2828,26 @@ function resolveManagedEngine(engine, cell, cfg = {}) {
|
|
|
2772
2828
|
// dal push generico qui sotto come ultimo argomento. Senza prompt parte il
|
|
2773
2829
|
// TUI interattivo nella cwd della cella.
|
|
2774
2830
|
if (model) args.push('--model', model);
|
|
2831
|
+
} else if (spec.client === 'opencode') {
|
|
2832
|
+
// OpenCode TUI (0.9.75, misurato su 1.18.33): auth delegata al login/locale
|
|
2833
|
+
// del client (gateway `opencode`, free tier anche senza credenziali) —
|
|
2834
|
+
// niente env provider, niente credenziali su argv, NE' config scritta da
|
|
2835
|
+
// NexusCrew: MCP e provider custom restano nell'opencode.jsonc dell'utente
|
|
2836
|
+
// (o nel file per-cella che l'operatore passa con OPENCODE_CONFIG, che
|
|
2837
|
+
// qui non si compone). `--standalone` NON esiste in 1.18.33: ogni
|
|
2838
|
+
// invocazione gira col proprio server e muore con essa (misurato con ps
|
|
2839
|
+
// dopo la chiusura), quindi il TUI parte nudo — quel flag sarebbe servito
|
|
2840
|
+
// per un server di background condiviso per utente che questa versione non
|
|
2841
|
+
// avvia. Il modello viaggia come `-m provider/model`: un id senza `/`
|
|
2842
|
+
// e' del gateway integrato (`opencode/<model>`), uno gia' prefissato
|
|
2843
|
+
// passa tale e quale (l'operatore che ha config sue sa cosa chiede).
|
|
2844
|
+
// Il prompt di cella va su `--prompt` (precompila la casella di input,
|
|
2845
|
+
// pattern di agy --prompt-interactive): il posizionale e' il PATH di
|
|
2846
|
+
// progetto, mai testo. Niente send-keys: il classifier di readiness
|
|
2847
|
+
// (prompt-delivery) copre kimi/claude/vl e un marker nuovo va misurato
|
|
2848
|
+
// sul TUI vivo prima di essere fidato — residuo dichiarato.
|
|
2849
|
+
if (model) args.push('-m', model.includes('/') ? model : `opencode/${model}`);
|
|
2850
|
+
if (cell?.prompt) args.push('--prompt');
|
|
2775
2851
|
} else if (spec.client === 'vl') {
|
|
2776
2852
|
// VL/Vivling runtime TUI: auth is handled by the runtime in every variant
|
|
2777
2853
|
// (config.toml / VL_API_KEY by name via envPassthrough), never
|
|
@@ -3014,7 +3090,7 @@ module.exports = {
|
|
|
3014
3090
|
ALIBABA_CODEX_MODELS, ALIBABA_TOKEN_PLAN_CONTEXT, ALIBABA_PI_MODELS,
|
|
3015
3091
|
OPENCODE_GO_MESSAGES_MODELS, OPENCODE_GO_RESPONSES_MODELS, OPENCODE_GO_CHAT_MODELS, OPENCODE_GO_LIMITS,
|
|
3016
3092
|
OPENCODE_GO_ANTHROPIC_ROOT, OPENCODE_GO_API_BASE,
|
|
3017
|
-
CLIENT_LABELS, normalizeManagedSpec, profileFor,
|
|
3093
|
+
CLIENT_LABELS, CLIENT_ORDER, engineClient, ordinaEngine, isStandardClient, normalizeManagedSpec, profileFor,
|
|
3018
3094
|
defaultDefinitions, defaultShellEngine, defaultAgyEngine, defaultKimiEngine, defaultGrokEngine, defaultVlEngine, describeManaged, describeCatalogCredential, discoverOllamaModels, resolveManagedEngine, needsExplicitNode,
|
|
3019
3095
|
discoverPiModels, EXTERNAL_DISCOVERY_TIMEOUT_MS, parseEnvFile, parseProviderShellFile, findBinary, publicCatalog, writePiProviderExtension, customCatalogFor,
|
|
3020
3096
|
codexSurfaceNames, writeCellCodexProfile,
|
package/package.json
CHANGED