@vascend/talos 0.1.0 → 0.1.1
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/README.md +7 -3
- package/dist/backend/asset-events.js +9 -9
- package/dist/core/agent.js +3 -3
- package/dist/core/cli-args.js +50 -50
- package/dist/core/cli-installation.d.ts +4 -0
- package/dist/core/cli-installation.js +55 -0
- package/dist/core/compaction.js +26 -26
- package/dist/core/config.js +101 -101
- package/dist/core/execution-guidance.js +21 -21
- package/dist/core/package-update.d.ts +3 -1
- package/dist/core/package-update.js +19 -6
- package/dist/core/self-update.d.ts +1 -1
- package/dist/core/signed-release.d.ts +33 -0
- package/dist/core/signed-release.js +121 -0
- package/dist/core/update-check.d.ts +1 -1
- package/dist/core/update-check.js +15 -14
- package/dist/core/vibe-modes.js +13 -13
- package/dist/distribution/install.d.ts +1 -0
- package/dist/distribution/install.js +31 -0
- package/dist/install.mjs +271 -0
- package/dist/node-runtime.json +13 -0
- package/dist/providers/oauth-callback-server.js +32 -32
- package/dist/tools/capabilities.js +30 -30
- package/dist/tools/computer-capture.js +50 -50
- package/dist/tools/program.js +82 -82
- package/dist/tui/i18n-catalog.js +16 -0
- package/dist/tui/quick-help.js +31 -31
- package/dist/tui/repl.js +5 -5
- package/dist/tui/update-prompt.js +1 -0
- package/dist/vascend/code-events.js +9 -9
- package/dist/vascend/completeness.js +31 -31
- package/dist/vascend/context-fabric.js +16 -16
- package/dist/vascend/contract-memory.js +30 -30
- package/dist/vascend/council.js +82 -82
- package/dist/vascend/decision-promotion.js +23 -23
- package/dist/vascend/granular-kingdom.js +17 -17
- package/dist/vascend/granular-launcher.js +2 -2
- package/dist/vascend/kingdom-kpi.js +19 -19
- package/dist/vascend/kingdom-run.js +6 -6
- package/dist/vascend/kingdom.js +56 -56
- package/dist/vascend/mandate.js +21 -21
- package/dist/vascend/objective-revision.js +34 -34
- package/dist/vascend/patch-plan.js +22 -22
- package/dist/vascend/requirements.js +29 -29
- package/dist/vascend/reviewer.js +26 -26
- package/dist/vascend/room-memory.js +25 -25
- package/dist/vascend/runtime.js +44 -44
- package/dist/vascend/self-improve.js +39 -39
- package/dist/vascend/test-failure-memory.js +9 -9
- package/dist/vascend/vision.js +18 -18
- package/dist/vendor/computer-use/README.md +7 -7
- package/dist/vendor/computer-use/computer-desktop.js +211 -211
- package/dist/vendor/computer-use/computer-observe.js +315 -315
- package/dist/vendor/computer-use/computer-scripts.js +1230 -1230
- package/dist/vendor/computer-use/computer-tools.js +2136 -2136
- package/dist/vendor/computer-use/package.json +1 -1
- package/dist/vendor/computer-use/rac/atomic-input.js +91 -91
- package/dist/vendor/computer-use/rac/grammatica.js +101 -101
- package/dist/vendor/computer-use/rac/parser.js +185 -185
- package/dist/vendor/computer-use/rac/protocol.js +136 -136
- package/install.ps1 +374 -30
- package/install.sh +356 -43
- package/package.json +2 -2
- package/dist/core/kingdoms-mode-state.d.ts +0 -24
- package/dist/core/kingdoms-mode-state.js +0 -139
- package/dist/text-sanitize.js +0 -28
|
@@ -1,185 +1,185 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
// Parser incrementale MCP-RAC. L'unica uscita eseguibile e' `chunk`, prodotta
|
|
4
|
-
// esclusivamente dopo `;`. END chiude l'azione ma non rende eseguibile il testo
|
|
5
|
-
// che lo precede: un ultimo segmento senza `;` viene rifiutato.
|
|
6
|
-
|
|
7
|
-
const G = require('./grammatica.js');
|
|
8
|
-
|
|
9
|
-
const PENSIERO_INIZIO = '<|thought_start|>';
|
|
10
|
-
const PENSIERO_FINE = '<|thought_end|>';
|
|
11
|
-
const CODA_MINIMA = Math.max(PENSIERO_INIZIO.length, PENSIERO_FINE.length, 'ACTION'.length);
|
|
12
|
-
|
|
13
|
-
function creaParser(options = {}) {
|
|
14
|
-
const bufferMassimo = Number.isInteger(options.bufferMassimo)
|
|
15
|
-
? Math.max(256, options.bufferMassimo) : G.LIMITI.BUFFER_MASSIMO;
|
|
16
|
-
let buffer = '';
|
|
17
|
-
let stato = 'fuori';
|
|
18
|
-
let azione = null;
|
|
19
|
-
let sequenza = 0;
|
|
20
|
-
|
|
21
|
-
const scartoCorrente = () => azione ? {
|
|
22
|
-
observationId: azione.observationId,
|
|
23
|
-
chunkEmessi: azione.mouseVisto + azione.keysetEmessi,
|
|
24
|
-
ultimaSequenza: sequenza,
|
|
25
|
-
} : null;
|
|
26
|
-
|
|
27
|
-
function errore(motivo) {
|
|
28
|
-
const scarto = scartoCorrente();
|
|
29
|
-
azione = null;
|
|
30
|
-
stato = 'avvelenato';
|
|
31
|
-
return { tipo: 'errore', motivo, scarto };
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function commitSegmento(grezzo) {
|
|
35
|
-
if (!azione) return errore('segmento fuori da ACTION');
|
|
36
|
-
const testo = grezzo.trim();
|
|
37
|
-
let parsed;
|
|
38
|
-
let indice;
|
|
39
|
-
if (!azione.mouseVisto) {
|
|
40
|
-
parsed = G.analizzaMouse(testo);
|
|
41
|
-
indice = 0;
|
|
42
|
-
if (!parsed.errore) azione.mouseVisto = 1;
|
|
43
|
-
} else {
|
|
44
|
-
if (azione.keysetEmessi >= G.LIMITI.CHUNK_PER_AZIONE) {
|
|
45
|
-
return errore(`piu' di ${G.LIMITI.CHUNK_PER_AZIONE} keyset in un'azione`);
|
|
46
|
-
}
|
|
47
|
-
parsed = G.analizzaKeyset(testo);
|
|
48
|
-
indice = azione.keysetEmessi;
|
|
49
|
-
if (!parsed.errore) azione.keysetEmessi += 1;
|
|
50
|
-
}
|
|
51
|
-
if (parsed.errore) return errore(parsed.errore);
|
|
52
|
-
sequenza += 1;
|
|
53
|
-
return {
|
|
54
|
-
tipo: 'chunk',
|
|
55
|
-
chunk: {
|
|
56
|
-
...parsed,
|
|
57
|
-
observationId: azione.observationId,
|
|
58
|
-
ttlMs: azione.ttlMs,
|
|
59
|
-
sequenza,
|
|
60
|
-
indice,
|
|
61
|
-
},
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function trovaAction() {
|
|
66
|
-
const match = /(?:^|\n)ACTION(?=\s)/.exec(buffer);
|
|
67
|
-
if (!match) return -1;
|
|
68
|
-
return match.index + (buffer[match.index] === '\n' ? 1 : 0);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function passo(finale) {
|
|
72
|
-
if (stato === 'pensiero') {
|
|
73
|
-
const fine = buffer.indexOf(PENSIERO_FINE);
|
|
74
|
-
if (fine < 0) {
|
|
75
|
-
const sicuro = finale ? buffer.length : Math.max(0, buffer.length - CODA_MINIMA);
|
|
76
|
-
if (!sicuro) return null;
|
|
77
|
-
const testo = buffer.slice(0, sicuro);
|
|
78
|
-
buffer = buffer.slice(sicuro);
|
|
79
|
-
return { evento: { tipo: 'pensiero', testo } };
|
|
80
|
-
}
|
|
81
|
-
const testo = buffer.slice(0, fine);
|
|
82
|
-
buffer = buffer.slice(fine + PENSIERO_FINE.length);
|
|
83
|
-
stato = 'fuori';
|
|
84
|
-
return { evento: testo ? { tipo: 'pensiero', testo } : { tipo: 'pensiero_fine' } };
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
if (stato !== 'corpo' && buffer.startsWith(PENSIERO_INIZIO)) {
|
|
88
|
-
buffer = buffer.slice(PENSIERO_INIZIO.length);
|
|
89
|
-
stato = 'pensiero';
|
|
90
|
-
return { avanzato: true };
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
if (stato === 'fuori' || stato === 'avvelenato') {
|
|
94
|
-
const idx = trovaAction();
|
|
95
|
-
if (idx < 0) {
|
|
96
|
-
const tieni = finale ? 0 : CODA_MINIMA;
|
|
97
|
-
if (buffer.length > tieni) {
|
|
98
|
-
buffer = tieni ? buffer.slice(-tieni) : '';
|
|
99
|
-
return { avanzato: true };
|
|
100
|
-
}
|
|
101
|
-
return null;
|
|
102
|
-
}
|
|
103
|
-
if (idx > 0) {
|
|
104
|
-
buffer = buffer.slice(idx);
|
|
105
|
-
return { avanzato: true };
|
|
106
|
-
}
|
|
107
|
-
const aCapo = buffer.indexOf('\n');
|
|
108
|
-
if (aCapo < 0) return null;
|
|
109
|
-
const testa = G.analizzaIntestazione(buffer.slice(0, aCapo).replace(/\r$/, ''));
|
|
110
|
-
buffer = buffer.slice(aCapo + 1);
|
|
111
|
-
if (testa.errore) return { evento: errore(testa.errore) };
|
|
112
|
-
azione = {
|
|
113
|
-
observationId: testa.observationId,
|
|
114
|
-
ttlMs: testa.ttlMs,
|
|
115
|
-
mouseVisto: 0,
|
|
116
|
-
keysetEmessi: 0,
|
|
117
|
-
};
|
|
118
|
-
stato = 'corpo';
|
|
119
|
-
return { evento: { tipo: 'intestazione', ...testa } };
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const punto = buffer.indexOf(';');
|
|
123
|
-
const endMatch = /(?:^|\n)\s*END(?=\s|$)/.exec(buffer);
|
|
124
|
-
const fine = endMatch ? endMatch.index : -1;
|
|
125
|
-
if (punto >= 0 && (fine < 0 || punto < fine)) {
|
|
126
|
-
const segmento = buffer.slice(0, punto);
|
|
127
|
-
buffer = buffer.slice(punto + 1);
|
|
128
|
-
return { evento: commitSegmento(segmento) };
|
|
129
|
-
}
|
|
130
|
-
if (fine >= 0) {
|
|
131
|
-
const prefisso = buffer.slice(0, fine).trim();
|
|
132
|
-
const conclusa = azione;
|
|
133
|
-
buffer = buffer.slice(fine + endMatch[0].length);
|
|
134
|
-
if (prefisso) return { evento: errore('segmento prima di END privo di delimitatore ;') };
|
|
135
|
-
if (!conclusa.mouseVisto || conclusa.keysetEmessi !== G.LIMITI.CHUNK_PER_AZIONE) {
|
|
136
|
-
return { evento: errore(`azione incompleta: attesi mouse + ${G.LIMITI.CHUNK_PER_AZIONE} keyset`) };
|
|
137
|
-
}
|
|
138
|
-
azione = null;
|
|
139
|
-
stato = 'fuori';
|
|
140
|
-
return { evento: { tipo: 'fine_azione', observationId: conclusa.observationId, ultimaSequenza: sequenza } };
|
|
141
|
-
}
|
|
142
|
-
return null;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function estrai(finale) {
|
|
146
|
-
const eventi = [];
|
|
147
|
-
for (;;) {
|
|
148
|
-
const r = passo(finale);
|
|
149
|
-
if (!r) break;
|
|
150
|
-
if (r.evento) eventi.push(r.evento);
|
|
151
|
-
}
|
|
152
|
-
return eventi;
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
return {
|
|
156
|
-
push(delta) {
|
|
157
|
-
buffer += String(delta == null ? '' : delta);
|
|
158
|
-
if (buffer.length > bufferMassimo) {
|
|
159
|
-
const event = errore(`buffer oltre ${bufferMassimo} byte`);
|
|
160
|
-
buffer = buffer.slice(-CODA_MINIMA);
|
|
161
|
-
return [event, ...estrai(false)];
|
|
162
|
-
}
|
|
163
|
-
return estrai(false);
|
|
164
|
-
},
|
|
165
|
-
fine() {
|
|
166
|
-
const eventi = estrai(true);
|
|
167
|
-
if (stato === 'corpo' && azione) eventi.push({ tipo: 'troncata', ...scartoCorrente() });
|
|
168
|
-
azione = null;
|
|
169
|
-
stato = 'fuori';
|
|
170
|
-
buffer = '';
|
|
171
|
-
return eventi;
|
|
172
|
-
},
|
|
173
|
-
reset() {
|
|
174
|
-
const scarto = scartoCorrente();
|
|
175
|
-
buffer = '';
|
|
176
|
-
stato = 'fuori';
|
|
177
|
-
azione = null;
|
|
178
|
-
return scarto;
|
|
179
|
-
},
|
|
180
|
-
stato: () => stato,
|
|
181
|
-
diagnostica: () => ({ stato, bytesInBuffer: buffer.length, sequenza }),
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
module.exports = { creaParser, PENSIERO_INIZIO, PENSIERO_FINE };
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Parser incrementale MCP-RAC. L'unica uscita eseguibile e' `chunk`, prodotta
|
|
4
|
+
// esclusivamente dopo `;`. END chiude l'azione ma non rende eseguibile il testo
|
|
5
|
+
// che lo precede: un ultimo segmento senza `;` viene rifiutato.
|
|
6
|
+
|
|
7
|
+
const G = require('./grammatica.js');
|
|
8
|
+
|
|
9
|
+
const PENSIERO_INIZIO = '<|thought_start|>';
|
|
10
|
+
const PENSIERO_FINE = '<|thought_end|>';
|
|
11
|
+
const CODA_MINIMA = Math.max(PENSIERO_INIZIO.length, PENSIERO_FINE.length, 'ACTION'.length);
|
|
12
|
+
|
|
13
|
+
function creaParser(options = {}) {
|
|
14
|
+
const bufferMassimo = Number.isInteger(options.bufferMassimo)
|
|
15
|
+
? Math.max(256, options.bufferMassimo) : G.LIMITI.BUFFER_MASSIMO;
|
|
16
|
+
let buffer = '';
|
|
17
|
+
let stato = 'fuori';
|
|
18
|
+
let azione = null;
|
|
19
|
+
let sequenza = 0;
|
|
20
|
+
|
|
21
|
+
const scartoCorrente = () => azione ? {
|
|
22
|
+
observationId: azione.observationId,
|
|
23
|
+
chunkEmessi: azione.mouseVisto + azione.keysetEmessi,
|
|
24
|
+
ultimaSequenza: sequenza,
|
|
25
|
+
} : null;
|
|
26
|
+
|
|
27
|
+
function errore(motivo) {
|
|
28
|
+
const scarto = scartoCorrente();
|
|
29
|
+
azione = null;
|
|
30
|
+
stato = 'avvelenato';
|
|
31
|
+
return { tipo: 'errore', motivo, scarto };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function commitSegmento(grezzo) {
|
|
35
|
+
if (!azione) return errore('segmento fuori da ACTION');
|
|
36
|
+
const testo = grezzo.trim();
|
|
37
|
+
let parsed;
|
|
38
|
+
let indice;
|
|
39
|
+
if (!azione.mouseVisto) {
|
|
40
|
+
parsed = G.analizzaMouse(testo);
|
|
41
|
+
indice = 0;
|
|
42
|
+
if (!parsed.errore) azione.mouseVisto = 1;
|
|
43
|
+
} else {
|
|
44
|
+
if (azione.keysetEmessi >= G.LIMITI.CHUNK_PER_AZIONE) {
|
|
45
|
+
return errore(`piu' di ${G.LIMITI.CHUNK_PER_AZIONE} keyset in un'azione`);
|
|
46
|
+
}
|
|
47
|
+
parsed = G.analizzaKeyset(testo);
|
|
48
|
+
indice = azione.keysetEmessi;
|
|
49
|
+
if (!parsed.errore) azione.keysetEmessi += 1;
|
|
50
|
+
}
|
|
51
|
+
if (parsed.errore) return errore(parsed.errore);
|
|
52
|
+
sequenza += 1;
|
|
53
|
+
return {
|
|
54
|
+
tipo: 'chunk',
|
|
55
|
+
chunk: {
|
|
56
|
+
...parsed,
|
|
57
|
+
observationId: azione.observationId,
|
|
58
|
+
ttlMs: azione.ttlMs,
|
|
59
|
+
sequenza,
|
|
60
|
+
indice,
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function trovaAction() {
|
|
66
|
+
const match = /(?:^|\n)ACTION(?=\s)/.exec(buffer);
|
|
67
|
+
if (!match) return -1;
|
|
68
|
+
return match.index + (buffer[match.index] === '\n' ? 1 : 0);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function passo(finale) {
|
|
72
|
+
if (stato === 'pensiero') {
|
|
73
|
+
const fine = buffer.indexOf(PENSIERO_FINE);
|
|
74
|
+
if (fine < 0) {
|
|
75
|
+
const sicuro = finale ? buffer.length : Math.max(0, buffer.length - CODA_MINIMA);
|
|
76
|
+
if (!sicuro) return null;
|
|
77
|
+
const testo = buffer.slice(0, sicuro);
|
|
78
|
+
buffer = buffer.slice(sicuro);
|
|
79
|
+
return { evento: { tipo: 'pensiero', testo } };
|
|
80
|
+
}
|
|
81
|
+
const testo = buffer.slice(0, fine);
|
|
82
|
+
buffer = buffer.slice(fine + PENSIERO_FINE.length);
|
|
83
|
+
stato = 'fuori';
|
|
84
|
+
return { evento: testo ? { tipo: 'pensiero', testo } : { tipo: 'pensiero_fine' } };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (stato !== 'corpo' && buffer.startsWith(PENSIERO_INIZIO)) {
|
|
88
|
+
buffer = buffer.slice(PENSIERO_INIZIO.length);
|
|
89
|
+
stato = 'pensiero';
|
|
90
|
+
return { avanzato: true };
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (stato === 'fuori' || stato === 'avvelenato') {
|
|
94
|
+
const idx = trovaAction();
|
|
95
|
+
if (idx < 0) {
|
|
96
|
+
const tieni = finale ? 0 : CODA_MINIMA;
|
|
97
|
+
if (buffer.length > tieni) {
|
|
98
|
+
buffer = tieni ? buffer.slice(-tieni) : '';
|
|
99
|
+
return { avanzato: true };
|
|
100
|
+
}
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
if (idx > 0) {
|
|
104
|
+
buffer = buffer.slice(idx);
|
|
105
|
+
return { avanzato: true };
|
|
106
|
+
}
|
|
107
|
+
const aCapo = buffer.indexOf('\n');
|
|
108
|
+
if (aCapo < 0) return null;
|
|
109
|
+
const testa = G.analizzaIntestazione(buffer.slice(0, aCapo).replace(/\r$/, ''));
|
|
110
|
+
buffer = buffer.slice(aCapo + 1);
|
|
111
|
+
if (testa.errore) return { evento: errore(testa.errore) };
|
|
112
|
+
azione = {
|
|
113
|
+
observationId: testa.observationId,
|
|
114
|
+
ttlMs: testa.ttlMs,
|
|
115
|
+
mouseVisto: 0,
|
|
116
|
+
keysetEmessi: 0,
|
|
117
|
+
};
|
|
118
|
+
stato = 'corpo';
|
|
119
|
+
return { evento: { tipo: 'intestazione', ...testa } };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const punto = buffer.indexOf(';');
|
|
123
|
+
const endMatch = /(?:^|\n)\s*END(?=\s|$)/.exec(buffer);
|
|
124
|
+
const fine = endMatch ? endMatch.index : -1;
|
|
125
|
+
if (punto >= 0 && (fine < 0 || punto < fine)) {
|
|
126
|
+
const segmento = buffer.slice(0, punto);
|
|
127
|
+
buffer = buffer.slice(punto + 1);
|
|
128
|
+
return { evento: commitSegmento(segmento) };
|
|
129
|
+
}
|
|
130
|
+
if (fine >= 0) {
|
|
131
|
+
const prefisso = buffer.slice(0, fine).trim();
|
|
132
|
+
const conclusa = azione;
|
|
133
|
+
buffer = buffer.slice(fine + endMatch[0].length);
|
|
134
|
+
if (prefisso) return { evento: errore('segmento prima di END privo di delimitatore ;') };
|
|
135
|
+
if (!conclusa.mouseVisto || conclusa.keysetEmessi !== G.LIMITI.CHUNK_PER_AZIONE) {
|
|
136
|
+
return { evento: errore(`azione incompleta: attesi mouse + ${G.LIMITI.CHUNK_PER_AZIONE} keyset`) };
|
|
137
|
+
}
|
|
138
|
+
azione = null;
|
|
139
|
+
stato = 'fuori';
|
|
140
|
+
return { evento: { tipo: 'fine_azione', observationId: conclusa.observationId, ultimaSequenza: sequenza } };
|
|
141
|
+
}
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function estrai(finale) {
|
|
146
|
+
const eventi = [];
|
|
147
|
+
for (;;) {
|
|
148
|
+
const r = passo(finale);
|
|
149
|
+
if (!r) break;
|
|
150
|
+
if (r.evento) eventi.push(r.evento);
|
|
151
|
+
}
|
|
152
|
+
return eventi;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
push(delta) {
|
|
157
|
+
buffer += String(delta == null ? '' : delta);
|
|
158
|
+
if (buffer.length > bufferMassimo) {
|
|
159
|
+
const event = errore(`buffer oltre ${bufferMassimo} byte`);
|
|
160
|
+
buffer = buffer.slice(-CODA_MINIMA);
|
|
161
|
+
return [event, ...estrai(false)];
|
|
162
|
+
}
|
|
163
|
+
return estrai(false);
|
|
164
|
+
},
|
|
165
|
+
fine() {
|
|
166
|
+
const eventi = estrai(true);
|
|
167
|
+
if (stato === 'corpo' && azione) eventi.push({ tipo: 'troncata', ...scartoCorrente() });
|
|
168
|
+
azione = null;
|
|
169
|
+
stato = 'fuori';
|
|
170
|
+
buffer = '';
|
|
171
|
+
return eventi;
|
|
172
|
+
},
|
|
173
|
+
reset() {
|
|
174
|
+
const scarto = scartoCorrente();
|
|
175
|
+
buffer = '';
|
|
176
|
+
stato = 'fuori';
|
|
177
|
+
azione = null;
|
|
178
|
+
return scarto;
|
|
179
|
+
},
|
|
180
|
+
stato: () => stato,
|
|
181
|
+
diagnostica: () => ({ stato, bytesInBuffer: buffer.length, sequenza }),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
module.exports = { creaParser, PENSIERO_INIZIO, PENSIERO_FINE };
|
|
@@ -1,136 +1,136 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
const crypto = require('node:crypto');
|
|
4
|
-
|
|
5
|
-
const EXTENSION_ID = 'io.vascend.mcp.reactive-control/0.1';
|
|
6
|
-
const MAGIC = Buffer.from('RAC1');
|
|
7
|
-
const VERSION = 1;
|
|
8
|
-
const HEADER_BYTES = 52;
|
|
9
|
-
|
|
10
|
-
const MESSAGE_TYPES = Object.freeze({
|
|
11
|
-
OBS_KEYFRAME: 1,
|
|
12
|
-
OBS_DELTA: 2,
|
|
13
|
-
ACTION_START: 3,
|
|
14
|
-
ACTION_CHUNK: 4,
|
|
15
|
-
ACTION_REVOKE: 5,
|
|
16
|
-
ACK: 6,
|
|
17
|
-
SAFETY_STOP: 7,
|
|
18
|
-
});
|
|
19
|
-
const MESSAGE_NAMES = Object.freeze(Object.fromEntries(
|
|
20
|
-
Object.entries(MESSAGE_TYPES).map(([name, value]) => [value, name]),
|
|
21
|
-
));
|
|
22
|
-
|
|
23
|
-
const SERVER_CAPABILITIES = Object.freeze({
|
|
24
|
-
extension: EXTENSION_ID,
|
|
25
|
-
version: VERSION,
|
|
26
|
-
transport: ['node-ipc'],
|
|
27
|
-
framing: ['rac-binary-v1'],
|
|
28
|
-
observation: ['keyframe', 'delta', 'checkpoint', 'revoke'],
|
|
29
|
-
actionRateHz: 30,
|
|
30
|
-
observationRateHz: 5,
|
|
31
|
-
maxActionHorizonMs: 200,
|
|
32
|
-
maxLeaseMs: 250,
|
|
33
|
-
strictObservationMaxAgeMs: 450,
|
|
34
|
-
cumulativeAck: true,
|
|
35
|
-
checksum: 'crc32',
|
|
36
|
-
duplexObservations: false,
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
let crcTable = null;
|
|
40
|
-
function tableCrc32() {
|
|
41
|
-
if (crcTable) return crcTable;
|
|
42
|
-
crcTable = Array.from({ length: 256 }, (_, index) => {
|
|
43
|
-
let value = index;
|
|
44
|
-
for (let bit = 0; bit < 8; bit += 1) {
|
|
45
|
-
value = (value >>> 1) ^ ((value & 1) ? 0xedb88320 : 0);
|
|
46
|
-
}
|
|
47
|
-
return value >>> 0;
|
|
48
|
-
});
|
|
49
|
-
return crcTable;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function crc32(value) {
|
|
53
|
-
const bytes = Buffer.isBuffer(value) ? value : Buffer.from(value || '');
|
|
54
|
-
const table = tableCrc32();
|
|
55
|
-
let crc = 0xffffffff;
|
|
56
|
-
for (const byte of bytes) crc = (crc >>> 8) ^ table[(crc ^ byte) & 0xff];
|
|
57
|
-
return (crc ^ 0xffffffff) >>> 0;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
function sessionDigest(sessionId) {
|
|
61
|
-
return crypto.createHash('sha256').update(String(sessionId || '')).digest().subarray(0, 16);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function safeBigInt(value, name) {
|
|
65
|
-
try {
|
|
66
|
-
const parsed = BigInt(value || 0);
|
|
67
|
-
if (parsed < 0n || parsed > 0xffffffffffffffffn) throw new RangeError(name);
|
|
68
|
-
return parsed;
|
|
69
|
-
} catch (_) {
|
|
70
|
-
throw new TypeError(`${name || 'value'} non valido`);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function encodeFrame(input = {}) {
|
|
75
|
-
const type = typeof input.type === 'string' ? MESSAGE_TYPES[input.type] : Number(input.type);
|
|
76
|
-
if (!MESSAGE_NAMES[type]) throw new TypeError('RAC_MESSAGE_TYPE_INVALID');
|
|
77
|
-
const payload = Buffer.isBuffer(input.payload) ? input.payload : Buffer.from(String(input.payload || ''), 'utf8');
|
|
78
|
-
if (payload.length > 16 * 1024 * 1024) throw new RangeError('RAC_PAYLOAD_TOO_LARGE');
|
|
79
|
-
const header = Buffer.alloc(HEADER_BYTES);
|
|
80
|
-
MAGIC.copy(header, 0);
|
|
81
|
-
header.writeUInt8(VERSION, 4);
|
|
82
|
-
header.writeUInt8(type, 5);
|
|
83
|
-
header.writeUInt16BE(Number(input.flags || 0) & 0xffff, 6);
|
|
84
|
-
header.writeUInt32BE(Number(input.sequence || 0) >>> 0, 8);
|
|
85
|
-
header.writeBigUInt64BE(safeBigInt(input.monotonicUs, 'monotonicUs'), 12);
|
|
86
|
-
header.writeBigUInt64BE(safeBigInt(input.observationId, 'observationId'), 20);
|
|
87
|
-
header.writeUInt32BE(payload.length >>> 0, 28);
|
|
88
|
-
header.writeUInt32BE(crc32(payload), 32);
|
|
89
|
-
sessionDigest(input.sessionId).copy(header, 36);
|
|
90
|
-
return Buffer.concat([header, payload]);
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
function decodeFrame(value, expected = {}) {
|
|
94
|
-
const frame = Buffer.isBuffer(value) ? value : Buffer.from(value || '');
|
|
95
|
-
if (frame.length < HEADER_BYTES) return { ok: false, code: 'RAC_FRAME_TRUNCATED' };
|
|
96
|
-
if (!frame.subarray(0, 4).equals(MAGIC)) return { ok: false, code: 'RAC_FRAME_MAGIC' };
|
|
97
|
-
if (frame.readUInt8(4) !== VERSION) return { ok: false, code: 'RAC_FRAME_VERSION' };
|
|
98
|
-
const type = frame.readUInt8(5);
|
|
99
|
-
if (!MESSAGE_NAMES[type]) return { ok: false, code: 'RAC_FRAME_TYPE' };
|
|
100
|
-
const payloadLength = frame.readUInt32BE(28);
|
|
101
|
-
if (frame.length !== HEADER_BYTES + payloadLength) return { ok: false, code: 'RAC_FRAME_LENGTH' };
|
|
102
|
-
const payload = frame.subarray(HEADER_BYTES);
|
|
103
|
-
if (crc32(payload) !== frame.readUInt32BE(32)) return { ok: false, code: 'RAC_FRAME_CHECKSUM' };
|
|
104
|
-
if (expected.sessionId && !frame.subarray(36, 52).equals(sessionDigest(expected.sessionId))) {
|
|
105
|
-
return { ok: false, code: 'RAC_FRAME_SESSION' };
|
|
106
|
-
}
|
|
107
|
-
const observationId = frame.readBigUInt64BE(20).toString();
|
|
108
|
-
if (expected.observationId && String(expected.observationId) !== observationId) {
|
|
109
|
-
return { ok: false, code: 'STALE_OBSERVATION' };
|
|
110
|
-
}
|
|
111
|
-
return {
|
|
112
|
-
ok: true,
|
|
113
|
-
type: MESSAGE_NAMES[type],
|
|
114
|
-
typeId: type,
|
|
115
|
-
flags: frame.readUInt16BE(6),
|
|
116
|
-
sequence: frame.readUInt32BE(8),
|
|
117
|
-
monotonicUs: frame.readBigUInt64BE(12),
|
|
118
|
-
observationId,
|
|
119
|
-
payload,
|
|
120
|
-
checksum: frame.readUInt32BE(32),
|
|
121
|
-
};
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
function negotiateCapabilities(client = {}) {
|
|
125
|
-
const extension = String(client.extension || EXTENSION_ID);
|
|
126
|
-
if (extension !== EXTENSION_ID) return { ok: false, code: 'RAC_EXTENSION_UNSUPPORTED' };
|
|
127
|
-
const requestedTransports = Array.isArray(client.transport) ? client.transport : ['node-ipc'];
|
|
128
|
-
const transport = SERVER_CAPABILITIES.transport.find((item) => requestedTransports.includes(item));
|
|
129
|
-
if (!transport) return { ok: false, code: 'RAC_TRANSPORT_UNSUPPORTED' };
|
|
130
|
-
return { ok: true, ...SERVER_CAPABILITIES, transport };
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
module.exports = {
|
|
134
|
-
EXTENSION_ID, VERSION, HEADER_BYTES, MESSAGE_TYPES, SERVER_CAPABILITIES,
|
|
135
|
-
crc32, encodeFrame, decodeFrame, negotiateCapabilities,
|
|
136
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const crypto = require('node:crypto');
|
|
4
|
+
|
|
5
|
+
const EXTENSION_ID = 'io.vascend.mcp.reactive-control/0.1';
|
|
6
|
+
const MAGIC = Buffer.from('RAC1');
|
|
7
|
+
const VERSION = 1;
|
|
8
|
+
const HEADER_BYTES = 52;
|
|
9
|
+
|
|
10
|
+
const MESSAGE_TYPES = Object.freeze({
|
|
11
|
+
OBS_KEYFRAME: 1,
|
|
12
|
+
OBS_DELTA: 2,
|
|
13
|
+
ACTION_START: 3,
|
|
14
|
+
ACTION_CHUNK: 4,
|
|
15
|
+
ACTION_REVOKE: 5,
|
|
16
|
+
ACK: 6,
|
|
17
|
+
SAFETY_STOP: 7,
|
|
18
|
+
});
|
|
19
|
+
const MESSAGE_NAMES = Object.freeze(Object.fromEntries(
|
|
20
|
+
Object.entries(MESSAGE_TYPES).map(([name, value]) => [value, name]),
|
|
21
|
+
));
|
|
22
|
+
|
|
23
|
+
const SERVER_CAPABILITIES = Object.freeze({
|
|
24
|
+
extension: EXTENSION_ID,
|
|
25
|
+
version: VERSION,
|
|
26
|
+
transport: ['node-ipc'],
|
|
27
|
+
framing: ['rac-binary-v1'],
|
|
28
|
+
observation: ['keyframe', 'delta', 'checkpoint', 'revoke'],
|
|
29
|
+
actionRateHz: 30,
|
|
30
|
+
observationRateHz: 5,
|
|
31
|
+
maxActionHorizonMs: 200,
|
|
32
|
+
maxLeaseMs: 250,
|
|
33
|
+
strictObservationMaxAgeMs: 450,
|
|
34
|
+
cumulativeAck: true,
|
|
35
|
+
checksum: 'crc32',
|
|
36
|
+
duplexObservations: false,
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
let crcTable = null;
|
|
40
|
+
function tableCrc32() {
|
|
41
|
+
if (crcTable) return crcTable;
|
|
42
|
+
crcTable = Array.from({ length: 256 }, (_, index) => {
|
|
43
|
+
let value = index;
|
|
44
|
+
for (let bit = 0; bit < 8; bit += 1) {
|
|
45
|
+
value = (value >>> 1) ^ ((value & 1) ? 0xedb88320 : 0);
|
|
46
|
+
}
|
|
47
|
+
return value >>> 0;
|
|
48
|
+
});
|
|
49
|
+
return crcTable;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function crc32(value) {
|
|
53
|
+
const bytes = Buffer.isBuffer(value) ? value : Buffer.from(value || '');
|
|
54
|
+
const table = tableCrc32();
|
|
55
|
+
let crc = 0xffffffff;
|
|
56
|
+
for (const byte of bytes) crc = (crc >>> 8) ^ table[(crc ^ byte) & 0xff];
|
|
57
|
+
return (crc ^ 0xffffffff) >>> 0;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function sessionDigest(sessionId) {
|
|
61
|
+
return crypto.createHash('sha256').update(String(sessionId || '')).digest().subarray(0, 16);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function safeBigInt(value, name) {
|
|
65
|
+
try {
|
|
66
|
+
const parsed = BigInt(value || 0);
|
|
67
|
+
if (parsed < 0n || parsed > 0xffffffffffffffffn) throw new RangeError(name);
|
|
68
|
+
return parsed;
|
|
69
|
+
} catch (_) {
|
|
70
|
+
throw new TypeError(`${name || 'value'} non valido`);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function encodeFrame(input = {}) {
|
|
75
|
+
const type = typeof input.type === 'string' ? MESSAGE_TYPES[input.type] : Number(input.type);
|
|
76
|
+
if (!MESSAGE_NAMES[type]) throw new TypeError('RAC_MESSAGE_TYPE_INVALID');
|
|
77
|
+
const payload = Buffer.isBuffer(input.payload) ? input.payload : Buffer.from(String(input.payload || ''), 'utf8');
|
|
78
|
+
if (payload.length > 16 * 1024 * 1024) throw new RangeError('RAC_PAYLOAD_TOO_LARGE');
|
|
79
|
+
const header = Buffer.alloc(HEADER_BYTES);
|
|
80
|
+
MAGIC.copy(header, 0);
|
|
81
|
+
header.writeUInt8(VERSION, 4);
|
|
82
|
+
header.writeUInt8(type, 5);
|
|
83
|
+
header.writeUInt16BE(Number(input.flags || 0) & 0xffff, 6);
|
|
84
|
+
header.writeUInt32BE(Number(input.sequence || 0) >>> 0, 8);
|
|
85
|
+
header.writeBigUInt64BE(safeBigInt(input.monotonicUs, 'monotonicUs'), 12);
|
|
86
|
+
header.writeBigUInt64BE(safeBigInt(input.observationId, 'observationId'), 20);
|
|
87
|
+
header.writeUInt32BE(payload.length >>> 0, 28);
|
|
88
|
+
header.writeUInt32BE(crc32(payload), 32);
|
|
89
|
+
sessionDigest(input.sessionId).copy(header, 36);
|
|
90
|
+
return Buffer.concat([header, payload]);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function decodeFrame(value, expected = {}) {
|
|
94
|
+
const frame = Buffer.isBuffer(value) ? value : Buffer.from(value || '');
|
|
95
|
+
if (frame.length < HEADER_BYTES) return { ok: false, code: 'RAC_FRAME_TRUNCATED' };
|
|
96
|
+
if (!frame.subarray(0, 4).equals(MAGIC)) return { ok: false, code: 'RAC_FRAME_MAGIC' };
|
|
97
|
+
if (frame.readUInt8(4) !== VERSION) return { ok: false, code: 'RAC_FRAME_VERSION' };
|
|
98
|
+
const type = frame.readUInt8(5);
|
|
99
|
+
if (!MESSAGE_NAMES[type]) return { ok: false, code: 'RAC_FRAME_TYPE' };
|
|
100
|
+
const payloadLength = frame.readUInt32BE(28);
|
|
101
|
+
if (frame.length !== HEADER_BYTES + payloadLength) return { ok: false, code: 'RAC_FRAME_LENGTH' };
|
|
102
|
+
const payload = frame.subarray(HEADER_BYTES);
|
|
103
|
+
if (crc32(payload) !== frame.readUInt32BE(32)) return { ok: false, code: 'RAC_FRAME_CHECKSUM' };
|
|
104
|
+
if (expected.sessionId && !frame.subarray(36, 52).equals(sessionDigest(expected.sessionId))) {
|
|
105
|
+
return { ok: false, code: 'RAC_FRAME_SESSION' };
|
|
106
|
+
}
|
|
107
|
+
const observationId = frame.readBigUInt64BE(20).toString();
|
|
108
|
+
if (expected.observationId && String(expected.observationId) !== observationId) {
|
|
109
|
+
return { ok: false, code: 'STALE_OBSERVATION' };
|
|
110
|
+
}
|
|
111
|
+
return {
|
|
112
|
+
ok: true,
|
|
113
|
+
type: MESSAGE_NAMES[type],
|
|
114
|
+
typeId: type,
|
|
115
|
+
flags: frame.readUInt16BE(6),
|
|
116
|
+
sequence: frame.readUInt32BE(8),
|
|
117
|
+
monotonicUs: frame.readBigUInt64BE(12),
|
|
118
|
+
observationId,
|
|
119
|
+
payload,
|
|
120
|
+
checksum: frame.readUInt32BE(32),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function negotiateCapabilities(client = {}) {
|
|
125
|
+
const extension = String(client.extension || EXTENSION_ID);
|
|
126
|
+
if (extension !== EXTENSION_ID) return { ok: false, code: 'RAC_EXTENSION_UNSUPPORTED' };
|
|
127
|
+
const requestedTransports = Array.isArray(client.transport) ? client.transport : ['node-ipc'];
|
|
128
|
+
const transport = SERVER_CAPABILITIES.transport.find((item) => requestedTransports.includes(item));
|
|
129
|
+
if (!transport) return { ok: false, code: 'RAC_TRANSPORT_UNSUPPORTED' };
|
|
130
|
+
return { ok: true, ...SERVER_CAPABILITIES, transport };
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
module.exports = {
|
|
134
|
+
EXTENSION_ID, VERSION, HEADER_BYTES, MESSAGE_TYPES, SERVER_CAPABILITIES,
|
|
135
|
+
crc32, encodeFrame, decodeFrame, negotiateCapabilities,
|
|
136
|
+
};
|