@mmmbuto/nexuscrew 0.9.16 → 0.9.18
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 +157 -3
- package/docs/ALIBABA_TOKEN_PLAN.md +70 -0
- package/docs/AUDIO_SHARE.md +119 -0
- package/docs/CELL_PANEL.md +156 -0
- package/docs/CONFIGURATION.md +122 -0
- package/docs/FLEET.md +222 -0
- package/docs/INSTALLATION.md +128 -0
- package/docs/LIVE_PROMPT.md +110 -0
- package/docs/MCP.md +151 -0
- package/docs/NODES.md +214 -0
- package/docs/NOTIFICATIONS.md +101 -0
- package/docs/OPERATIONS.md +114 -0
- package/docs/README.md +55 -0
- package/docs/SECURITY.md +185 -0
- package/docs/VL_MICRO_NODES.md +107 -0
- package/docs/img/fleet-deck-desktop.png +0 -0
- package/docs/img/session-mobile.png +0 -0
- package/docs/live-prompt-templates/LIVE_PROMPT.en.md +67 -0
- package/docs/live-prompt-templates/LIVE_PROMPT.es.md +70 -0
- package/docs/live-prompt-templates/LIVE_PROMPT.it.md +67 -0
- package/frontend/dist/assets/{index-CwsGpQwK.js → index-DmpQoCsc.js} +1 -1
- package/frontend/dist/index.html +1 -1
- package/frontend/dist/version.json +1 -1
- package/lib/auth/middleware.js +1 -1
- package/lib/cells/scope-guard.js +2 -2
- package/lib/cli/commands.js +7 -7
- package/lib/cli/fleet-service.js +2 -2
- package/lib/cli/pidfile.js +15 -15
- package/lib/cli/service.js +2 -2
- package/lib/cli/stable-alias.js +1 -1
- package/lib/files/routes.js +2 -2
- package/lib/fleet/builtin.js +6 -6
- package/lib/fleet/catalogs/zai-responses.json +82 -0
- package/lib/fleet/cell-exec.js +4 -4
- package/lib/fleet/cell-lease-server.js +17 -17
- package/lib/fleet/definitions.js +28 -17
- package/lib/fleet/launch-broker.js +3 -3
- package/lib/fleet/lease-client.js +1 -1
- package/lib/fleet/managed.js +86 -26
- package/lib/fleet/prompt-delivery.js +1 -1
- package/lib/fleet/runtime.js +3 -3
- package/lib/live-host/bridge.js +3 -3
- package/lib/live-host/routes.js +1 -1
- package/lib/mcp/server.js +7 -8
- package/lib/mcp/tools.js +1 -1
- package/lib/nodes/commands.js +2 -2
- package/lib/nodes/health.js +1 -1
- package/lib/nodes/identity.js +10 -13
- package/lib/nodes/store.js +2 -2
- package/lib/nodes/tunnel-supervisor.js +7 -7
- package/lib/nodes/tunnel.js +10 -11
- package/lib/notify/asks.js +4 -4
- package/lib/notify/persist.js +1 -1
- package/lib/notify/push.js +4 -4
- package/lib/notify/routes.js +12 -12
- package/lib/proxy/node-proxy.js +4 -4
- package/lib/proxy/panel-proxy.js +2 -2
- package/lib/pty/attach.js +1 -1
- package/lib/server.js +3 -3
- package/lib/settings/pairing-coordinator.js +1 -1
- package/lib/settings/routes.js +4 -4
- package/lib/tmux/lifecycle.js +4 -4
- package/lib/tmux/preview.js +1 -1
- package/lib/update/runner.js +9 -9
- package/lib/vl-nodes/broker.js +1 -1
- package/lib/ws/bridge.js +1 -1
- package/package.json +20 -2
- package/frontend/index.html +0 -20
package/lib/cli/commands.js
CHANGED
|
@@ -560,7 +560,7 @@ async function probeNexusCrew(port, token, opts = {}) {
|
|
|
560
560
|
// dice che il servizio E' IN PIEDI e che il token locale non vale — e chi deve
|
|
561
561
|
// spiegare un riavvio ha bisogno di distinguerle, altrimenti dichiara fallito
|
|
562
562
|
// un riavvio riuscito e manda a cercare un processo morto che e' vivo.
|
|
563
|
-
// Rilievo
|
|
563
|
+
// Rilievo della revisione: il caso token-assente era gia' coperto, questo no.
|
|
564
564
|
async function probeNexusCrewStatus(port, token, opts = {}) {
|
|
565
565
|
const fetchImpl = opts.fetchImpl || globalThis.fetch;
|
|
566
566
|
if (typeof fetchImpl !== 'function') return null;
|
|
@@ -932,7 +932,7 @@ function autoUpdateCommand(args, opts = {}) {
|
|
|
932
932
|
// stesso file: tmp nella stessa directory, 0600, rename. Un `writeFileSync`
|
|
933
933
|
// diretto puo' lasciare un config.json troncato se il processo muore a
|
|
934
934
|
// meta' — e un config.json illeggibile e' un nodo che non riparte, cioe'
|
|
935
|
-
// proprio il guasto che questo comando serve a evitare. Rilievo
|
|
935
|
+
// proprio il guasto che questo comando serve a evitare. Rilievo della revisione.
|
|
936
936
|
try {
|
|
937
937
|
if (fs.lstatSync(configPath).isSymbolicLink()) {
|
|
938
938
|
log('autoupdate: config.json e\' un symlink, non lo scrivo');
|
|
@@ -975,7 +975,7 @@ function autoUpdateCommand(args, opts = {}) {
|
|
|
975
975
|
|
|
976
976
|
// update: npm i -g @latest + sanazione definizioni di boot + restart se attivo.
|
|
977
977
|
// Fallimento npm -> messaggio chiaro, code 1.
|
|
978
|
-
//
|
|
978
|
+
// rimedio da revisione, difetto 1: la via manuale attraversa la STESSA
|
|
979
979
|
// sanazione del runner (healBootDefinitions: rigenera le definizioni sul
|
|
980
980
|
// node vivo e APPLICA l'attivazione differita). Prima update() reinstallava
|
|
981
981
|
// e riavviava soltanto: chi aggiornava a mano con un node appena cambiato
|
|
@@ -1177,7 +1177,7 @@ async function dispatchNodes(rest, flags, opts = {}) {
|
|
|
1177
1177
|
if (!cells.length) { log('nodes cells: elenco vuoto — usa `none` se intendi nessuna cella'); return { code: 1 }; }
|
|
1178
1178
|
return { code: nodesCmds.nodesEdit({ ...opts, log, ref, patch: { cellVisibility: 'selected', cells } }).code };
|
|
1179
1179
|
}
|
|
1180
|
-
// Accesso ai pannelli (
|
|
1180
|
+
// Accesso ai pannelli (da revisione): dietro un pannello c'e' un browser con sessioni
|
|
1181
1181
|
// gia' autenticate, e quell'accesso non si revoca cambiando una chiave. Il
|
|
1182
1182
|
// resto del modello tratta un peer pairato come l'operatore stesso; qui no:
|
|
1183
1183
|
// default negato, si concede un nodo per volta.
|
|
@@ -1407,7 +1407,7 @@ function dispatch(argv, opts = {}) {
|
|
|
1407
1407
|
// SENZA TOKEN NON SI PUO' DIRE NIENTE SULLA SALUTE, e soprattutto non si
|
|
1408
1408
|
// deve dire qualcosa di sbagliato: la sonda fallirebbe per autenticazione e
|
|
1409
1409
|
// il messaggio incolperebbe la porta o il processo, mandando a cercare dove
|
|
1410
|
-
// il problema non e'. Rilievo
|
|
1410
|
+
// il problema non e'. Rilievo della revisione. Il riavvio e' comunque partito,
|
|
1411
1411
|
// quindi non e' un fallimento: e' una verifica che non si e' potuta fare, e
|
|
1412
1412
|
// si dice cosi'.
|
|
1413
1413
|
if (!token) {
|
|
@@ -1460,8 +1460,8 @@ function dispatch(argv, opts = {}) {
|
|
|
1460
1460
|
// processi sulla stessa porta, e il nostro sopravvivrebbe allo stop del
|
|
1461
1461
|
// servizio. Li' si riferisce e basta.
|
|
1462
1462
|
//
|
|
1463
|
-
//
|
|
1464
|
-
//
|
|
1463
|
+
// Invariante: il ramo managed non chiama `startPortable`; un fallback
|
|
1464
|
+
// locale creerebbe un secondo processo fuori dal controllo del gestore.
|
|
1465
1465
|
if (result.runtimeOwner === 'managed') {
|
|
1466
1466
|
log(`restart: il servizio NON risponde su 127.0.0.1:${port} dopo il riavvio.`);
|
|
1467
1467
|
log(' Il runtime e\' gestito dal servizio di sistema: non avvio un processo accanto.');
|
package/lib/cli/fleet-service.js
CHANGED
|
@@ -238,7 +238,7 @@ function fleetInstallCommands(platform, target, ctx) {
|
|
|
238
238
|
// Install no-symlink + atomic rename (come service.js). execImpl iniettabile per test;
|
|
239
239
|
// default execFileSync (argv diretto, MAI shell string). Su activation fallita il file
|
|
240
240
|
// e' PRESERVATO e le failure raccolte per diagnosi (M1: non si ingoia, non si rollback).
|
|
241
|
-
//
|
|
241
|
+
// da revisione: `activate: false` (speculare a installService) scrive la definizione SENZA
|
|
242
242
|
// toccare il service manager — per chi rigenera a meta' aggiornamento: il companion
|
|
243
243
|
// e' RunAtLoad/oneshot, un bootstrap adesso eseguirebbe le celle boot:true mentre
|
|
244
244
|
// il servizio principale sta per essere riavviato. I comandi saltati sono dichiarati
|
|
@@ -313,7 +313,7 @@ function installFleetService(platform, content, ctx, {
|
|
|
313
313
|
|
|
314
314
|
// Gemello di isOurService (service.js): stessa ragione, stessa disciplina —
|
|
315
315
|
// l'ancora vive accanto al template della companion.
|
|
316
|
-
// Stessa disciplina di service.js (
|
|
316
|
+
// Stessa disciplina di service.js (da revisione): la riga identificativa e'
|
|
317
317
|
// definita una volta e usata SIA dal template SIA dall'ancora, e l'ancora e' la
|
|
318
318
|
// riga ESATTA — un prefisso lascia passare il servizio di terzi che comincia la
|
|
319
319
|
// propria Description col nostro nome.
|
package/lib/cli/pidfile.js
CHANGED
|
@@ -72,7 +72,7 @@ function readProcessStart(pid) {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
// Campi che SOLO writePidfile puo' scrivere: mai dal chiamante via `extra`.
|
|
75
|
-
// Misurato
|
|
75
|
+
// Misurato in revisione: con lo spread di extra per ultimo,
|
|
76
76
|
// writePidfile(f, pid, cmd, {processStart:'FINTO', attestation:'unsupported'})
|
|
77
77
|
// vinceva sul valore vero calcolato da probeProcessStart — un chiamante
|
|
78
78
|
// poteva scrivere un'attestazione INVENTATA. safeExtra la filtra qui, e i
|
|
@@ -105,7 +105,7 @@ const RESERVED_META_FIELDS = new Set(['pid', 'cmd', 'startTs', 'uid', 'processSt
|
|
|
105
105
|
// controlla quella variabile d'ambiente controlla anche la finestra — non
|
|
106
106
|
// un confine di sicurezza reale (chi setta l'ambiente del processo ha gia'
|
|
107
107
|
// vinto), ma va detto: il marker non e' una garanzia piu' forte di questa.
|
|
108
|
-
// 3.
|
|
108
|
+
// 3. da revisione: claimSchemaMarker (sotto) crea il marker al KILL, non solo
|
|
109
109
|
// al write — quindi un pidfile che sta per essere RIMOSSO puo' lasciare
|
|
110
110
|
// dietro un marker in una directory che magari non ospitera' mai piu' un
|
|
111
111
|
// runtime. E' INNOCUO: il marker non ha altro effetto che rendere PIU'
|
|
@@ -119,7 +119,7 @@ function schemaMarkerPath(pidfilePath) {
|
|
|
119
119
|
return path.join(path.dirname(pidfilePath), '.pidfile-schema-v2');
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
//
|
|
122
|
+
// da revisione interna: misurato in revisione
|
|
123
123
|
// sul frozen — se .pidfile-schema-v2 esiste come DIRECTORY (o symlink, o
|
|
124
124
|
// qualunque cosa non sia un file regolare), il vecchio hasSchemaMarker
|
|
125
125
|
// tornava `false` per costruzione (isFile() falso), e ensureSchemaMarker
|
|
@@ -192,7 +192,7 @@ function hasSchemaMarker(pidfilePath) {
|
|
|
192
192
|
// pidfile e' gia' stato scritto quando arriviamo qui, ed e' lui
|
|
193
193
|
// l'informazione che conta.
|
|
194
194
|
//
|
|
195
|
-
//
|
|
195
|
+
// da revisione interna: questa funzione NON
|
|
196
196
|
// garantisce piu' nulla sul futuro — prima il commento qui diceva "il
|
|
197
197
|
// prossimo killPidfile lo scoprira' da solo", ed era FALSO: se questa
|
|
198
198
|
// scrittura fallisce con ENOSPC/EROFS/quota/permesso transitorio, non resta
|
|
@@ -215,7 +215,7 @@ function ensureSchemaMarker(pidfilePath) {
|
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
217
|
|
|
218
|
-
//
|
|
218
|
+
// da revisione: LA decisione di sicurezza. checkSchemaMarker (sopra) guarda
|
|
219
219
|
// un EVENTO PASSATO — "il marker e' stato scritto" — che puo' fallire in
|
|
220
220
|
// silenzio senza lasciare traccia (vedi ensureSchemaMarker): il percorso
|
|
221
221
|
// resta ENOENT, indistinguibile da "mai tentato". claimSchemaMarker sposta
|
|
@@ -444,14 +444,14 @@ function killPidfile(p, signal = 'SIGTERM', impl = {}) {
|
|
|
444
444
|
// Prima di questo controllo (nascita, aggiunta dopo cmd) il segnale
|
|
445
445
|
// partiva sul solo cmd, senza verifica di identità, per QUALUNQUE pidfile
|
|
446
446
|
// senza processStart — vecchio o nuovo, piattaforma capace o no: era
|
|
447
|
-
// esattamente il buco piu' largo trovato
|
|
447
|
+
// esattamente il buco piu' largo trovato in revisione, perche'
|
|
448
448
|
// "nessuna nascita nel meta" copriva sia "questa macchina non sa attestare"
|
|
449
449
|
// sia "l'attestazione e' fallita alla creazione per un motivo che non
|
|
450
450
|
// conosciamo" con lo STESSO comportamento permissivo. Il ramo sotto separa
|
|
451
451
|
// i due casi guardando cosa writePidfile ha DICHIARATO al momento in cui
|
|
452
452
|
// contava (probeProcessStart, sopra), non ricalcolando una sonda adesso —
|
|
453
453
|
// rifarla qui confonderebbe di nuovo "non so attestare" con "non ci sono
|
|
454
|
-
// riuscito in QUESTO istante", l'errore di categoria che
|
|
454
|
+
// riuscito in QUESTO istante", l'errore di categoria che la revisione ha
|
|
455
455
|
// contestato nel primo disegno di questo fix.
|
|
456
456
|
let unverifiedBirth = false;
|
|
457
457
|
let schemaMarker = null; // calcolato pigro: solo se serve davvero (vedi sotto)
|
|
@@ -489,7 +489,7 @@ function killPidfile(p, signal = 'SIGTERM', impl = {}) {
|
|
|
489
489
|
// altro probe su PID/cmd (la stessa prova debole con un altro nome): serve
|
|
490
490
|
// una prova ALTERNATIVA del proprietario — per esempio un runId verificato
|
|
491
491
|
// contro un endpoint locale autenticato. Non e' implementata: e' un lavoro
|
|
492
|
-
// a se', con un suo giro di
|
|
492
|
+
// a se', con un suo giro di revisione dedicato (non eseguito).
|
|
493
493
|
unverifiedBirth = 'unsupported';
|
|
494
494
|
} else if (meta.attestation === 'indeterminate') {
|
|
495
495
|
// Il codice NUOVO (questo, o una versione successiva a questo fix) ha
|
|
@@ -497,7 +497,7 @@ function killPidfile(p, signal = 'SIGTERM', impl = {}) {
|
|
|
497
497
|
// che non sa classificare come proprieta' della piattaforma (vedi
|
|
498
498
|
// probeProcessStart sopra) — sa che avrebbe potuto, su questo pid,
|
|
499
499
|
// adesso, e non ce l'ha fatta. Trattarlo come "la piattaforma non sa
|
|
500
|
-
// attestare" sarebbe l'errore di categoria contestato
|
|
500
|
+
// attestare" sarebbe l'errore di categoria contestato in revisione:
|
|
501
501
|
// un'assenza non e' una proprieta' stabile solo perche' e' comoda da
|
|
502
502
|
// leggere cosi'. Si rifiuta: il cmd da solo matcha per inclusioni, e non
|
|
503
503
|
// basta a chi non sa nemmeno perche' la nascita manca QUI. Il pidfile
|
|
@@ -512,7 +512,7 @@ function killPidfile(p, signal = 'SIGTERM', impl = {}) {
|
|
|
512
512
|
// forma di meta la puo' produrre anche un chiamante non nostro): "nessun
|
|
513
513
|
// campo" da solo non prova la provenienza.
|
|
514
514
|
//
|
|
515
|
-
//
|
|
515
|
+
// da revisione: la domanda che decide non e' piu' "il marker e' gia'
|
|
516
516
|
// stato scritto?" (un evento passato che puo' fallire in silenzio —
|
|
517
517
|
// vedi ensureSchemaMarker/claimSchemaMarker sopra) ma "posso scriverlo
|
|
518
518
|
// ADESSO?" — claimSchemaMarker prova a crearlo in questo esatto istante:
|
|
@@ -534,9 +534,9 @@ function killPidfile(p, signal = 'SIGTERM', impl = {}) {
|
|
|
534
534
|
// scrivere qui, verificato nell'istante in cui
|
|
535
535
|
// conta, non dedotto da uno passato. Si rifiuta.
|
|
536
536
|
//
|
|
537
|
-
// MISURATO
|
|
537
|
+
// MISURATO IN REVISIONE: trattare OGNI meta senza attestazione come
|
|
538
538
|
// indeterminate rompe l'aggiornamento automatico per ogni nodo il cui
|
|
539
|
-
// runtime e' ancora precedente a quando processStart e' nato (
|
|
539
|
+
// runtime e' ancora precedente a quando processStart e' nato (introdotto in
|
|
540
540
|
// v0.9.0) — npm install sovrascrive pidfile.js PRIMA che il runner lo
|
|
541
541
|
// richieda, il runner legge il pidfile VECCHIO del runtime in esecuzione,
|
|
542
542
|
// killPidfile rifiuta, stopPortableRuntime torna killed:false,
|
|
@@ -555,9 +555,9 @@ function killPidfile(p, signal = 'SIGTERM', impl = {}) {
|
|
|
555
555
|
// morire (il difetto gia' visto sul campo che l'ingresso unico di
|
|
556
556
|
// riavvio doveva chiudere); un ramo "legacy" permanente basato sulla sola
|
|
557
557
|
// FORMA del meta (lasciava aperti per sempre restore da backup,
|
|
558
|
-
// corruzione e downgrade —
|
|
559
|
-
// controllato ma mai riprovato al kill (
|
|
560
|
-
//
|
|
558
|
+
// corruzione e downgrade — la revisione l'ha contestata); un marker
|
|
559
|
+
// controllato ma mai riprovato al kill (da revisione:
|
|
560
|
+
// una scrittura fallita in silenzio riapre
|
|
561
561
|
// lo stesso buco senza lasciare traccia).
|
|
562
562
|
schemaMarker = (impl.claimSchemaMarkerImpl || claimSchemaMarker)(p);
|
|
563
563
|
if (schemaMarker.state === 'created') {
|
package/lib/cli/service.js
CHANGED
|
@@ -407,7 +407,7 @@ function installCommands(platform, target, ctx) {
|
|
|
407
407
|
}
|
|
408
408
|
|
|
409
409
|
|
|
410
|
-
// --- Riconoscimento di PROPRIETA' (
|
|
410
|
+
// --- Riconoscimento di PROPRIETA' (da revisione) -------------------------
|
|
411
411
|
// La sanazione automatica riscrive una definizione solo se e' NOSTRA. La prima
|
|
412
412
|
// versione cercava /nexuscrew/i nel contenuto: un unit di terzi che ci nomina
|
|
413
413
|
// in un commento («Avviato dopo NexusCrew») veniva sovrascritto. La parola non
|
|
@@ -426,7 +426,7 @@ function installCommands(platform, target, ctx) {
|
|
|
426
426
|
// dall'ancora: cosi' non possono divergere per costruzione, e il test guardia
|
|
427
427
|
// diventa una seconda difesa invece dell'unica.
|
|
428
428
|
//
|
|
429
|
-
//
|
|
429
|
+
// da revisione: l'ancora linux era un PREFISSO
|
|
430
430
|
// (`/^Description=NexusCrew\b/`) e un servizio di terzi che comincia la sua
|
|
431
431
|
// Description con il nostro nome — «NexusCrew-compatible proxy», «NexusCrew
|
|
432
432
|
// fork by tizio» — veniva riconosciuto come nostro e sovrascritto. Il mac era
|
package/lib/cli/stable-alias.js
CHANGED
|
@@ -109,7 +109,7 @@ function resolveBootPaths({ nodeBin, entryPath, env = process.env, realpath } =
|
|
|
109
109
|
};
|
|
110
110
|
}
|
|
111
111
|
|
|
112
|
-
//
|
|
112
|
+
// da revisione: il runner dell'aggiornamento puo' girare con un process.execPath gia'
|
|
113
113
|
// MORTO su disco — l'upgrade di node (Homebrew) unlinka il Cellar vecchio
|
|
114
114
|
// mentre il processo resta in memoria, e npm reinstalla il pacchetto nel
|
|
115
115
|
// prefix del node NUOVO. In quel caso resolveBootPaths scriverebbe il path
|
package/lib/files/routes.js
CHANGED
|
@@ -47,7 +47,7 @@ function filesRoutes({ cfg, sessionExists, paste, notifier, readonly = () => fal
|
|
|
47
47
|
// pasteToSession). Il nome upload e' sanitizzato: se scatta, la causa e'
|
|
48
48
|
// il filesRoot di configurazione (control char) o un path oltre i 4096
|
|
49
49
|
// char. Rifiuto noto PRIMA di tmux: la PTY non va neanche tentata.
|
|
50
|
-
// 500, non 502 (rilievo
|
|
50
|
+
// 500, non 502 (rilievo da revisione interna): qui il valle NON e' stato
|
|
51
51
|
// contattato — a rifiutare siamo noi, per configurazione nostra. 502
|
|
52
52
|
// affermerebbe «il servizio a monte non risponde» e sveglierebbe un
|
|
53
53
|
// allarme su un tmux che sta benissimo. La coppia di codici e'
|
|
@@ -75,7 +75,7 @@ function filesRoutes({ cfg, sessionExists, paste, notifier, readonly = () => fal
|
|
|
75
75
|
// nell'outbox della sessione mittente. Path sorgente fail-closed: stringa
|
|
76
76
|
// assoluta, realpath ESISTENTE sotto HOME (i symlink si risolvono PRIMA del
|
|
77
77
|
// check: un link che esce da HOME viene rifiutato), file regolare.
|
|
78
|
-
//
|
|
78
|
+
// da revisione: gated READONLY — la copia e' una scrittura su disco; il gate
|
|
79
79
|
// sta PRIMA di ogni altro check (nessun probe di sessione/path in READONLY).
|
|
80
80
|
const bridgeReadonly = () => (cfg.readonlyDefault === true || process.env.NEXUSCREW_READONLY === '1');
|
|
81
81
|
router.post('/outbox', express.json({ limit: '8kb' }), (req, res) => {
|
package/lib/fleet/builtin.js
CHANGED
|
@@ -188,7 +188,7 @@ function risolviEseguibile(nome) {
|
|
|
188
188
|
function dirScrivibileDaTutti(file) {
|
|
189
189
|
// Tutta la CATENA, non il solo genitore: con `/a` scrivibile da chiunque,
|
|
190
190
|
// `/a/b/docker` resta sostituibile rinominando `b` — il binario e la sua
|
|
191
|
-
// directory immediata possono essere ineccepibili e il percorso no.
|
|
191
|
+
// directory immediata possono essere ineccepibili e il percorso no. Una revisione
|
|
192
192
|
// ha riprodotto esattamente questo caso su un controllo fermo al genitore.
|
|
193
193
|
//
|
|
194
194
|
// Lo sticky bit interrompe la risalita per quel livello: li' il kernel
|
|
@@ -586,11 +586,11 @@ async function createBuiltinFleet(cfg = {}) {
|
|
|
586
586
|
// passano il flag, quindi la suite esistente non vede endpoint lease aperti da up().
|
|
587
587
|
const cellLeaseEnabled = cfg.cellLeaseEnabled === true;
|
|
588
588
|
|
|
589
|
-
// ---
|
|
589
|
+
// --- da revisione interna: TUTTI i gate availability AVVENGONO PRIMA di creare
|
|
590
590
|
// leaseManager/launchBroker e di leaseManager.boot(). leaseManager non esiste finche'
|
|
591
591
|
// la Fleet non e' confermata disponibile, quindi NESSUN endpoint lease resta vivo su
|
|
592
592
|
// un early return (off/blocked) finche' ogni gate resta sopra questa boundary.
|
|
593
|
-
//
|
|
593
|
+
// da revisione interna: questo e' un fatto contingente del ref corrente, NON una
|
|
594
594
|
// garanzia anti-refactor. Un gate aggiunto DOPO la creazione del manager passerebbe
|
|
595
595
|
// inosservato senza un controllo statico/mutation-sensitive: il commento non impedisce
|
|
596
596
|
// un `return off` futuro oltre la boundary. La promessa vera e' limitata: oggi nessun
|
|
@@ -617,7 +617,7 @@ async function createBuiltinFleet(cfg = {}) {
|
|
|
617
617
|
}
|
|
618
618
|
|
|
619
619
|
if (!readonly()) {
|
|
620
|
-
//
|
|
620
|
+
// da revisione (rilievo 3): lo snapshot della BASE si prende QUI, alla lettura
|
|
621
621
|
// che ha prodotto `boot` — non con una rilettura dopo la migrazione. Il
|
|
622
622
|
// vecchio `primaDelloScrivere` era una rilettura: una scrittura arrivata
|
|
623
623
|
// DURANTE migrateLegacyTmuxSessions (una catena di chiamate tmux: finestra
|
|
@@ -694,8 +694,8 @@ async function createBuiltinFleet(cfg = {}) {
|
|
|
694
694
|
// il canale lease del supervisore.
|
|
695
695
|
...(leaseManager ? {
|
|
696
696
|
onLease: (socket, lease) => {
|
|
697
|
-
//
|
|
698
|
-
// persistenza del bind fallisce.
|
|
697
|
+
// da revisione interna: attachInitial restituisce false se la
|
|
698
|
+
// persistenza del bind fallisce. da revisione interna: ora il
|
|
699
699
|
// broker chiama onLease PRIMA di scrivere il payload. Il ritorno
|
|
700
700
|
// ESPLICITO false (o la socket distrutta qui) comunica al broker di NON
|
|
701
701
|
// consegnare il payload: il supervisore riceve la chiusura prima del
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
{
|
|
2
|
+
"models": [
|
|
3
|
+
{
|
|
4
|
+
"slug": "glm-5.3",
|
|
5
|
+
"display_name": "glm-5.3",
|
|
6
|
+
"description": "Z.ai's latest flagship model",
|
|
7
|
+
"default_reasoning_level": "max",
|
|
8
|
+
"supported_reasoning_levels": [
|
|
9
|
+
{
|
|
10
|
+
"description": "Light reasoning",
|
|
11
|
+
"effort": "low"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"description": "Enhanced reasoning",
|
|
15
|
+
"effort": "high"
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
"description": "Deep reasoning",
|
|
19
|
+
"effort": "max"
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"shell_type": "shell_command",
|
|
23
|
+
"visibility": "list",
|
|
24
|
+
"supported_in_api": true,
|
|
25
|
+
"priority": 0,
|
|
26
|
+
"base_instructions": "",
|
|
27
|
+
"supports_reasoning_summaries": true,
|
|
28
|
+
"default_reasoning_summary": "none",
|
|
29
|
+
"support_verbosity": false,
|
|
30
|
+
"apply_patch_tool_type": "freeform",
|
|
31
|
+
"truncation_policy": {
|
|
32
|
+
"limit": 10000,
|
|
33
|
+
"mode": "bytes"
|
|
34
|
+
},
|
|
35
|
+
"supports_parallel_tool_calls": true,
|
|
36
|
+
"availability_nux": null,
|
|
37
|
+
"upgrade": null,
|
|
38
|
+
"default_verbosity": null,
|
|
39
|
+
"web_search_tool_type": "text",
|
|
40
|
+
"supports_image_detail_original": false
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"slug": "glm-5.3-flash",
|
|
44
|
+
"display_name": "glm-5.3-flash",
|
|
45
|
+
"description": "Fast multimodal coding model",
|
|
46
|
+
"default_reasoning_level": "max",
|
|
47
|
+
"supported_reasoning_levels": [
|
|
48
|
+
{
|
|
49
|
+
"description": "Light reasoning",
|
|
50
|
+
"effort": "low"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"description": "Enhanced reasoning",
|
|
54
|
+
"effort": "high"
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"description": "Deep reasoning",
|
|
58
|
+
"effort": "max"
|
|
59
|
+
}
|
|
60
|
+
],
|
|
61
|
+
"shell_type": "shell_command",
|
|
62
|
+
"visibility": "list",
|
|
63
|
+
"supported_in_api": true,
|
|
64
|
+
"priority": 1,
|
|
65
|
+
"base_instructions": "",
|
|
66
|
+
"supports_reasoning_summaries": true,
|
|
67
|
+
"default_reasoning_summary": "none",
|
|
68
|
+
"support_verbosity": false,
|
|
69
|
+
"apply_patch_tool_type": "freeform",
|
|
70
|
+
"truncation_policy": {
|
|
71
|
+
"limit": 10000,
|
|
72
|
+
"mode": "bytes"
|
|
73
|
+
},
|
|
74
|
+
"supports_parallel_tool_calls": true,
|
|
75
|
+
"availability_nux": null,
|
|
76
|
+
"upgrade": null,
|
|
77
|
+
"default_verbosity": null,
|
|
78
|
+
"web_search_tool_type": "text_and_image",
|
|
79
|
+
"supports_image_detail_original": true
|
|
80
|
+
}
|
|
81
|
+
]
|
|
82
|
+
}
|
package/lib/fleet/cell-exec.js
CHANGED
|
@@ -19,7 +19,7 @@ const DEFAULT_SUPERVISE = Object.freeze({
|
|
|
19
19
|
maxRapidRestarts: 8,
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
-
//
|
|
22
|
+
// da revisione (correzione escalation): limite ESPLICITO fra SIGTERM e
|
|
23
23
|
// SIGKILL quando la lease e' persa (onLost, main()). Un figlio che ignora
|
|
24
24
|
// SIGTERM non deve restare appeso a tempo indeterminato.
|
|
25
25
|
const LEASE_LOST_KILL_ESCALATION_MS = 5000;
|
|
@@ -303,7 +303,7 @@ async function main(argv = process.argv.slice(2), seams = {}) {
|
|
|
303
303
|
// inherit. Il proof (2b) vive nel lease-client, MAI nell'env del child.
|
|
304
304
|
let generation = 0;
|
|
305
305
|
let leaseCtl = null;
|
|
306
|
-
//
|
|
306
|
+
// da revisione interna: current/stopping vivono QUI (prima del lease-client) perche'
|
|
307
307
|
// la callback onLost qui sotto li riferisce dalla closure.
|
|
308
308
|
let current = null; let stopping = false;
|
|
309
309
|
if (payload.lease && leaseSocket && !leaseSocket.destroyed) {
|
|
@@ -315,7 +315,7 @@ async function main(argv = process.argv.slice(2), seams = {}) {
|
|
|
315
315
|
// generation += 1). Passiamo un getter cosicche' il reconnect presenti
|
|
316
316
|
// sempre la generation corrente, non 0 fisso (riconcilia :301 con :381).
|
|
317
317
|
generation: () => generation,
|
|
318
|
-
//
|
|
318
|
+
// da revisione interna: lease persa per tutta la grace senza un
|
|
319
319
|
// reconnect riuscito. Prima il lease-client desisteva in silenzio dopo
|
|
320
320
|
// 60s e il child restava un orfano senza lease per tutta la sua vita.
|
|
321
321
|
// Ora il supervisore viene avvisato: ferma il child (stopping + SIGTERM)
|
|
@@ -324,7 +324,7 @@ async function main(argv = process.argv.slice(2), seams = {}) {
|
|
|
324
324
|
// Correzione (seconda riconsegna): un SOLO SIGTERM senza escalation
|
|
325
325
|
// lascia appeso un child che lo ignora (misurato sul percorso reale:
|
|
326
326
|
// {spawned:1, kills:['SIGTERM'], state:'pending'}) — l'orfano non NASCE
|
|
327
|
-
// piu' (
|
|
327
|
+
// piu' (caso originale chiuso davvero), ma un figlio gia' vivo che non
|
|
328
328
|
// collabora non muore. LEASE_LOST_KILL_ESCALATION_MS e' il limite
|
|
329
329
|
// ESPLICITO, scritto qui: se il child non e' uscito entro questa
|
|
330
330
|
// finestra dal SIGTERM, si passa a SIGKILL. `target` fissa il processo
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// issuedAt+60s). Il proof supervisore arriva al detentore sul canale lease
|
|
10
10
|
// (all'attach e a ogni refresh) e non transita mai nel child (R3.1.2 invariato).
|
|
11
11
|
//
|
|
12
|
-
// Storage per-cella (
|
|
12
|
+
// Storage per-cella (da revisione): un file per cella in <run>/cell-leases/. Il
|
|
13
13
|
// refresh di una cella rilegge e riscrive SOLO il proprio file: scompare la
|
|
14
14
|
// read-modify-write dell'intero store condiviso, la race su di essa e
|
|
15
15
|
// l'amplificazione O(N^2) misurata in 2a (N letture + N scritture complete
|
|
@@ -51,7 +51,7 @@ function sanitizeCell(cellId) {
|
|
|
51
51
|
const CELL_ID_RE = /^[A-Za-z0-9._-]{1,64}$/;
|
|
52
52
|
function validCellId(cellId) { return typeof cellId === 'string' && CELL_ID_RE.test(cellId); }
|
|
53
53
|
|
|
54
|
-
//
|
|
54
|
+
// da revisione interna: il formato che il runtime produce per un'identity.
|
|
55
55
|
// Condiviso da loadPersisted (primo ingresso, al boot) e track (secondo
|
|
56
56
|
// ingresso): la validazione e' UNA, non due copie da tenere allineate.
|
|
57
57
|
const EPOCH_RE = /^[a-f0-9]{16}$/;
|
|
@@ -112,7 +112,7 @@ function createLeaseManager(cfg = {}, seams = {}) {
|
|
|
112
112
|
// IC1.1 (rev28, emendata da rev29): quando il proof nasce da un commit del
|
|
113
113
|
// bound, issuedAt è DERIVATO da D (D − PROOF_TTL_MS), mai letto dal clock:
|
|
114
114
|
// expiresAt = issuedAt + TTL è esattamente D, senza toccare verifyProof,
|
|
115
|
-
// KIND_FIELDS o wire (vincolo
|
|
115
|
+
// KIND_FIELDS o wire (vincolo della revisione).
|
|
116
116
|
function issueLeaseProof(cellId, entry, lease, { issuedAt } = {}) {
|
|
117
117
|
return signProof(ensureVerifier(), {
|
|
118
118
|
kind: 'lease',
|
|
@@ -142,7 +142,7 @@ function createLeaseManager(cfg = {}, seams = {}) {
|
|
|
142
142
|
throw e;
|
|
143
143
|
}
|
|
144
144
|
const obj = JSON.parse(raw);
|
|
145
|
-
//
|
|
145
|
+
// da revisione: la FORMA del dato. La root deve essere l'entry attesa: plain-object
|
|
146
146
|
// con i campi del formato per-cell. Qualunque altra cosa e' illeggibile.
|
|
147
147
|
if (obj === null || typeof obj !== 'object' || Array.isArray(obj)) {
|
|
148
148
|
throw new Error(`store lease: entry per-cella non e' un oggetto: ${sanitizeCell(cellId)}`);
|
|
@@ -211,7 +211,7 @@ function createLeaseManager(cfg = {}, seams = {}) {
|
|
|
211
211
|
const prev = entry.graceDeadline;
|
|
212
212
|
entry.graceDeadline = D;
|
|
213
213
|
if (!persistEntry(cellId, entry)) {
|
|
214
|
-
entry.graceDeadline = prev; // rollback (
|
|
214
|
+
entry.graceDeadline = prev; // rollback (revisione): nessun commit, nessun ACK
|
|
215
215
|
return null;
|
|
216
216
|
}
|
|
217
217
|
return { t, deadline: D, issuedAt: D - PROOF_TTL_MS };
|
|
@@ -242,7 +242,7 @@ function createLeaseManager(cfg = {}, seams = {}) {
|
|
|
242
242
|
}
|
|
243
243
|
|
|
244
244
|
function bindLiveSocket(cellId, entry, socket, { lease }) {
|
|
245
|
-
//
|
|
245
|
+
// da revisione interna: persiste il bound PRIMA di associare il socket. Se il
|
|
246
246
|
// record non committa, rollback e ritorna false: il caller non dichiarera' lease.
|
|
247
247
|
// Diverso da EOF (onEOF): li una write fallita lascia un bound ANTERIORE (fail-closed
|
|
248
248
|
// anticipato, accettabile); qui il bind prometterebbe stato live senza commit durevole.
|
|
@@ -314,7 +314,7 @@ function createLeaseManager(cfg = {}, seams = {}) {
|
|
|
314
314
|
socket.on('data', onData);
|
|
315
315
|
socket.once('close', onEOF);
|
|
316
316
|
socket.once('end', onEOF);
|
|
317
|
-
// Correzione (
|
|
317
|
+
// Correzione (da revisione, segnalazione precisata): questo E' il socket
|
|
318
318
|
// su cui il server SCRIVE (l'ack di refresh — proprio dove l'EPIPE nasce se
|
|
319
319
|
// il peer muore mentre la write e' in volo). Un EventEmitter che emette
|
|
320
320
|
// 'error' senza listener fa un throw che termina l'INTERO processo — non
|
|
@@ -345,7 +345,7 @@ function createLeaseManager(cfg = {}, seams = {}) {
|
|
|
345
345
|
socket.setEncoding('utf8');
|
|
346
346
|
socket.on('data', onData);
|
|
347
347
|
socket.once('close', () => { try { socket.destroy(); } catch (_) {} });
|
|
348
|
-
// Correzione (
|
|
348
|
+
// Correzione (da revisione interna): stesso motivo di bindLiveSocket — il server scrive
|
|
349
349
|
// 'deny' su questo socket (onData sopra, JSON malformato); senza un
|
|
350
350
|
// handler 'error' un peer che muore mentre quella write e' in volo manda
|
|
351
351
|
// in throw fatale l'intero processo. Se la connessione avanza a
|
|
@@ -448,7 +448,7 @@ function createLeaseManager(cfg = {}, seams = {}) {
|
|
|
448
448
|
const server = netImpl.createServer((sock) => onStableConnection(cellId, sock));
|
|
449
449
|
await new Promise((resolve, reject) => {
|
|
450
450
|
server.once('error', reject);
|
|
451
|
-
//
|
|
451
|
+
// da revisione interna: niente piu' set/restore di process.umask.
|
|
452
452
|
// process.umask e' GLOBALE e la coppia set/restore non e' atomica: sotto
|
|
453
453
|
// openEndpoint concorrenti il restore di uno ripristinava lo 0o177 appena
|
|
454
454
|
// settato dall'altro e il processo restava driftato. La protezione e'
|
|
@@ -475,7 +475,7 @@ function createLeaseManager(cfg = {}, seams = {}) {
|
|
|
475
475
|
return sp;
|
|
476
476
|
}
|
|
477
477
|
|
|
478
|
-
//
|
|
478
|
+
// da revisione interna: track() serializzato PER CELLA. Due track
|
|
479
479
|
// simultanei sulla stessa cella vedevano entrambi cells.get() vuoto, generavano
|
|
480
480
|
// due identity divergenti e il perdente restava con un'identity inutile. La
|
|
481
481
|
// catena per cellId fa attendere al secondo la fine del primo; poi
|
|
@@ -501,7 +501,7 @@ function createLeaseManager(cfg = {}, seams = {}) {
|
|
|
501
501
|
// server NON si genera una nuova launchEpoch, perche' il supervisore vivo
|
|
502
502
|
// reconnecta con quella originale (e il suo proof la porta firmata).
|
|
503
503
|
const existing = cells.get(cellId);
|
|
504
|
-
//
|
|
504
|
+
// da revisione interna: snapshot dell'entry preesistente prima di mutare
|
|
505
505
|
// qualunque cosa. Se persistEntry fallisce su una cella gia' viva, il cleanup
|
|
506
506
|
// deve ripristinare lo stato precedente, NON cancellarlo.
|
|
507
507
|
const hadExisting = !!existing;
|
|
@@ -513,7 +513,7 @@ function createLeaseManager(cfg = {}, seams = {}) {
|
|
|
513
513
|
} : null;
|
|
514
514
|
// openEndpoint puo' aver creato un nuovo stableServer se l'entry non ne aveva uno.
|
|
515
515
|
const serverBeforeOpen = existing ? existing.stableServer : null;
|
|
516
|
-
//
|
|
516
|
+
// da revisione interna: il SECONDO INGRESSO valida come il primo. Un valore
|
|
517
517
|
// persistito e' riusato SOLO se ha il formato che il runtime produce
|
|
518
518
|
// (hex-16 l'epoch); malformato -> identity NUOVA generata, e la persistEntry
|
|
519
519
|
// qui sotto la scrive, riparando la entry corrotta sul disco.
|
|
@@ -541,8 +541,8 @@ function createLeaseManager(cfg = {}, seams = {}) {
|
|
|
541
541
|
await openEndpoint(cellId, entry);
|
|
542
542
|
cells.set(cellId, entry);
|
|
543
543
|
if (!persistEntry(cellId, entry)) {
|
|
544
|
-
//
|
|
545
|
-
//
|
|
544
|
+
// da revisione: il record durevole (identity) e' essenziale per la recovery post-restart.
|
|
545
|
+
// da revisione interna: se la cella era GIA' viva, NON cancellarla — ripristina
|
|
546
546
|
// lo stato precedente. Solo le risorse create in QUESTO tentativo vengono pulite.
|
|
547
547
|
if (hadExisting) {
|
|
548
548
|
entry.launchEpoch = existingSnapshot.launchEpoch;
|
|
@@ -590,7 +590,7 @@ function createLeaseManager(cfg = {}, seams = {}) {
|
|
|
590
590
|
// payload il canale appartiene al protocollo del broker (frame length-
|
|
591
591
|
// prefixed u32): una riga JSON scritta prima del payload corromperebbe la
|
|
592
592
|
// lettura di receivePayload e il child non nascerebbe — misurato dal gate
|
|
593
|
-
// (
|
|
593
|
+
// (da revisione interna, esito positivo). Il proof supervisore arriva con
|
|
594
594
|
// l'ACK del primo refresh, che il lease-client invia IMMEDIATAMENTE
|
|
595
595
|
// all'avvio: la finestra attach->primo-ack senza proof detenuto e' fail-closed
|
|
596
596
|
// (reconnect negato -> grace -> onLost), coerente col modello per cui nessun
|
|
@@ -628,7 +628,7 @@ function createLeaseManager(cfg = {}, seams = {}) {
|
|
|
628
628
|
log(`cell-lease: loadPersisted entry '${cellId}' illeggibile, saltata: ${e && e.message}`);
|
|
629
629
|
continue;
|
|
630
630
|
}
|
|
631
|
-
//
|
|
631
|
+
// da revisione interna: forma <> semantica. Il runtime produce launchEpoch
|
|
632
632
|
// come hex di 16 char. Fail-closed: formato errato = entry saltata.
|
|
633
633
|
if (!info || typeof info.launchEpoch !== 'string' || !EPOCH_RE.test(info.launchEpoch)) {
|
|
634
634
|
log(`cell-lease: loadPersisted entry '${cellId}' malformata, saltata`);
|
|
@@ -640,7 +640,7 @@ function createLeaseManager(cfg = {}, seams = {}) {
|
|
|
640
640
|
cells.set(cellId, {
|
|
641
641
|
launchEpoch: info.launchEpoch,
|
|
642
642
|
// IC8.5 (rev29): graceDeadline assente/illeggibile/non-intero = bound trattato come scaduto (0 -> deny sempre).
|
|
643
|
-
//
|
|
643
|
+
// da revisione: forma <> semantica — un bound oltre now()+2*GRACE_MS non e' producibile
|
|
644
644
|
// da questa fetta: trattato come scaduto (0). Tolleranza 2*GRACE_MS per un
|
|
645
645
|
// restart durante la grace. Fail-closed: bound assurdo = scaduto.
|
|
646
646
|
graceDeadline: (Number.isInteger(info.graceDeadline) && info.graceDeadline > 0
|