@vidiemme/copilot-proxy 0.1.1 → 0.3.0

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 CHANGED
@@ -17,7 +17,9 @@ Il progetto viene riconosciuto dal repository git su cui si sta lavorando: nessu
17
17
  profilo da cambiare quando si passa da un progetto all'altro.
18
18
 
19
19
  Non vengono mai salvati prompt ne' risposte: solo contatori. L'identita' della
20
- persona e' uno pseudonimo, cioe' un hash con salt non invertibile.
20
+ persona e' uno pseudonimo, cioe' un hash con salt non invertibile; il setup
21
+ propone di affiancargli il nome di `git config`, per riconoscersi nella dashboard
22
+ (`--anonymous` per non farlo).
21
23
 
22
24
  ## Uso
23
25
 
@@ -27,17 +29,31 @@ npx @vidiemme/copilot-proxy@latest setup --vscode --autostart
27
29
 
28
30
  # se qualcosa non torna
29
31
  npx @vidiemme/copilot-proxy@latest doctor
32
+
33
+ # versione, eventi misurati e consegnati, spool in attesa
34
+ npx @vidiemme/copilot-proxy@latest status
30
35
  ```
31
36
 
37
+ Con l'avvio automatico, dopo un aggiornamento (`npm i -g @vidiemme/copilot-proxy@latest`)
38
+ serve `copilot-proxy restart`; per toglierlo, `copilot-proxy uninstall`.
39
+
32
40
  Non c'e' nulla da configurare a mano: endpoint di raccolta, salt e cartelle dove
33
41
  cercare i progetti sono gia' nel pacchetto. L'unica cosa da procurarsi e' il
34
42
  token di raccolta, che il comando chiede in modo nascosto.
35
43
 
36
- `--vscode` scrive le impostazioni utente di VS Code, `--autostart` registra il
44
+ `--vscode` scrive le impostazioni utente di VS Code e dei derivati installati
45
+ (Insiders, VSCodium, Cursor, Windsurf, Antigravity), `--autostart` registra il
37
46
  proxy fra i servizi dell'utente: parte subito e a ogni accesso, senza che nessuno
38
47
  debba ricordarsi di lanciarlo. Con entrambe, dopo questo comando non c'e' piu'
39
48
  nulla da fare. Il comando stampa come disattivarlo.
40
49
 
50
+ Il setup chiede la porta locale (default 8787) e controlla che sia libera. Per
51
+ cambiarla dopo: `setup --force --port 9000 --vscode --autostart`, senza
52
+ reinserire il token.
53
+
54
+ JetBrains, Eclipse, Xcode e Visual Studio non si possono indirizzare al proxy:
55
+ il loro consumo non viene misurato.
56
+
41
57
  Senza `--autostart` il proxy si avvia a mano:
42
58
 
43
59
  ```bash
@@ -8,3 +8,7 @@ export interface AutostartResult {
8
8
  export declare function autostartFile(): string | undefined;
9
9
  export declare function isAutostartInstalled(): boolean;
10
10
  export declare function installAutostart(): AutostartResult;
11
+ /** Rimuove il servizio; `false` se non era installato. */
12
+ export declare function uninstallAutostart(): boolean;
13
+ /** Riavvia il servizio, ad esempio dopo un aggiornamento del pacchetto. */
14
+ export declare function restartAutostart(): void;
package/dist/autostart.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { execFileSync } from 'node:child_process';
2
- import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
2
+ import { existsSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
3
3
  import { homedir } from 'node:os';
4
4
  import { dirname, join } from 'node:path';
5
5
  import { fileURLToPath } from 'node:url';
@@ -48,12 +48,55 @@ export function installAutostart() {
48
48
  const result = process.platform === 'darwin' ? installLaunchd(file, entry) : installSystemd(file, entry);
49
49
  // La cache di npx non e' una posizione stabile: `npm cache clean` la svuota e
50
50
  // il servizio resterebbe a puntare nel vuoto.
51
+ const warnings = [];
51
52
  if (entry.includes('/_npx/')) {
52
- result.warning =
53
- 'Il servizio punta dentro la cache di npx, che puo\' essere ripulita. Per un avvio automatico stabile: npm i -g @vidiemme/copilot-proxy, poi rilancia il setup con --autostart';
53
+ warnings.push('Il servizio punta dentro la cache di npx, che puo\' essere ripulita. Per un avvio automatico stabile: npm i -g @vidiemme/copilot-proxy, poi rilancia il setup con --autostart');
54
54
  }
55
+ if (VERSION_MANAGER_PATH.test(process.execPath)) {
56
+ warnings.push(`Il servizio usa ${process.execPath}, gestito da un version manager: se quella versione di Node viene disinstallata il proxy non riparte. Dopo un cambio di versione rilancia il setup con --autostart --force`);
57
+ }
58
+ if (warnings.length > 0)
59
+ result.warning = warnings.join('\n');
55
60
  return result;
56
61
  }
62
+ /** Node installato da nvm, fnm, asdf, mise, n o volta: il percorso cambia a ogni versione. */
63
+ const VERSION_MANAGER_PATH = /[\\/](\.nvm|\.?fnm|\.asdf|\.volta[\\/]tools|n[\\/]versions|mise[\\/]installs)[\\/]/;
64
+ function launchdTarget() {
65
+ return `gui/${process.getuid?.() ?? ''}/${LABEL}`;
66
+ }
67
+ /** Rimuove il servizio; `false` se non era installato. */
68
+ export function uninstallAutostart() {
69
+ const file = autostartFile();
70
+ if (file === undefined || !existsSync(file))
71
+ return false;
72
+ try {
73
+ if (process.platform === 'darwin') {
74
+ execFileSync('launchctl', ['bootout', launchdTarget()], { stdio: 'ignore' });
75
+ }
76
+ else {
77
+ execFileSync('systemctl', ['--user', 'disable', '--now', UNIT], { stdio: 'ignore' });
78
+ }
79
+ }
80
+ catch {
81
+ /* gia' fermo */
82
+ }
83
+ rmSync(file, { force: true });
84
+ if (process.platform === 'linux') {
85
+ execFileSync('systemctl', ['--user', 'daemon-reload'], { stdio: 'ignore' });
86
+ }
87
+ return true;
88
+ }
89
+ /** Riavvia il servizio, ad esempio dopo un aggiornamento del pacchetto. */
90
+ export function restartAutostart() {
91
+ if (!isAutostartInstalled())
92
+ throw new Error('avvio automatico non installato');
93
+ if (process.platform === 'darwin') {
94
+ execFileSync('launchctl', ['kickstart', '-k', launchdTarget()], { stdio: 'pipe' });
95
+ }
96
+ else {
97
+ execFileSync('systemctl', ['--user', 'restart', UNIT], { stdio: 'pipe' });
98
+ }
99
+ }
57
100
  function installLaunchd(file, entry) {
58
101
  const log = join(configDir(), 'proxy.log');
59
102
  const env = environmentOverrides();
@@ -1 +1 @@
1
- {"version":3,"file":"autostart.js","sourceRoot":"","sources":["../src/autostart.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,KAAK,GAAG,4BAA4B,CAAC;AAC3C,MAAM,IAAI,GAAG,uBAAuB,CAAC;AAUrC,MAAM,UAAU,aAAa;IAC3B,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAClC,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,cAAc,EAAE,GAAG,KAAK,QAAQ,CAAC,CAAC;IACtE,CAAC;IACD,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QACjC,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IAC7D,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,MAAM,IAAI,GAAG,aAAa,EAAE,CAAC;IAC7B,OAAO,IAAI,KAAK,SAAS,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC;AAChD,CAAC;AAED;;;;;GAKG;AACH,SAAS,QAAQ;IACf,OAAO,aAAa,CAAC,IAAI,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC3D,CAAC;AAED,SAAS,SAAS,CAAC,KAAa;IAC9B,OAAO,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,KAAK,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC;AACtG,CAAC;AAED,SAAS,oBAAoB;IAC3B,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,EAAE,IAAI,EAAE,CAAC;IACpD,8EAA8E;IAC9E,yEAAyE;IACzE,OAAO,IAAI,CAAC,CAAC,CAAC,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,MAAM,IAAI,GAAG,aAAa,EAAE,CAAC;IAC7B,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;QACvB,MAAM,IAAI,KAAK,CACb,mCAAmC,OAAO,CAAC,QAAQ,mDAAmD,OAAO,CAAC,QAAQ,IAAI,QAAQ,EAAE,+CAA+C,CACpL,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,QAAQ,EAAE,CAAC;IACzB,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9C,SAAS,CAAC,SAAS,EAAE,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAEzD,MAAM,MAAM,GACV,OAAO,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,cAAc,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAE5F,8EAA8E;IAC9E,8CAA8C;IAC9C,IAAI,KAAK,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,MAAM,CAAC,OAAO;YACZ,+KAA+K,CAAC;IACpL,CAAC;IACD,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,cAAc,CAAC,IAAY,EAAE,KAAa;IACjD,MAAM,GAAG,GAAG,IAAI,CAAC,SAAS,EAAE,EAAE,WAAW,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAG,oBAAoB,EAAE,CAAC;IACnC,MAAM,QAAQ,GACZ,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,MAAM,KAAK,CAAC;QAC3B,CAAC,CAAC,EAAE;QACJ,CAAC,CAAC,gDAAgD,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC;aAChE,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,YAAY,SAAS,CAAC,GAAG,CAAC,iBAAiB,SAAS,CAAC,KAAK,CAAC,WAAW,CAAC;aAC7F,IAAI,CAAC,IAAI,CAAC,eAAe,CAAC;IAEnC,MAAM,KAAK,GAAG;;;;4BAIY,KAAK;;;cAGnB,SAAS,CAAC,OAAO,CAAC,QAAQ,CAAC;cAC3B,SAAS,CAAC,KAAK,CAAC;;;;;EAK5B,QAAQ,uCAAuC,SAAS,CAAC,GAAG,CAAC;wCACvB,SAAS,CAAC,GAAG,CAAC;;;CAGrD,CAAC;IACA,aAAa,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IAE3B,MAAM,MAAM,GAAG,OAAO,OAAO,CAAC,MAAM,EAAE,EAAE,IAAI,EAAE,EAAE,CAAC;IACjD,2EAA2E;IAC3E,oCAAoC;IACpC,IAAI,CAAC;QACH,YAAY,CAAC,WAAW,EAAE,CAAC,SAAS,EAAE,GAAG,MAAM,IAAI,KAAK,EAAE,CAAC,EAAE,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;IACpF,CAAC;IAAC,MAAM,CAAC;QACP,sBAAsB;IACxB,CAAC;IACD,YAAY,CAAC,WAAW,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IAE1E,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,qBAAqB,MAAM,IAAI,KAAK,UAAU,IAAI,EAAE,EAAE,CAAC;AACpF,CAAC;AAED,SAAS,cAAc,CAAC,IAAY,EAAE,KAAa;IACjD,MAAM,GAAG,GAAG,oBAAoB,EAAE,CAAC;IACnC,MAAM,QAAQ,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC;SACjC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,eAAe,GAAG,IAAI,KAAK,EAAE,CAAC;SACpD,IAAI,CAAC,IAAI,CAAC,CAAC;IAEd,MAAM,IAAI,GAAG;;;;;YAKH,OAAO,CAAC,QAAQ,IAAI,KAAK;;;EAGnC,QAAQ;;;;CAIT,CAAC;IACA,aAAa,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IAE1B,YAAY,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,eAAe,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IAC1E,YAAY,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC,CAAC;IAElF,OAAO,EAAE,IAAI,EAAE,UAAU,EAAE,kCAAkC,IAAI,UAAU,IAAI,EAAE,EAAE,CAAC;AACtF,CAAC"}
1
+ {"version":3,"file":"autostart.js","sourceRoot":"","sources":["../src/autostart.ts"],"names":[],"mappings":"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"}
package/dist/cli.js CHANGED
@@ -1,30 +1,44 @@
1
1
  #!/usr/bin/env node
2
- import { mkdirSync, readFileSync, writeFileSync, copyFileSync, statSync } from 'node:fs';
3
- import { dirname } from 'node:path';
2
+ import { mkdirSync, readFileSync, writeFileSync, statSync } from 'node:fs';
3
+ import { createInterface } from 'node:readline/promises';
4
4
  import { parseArgs } from 'node:util';
5
- import { installAutostart, isAutostartInstalled } from './autostart.js';
5
+ import { installAutostart, isAutostartInstalled, restartAutostart, uninstallAutostart } from './autostart.js';
6
6
  import { loadConfig } from './config.js';
7
- import { configDir, configFile, vscodeSettingsFile } from './paths.js';
7
+ import { applyEditorSettings, editorSnippet, installedEditors, knownEditors, pointsToProxy, readOverride, } from './editors.js';
8
+ import { configDir, configFile } from './paths.js';
8
9
  import { buildApp } from './server.js';
9
- const OVERRIDE_KEY = 'github.copilot.advanced.debug.overrideCapiUrl';
10
- const AUTH_TYPE_KEY = 'github.copilot.advanced.debug.overrideAuthType';
10
+ import { gitIdentity, portStatus } from './setupHelpers.js';
11
+ import { CLIENT_VERSION } from './version.js';
12
+ const DEFAULT_PORT = 8787;
11
13
  const USAGE = `
12
14
  Proxy locale che misura il consumo di token di GitHub Copilot e lo attribuisce
13
15
  al progetto su cui si sta lavorando.
14
16
 
15
- npx @vidiemme/copilot-proxy setup configura la postazione (una volta sola)
16
- npx @vidiemme/copilot-proxy start avvia il proxy
17
- npx @vidiemme/copilot-proxy doctor verifica che tutto sia a posto
17
+ npx @vidiemme/copilot-proxy setup configura la postazione (una volta sola)
18
+ npx @vidiemme/copilot-proxy start avvia il proxy
19
+ npx @vidiemme/copilot-proxy doctor verifica che tutto sia a posto
20
+ npx @vidiemme/copilot-proxy status versione, traffico misurato, eventi in attesa
21
+ npx @vidiemme/copilot-proxy restart riavvia il servizio (dopo un aggiornamento)
22
+ npx @vidiemme/copilot-proxy uninstall rimuove l'avvio automatico
23
+ npx @vidiemme/copilot-proxy --version
18
24
 
19
25
  Opzioni di setup:
20
- --vscode scrive anche le impostazioni utente di VS Code
26
+ --vscode scrive le impostazioni di VS Code e derivati (Insiders,
27
+ VSCodium, Cursor, Windsurf, Antigravity) installati
21
28
  --autostart avvia il proxy a ogni accesso, senza doverlo lanciare
29
+ --port <n> porta locale del proxy (default 8787; chiesta se interattivo)
30
+ --name <nome> nome da mostrare nella dashboard (default: git user.name)
31
+ --email <email> email associata (default: git user.email)
32
+ --anonymous non inviare nome ed email: resta solo lo pseudonimo
22
33
  --workspace-roots <a,b> cartelle sotto cui vivono i repository (default: la home)
23
- --port <n> porta locale del proxy (default 8787)
24
34
  --collector-url <url> endpoint di raccolta, se diverso da quello aziendale
25
35
  --salt <valore> salt degli pseudonimi, se diverso da quello aziendale
26
36
  --upstream <url> endpoint Copilot (default https://api.githubcopilot.com)
27
- --force sovrascrive una configurazione esistente
37
+ --force rifa' il setup partendo dalla configurazione esistente
38
+ (token compreso: per cambiare porta basta --force --port)
39
+
40
+ JetBrains, Eclipse, Xcode, Android Studio e Visual Studio non hanno un modo per
41
+ indirizzare Copilot al proxy: il loro traffico non viene misurato.
28
42
 
29
43
  L'unica cosa da procurarsi e' il token di raccolta, uguale per tutta l'azienda.
30
44
  Non si passa come opzione: finirebbe nella cronologia della shell e nella lista
@@ -117,21 +131,49 @@ function promptSecret(question) {
117
131
  stdin.on('data', onData);
118
132
  });
119
133
  }
134
+ async function promptLine(question) {
135
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
136
+ try {
137
+ return (await rl.question(question)).trim();
138
+ }
139
+ finally {
140
+ rl.close();
141
+ }
142
+ }
143
+ function validPort(value) {
144
+ const port = Number(value);
145
+ return Number.isInteger(port) && port >= 1 && port <= 65535;
146
+ }
147
+ function setOrDelete(values, key, value) {
148
+ if (value)
149
+ values[key] = value;
150
+ else
151
+ delete values[key];
152
+ }
120
153
  async function commandSetup(flags) {
121
154
  const existing = readConfigFile();
122
- if (Object.keys(existing).length > 0 && flags.force !== true) {
123
- console.error(`Esiste gia' ${configFile()}. Rilancia con --force per sostituirlo.`);
155
+ const reconfigure = Object.keys(existing).length > 0;
156
+ if (reconfigure && flags.force !== true) {
157
+ console.error(`Esiste gia' ${configFile()}. Rilancia con --force per modificarlo.`);
124
158
  return 1;
125
159
  }
126
- const token = process.env.COLLECTOR_TOKEN ?? (await promptSecret('Token di raccolta: '));
127
- if (token.trim().length === 0) {
160
+ const interactive = process.stdin.isTTY === true;
161
+ // Con --force si parte dalla configurazione esistente: cambiare porta non deve costare il token.
162
+ const values = {};
163
+ for (const [key, value] of Object.entries(existing))
164
+ if (value !== undefined)
165
+ values[key] = value;
166
+ const token = process.env.COLLECTOR_TOKEN?.trim() ||
167
+ values.COLLECTOR_TOKEN ||
168
+ (await promptSecret('Token di raccolta: ')).trim();
169
+ if (token.length === 0) {
128
170
  console.error('Token vuoto.');
129
171
  return 1;
130
172
  }
131
173
  // Si scrive solo cio' che si discosta dai default del pacchetto: un file
132
174
  // corto e' un file che si capisce, e i default restano aggiornabili con una
133
175
  // nuova versione invece che postazione per postazione.
134
- const values = { COLLECTOR_TOKEN: token.trim() };
176
+ values.COLLECTOR_TOKEN = token;
135
177
  const collectorUrl = flags['collector-url'];
136
178
  if (collectorUrl)
137
179
  values.COLLECTOR_URL = collectorUrl;
@@ -141,10 +183,35 @@ async function commandSetup(flags) {
141
183
  const workspaceRoots = flags['workspace-roots'];
142
184
  if (workspaceRoots)
143
185
  values.WORKSPACE_ROOTS = workspaceRoots;
144
- if (flags.port)
145
- values.PORT = String(flags.port);
146
186
  if (flags.upstream)
147
187
  values.UPSTREAM_BASE_URL = String(flags.upstream);
188
+ let port = flags.port ? String(flags.port) : values.PORT;
189
+ if (!port && interactive) {
190
+ port = (await promptLine(`Porta locale del proxy [${DEFAULT_PORT}]: `)) || undefined;
191
+ }
192
+ if (port && !validPort(port)) {
193
+ console.error(`Porta non valida: ${port}. Serve un intero fra 1 e 65535.`);
194
+ return 1;
195
+ }
196
+ setOrDelete(values, 'PORT', port && Number(port) !== DEFAULT_PORT ? String(Number(port)) : undefined);
197
+ if (flags.anonymous === true) {
198
+ setOrDelete(values, 'DEVELOPER_NAME', undefined);
199
+ setOrDelete(values, 'DEVELOPER_EMAIL', undefined);
200
+ }
201
+ else {
202
+ const git = gitIdentity();
203
+ let name = flags.name ?? values.DEVELOPER_NAME ?? git.name;
204
+ const email = flags.email ?? values.DEVELOPER_EMAIL ?? git.email;
205
+ if (interactive && flags.name === undefined) {
206
+ const answer = await promptLine(`Nome da mostrare nella dashboard [${name ?? 'nessuno'}] ("-" per restare anonimo): `);
207
+ if (answer === '-')
208
+ name = undefined;
209
+ else if (answer)
210
+ name = answer;
211
+ }
212
+ setOrDelete(values, 'DEVELOPER_NAME', name?.slice(0, 100));
213
+ setOrDelete(values, 'DEVELOPER_EMAIL', name ? email?.slice(0, 200) : undefined);
214
+ }
148
215
  // Meglio scoprire ora che la configurazione non regge, non al primo avvio.
149
216
  let config;
150
217
  try {
@@ -154,14 +221,22 @@ async function commandSetup(flags) {
154
221
  console.error(`Configurazione non valida: ${error.message}`);
155
222
  return 1;
156
223
  }
224
+ if ((await portStatus(config.port, config.host)) === 'busy') {
225
+ console.error(`La porta ${config.port} e' occupata da un altro programma. Scegline un'altra con --port <n>.`);
226
+ return 1;
227
+ }
157
228
  writeConfigFile(values);
158
229
  console.log(`Scritto ${configFile()}`);
230
+ console.log(`Proxy su http://127.0.0.1:${config.port}`);
159
231
  console.log(`Raccolta verso ${config.collectorUrl}`);
160
232
  console.log(`Progetti cercati sotto ${config.workspaceRoots.join(', ')}`);
233
+ console.log(config.developerName
234
+ ? `Nella dashboard comparirai come ${config.developerName}`
235
+ : 'Nella dashboard comparirai con uno pseudonimo');
161
236
  if (flags.vscode === true)
162
- applyVsCodeSettings(config.port);
237
+ applyEditors(config.port);
163
238
  else
164
- printVsCodeSettings(config.port);
239
+ printEditorSettings(config.port);
165
240
  if (flags.autostart === true) {
166
241
  try {
167
242
  const service = installAutostart();
@@ -180,37 +255,25 @@ async function commandSetup(flags) {
180
255
  console.log('Per non doverlo fare ogni volta, rilancia il setup con --autostart');
181
256
  return 0;
182
257
  }
183
- function vsCodeSnippet(port) {
184
- return { [OVERRIDE_KEY]: `http://127.0.0.1:${port}`, [AUTH_TYPE_KEY]: 'token' };
185
- }
186
- function printVsCodeSettings(port) {
187
- console.log('\nAggiungi alle impostazioni utente di VS Code (Preferences: Open User Settings (JSON)):\n' +
188
- `${JSON.stringify(vsCodeSnippet(port), null, 2)}\n` +
189
- 'Oppure rilancia il setup con --vscode per farlo scrivere al comando.');
258
+ function printEditorSettings(port, editor) {
259
+ console.log(`\nAggiungi alle impostazioni utente di ${editor?.name ?? 'VS Code (e dei suoi derivati)'} (Preferences: Open User Settings (JSON)):\n` +
260
+ `${JSON.stringify(editorSnippet(port), null, 2)}\n` +
261
+ (editor ? '' : 'Oppure rilancia il setup con --vscode per farlo scrivere al comando.'));
190
262
  }
191
- function applyVsCodeSettings(port) {
192
- const file = vscodeSettingsFile();
193
- let current = {};
194
- let exists = true;
195
- try {
196
- const raw = readFileSync(file, 'utf-8');
197
- // `settings.json` ammette commenti e virgole finali: se non e' JSON puro,
198
- // riscriverlo lo distruggerebbe. Meglio arrendersi e dire cosa aggiungere.
199
- current = JSON.parse(raw);
200
- copyFileSync(file, `${file}.bak`);
201
- }
202
- catch (error) {
203
- exists = error.code !== 'ENOENT';
204
- if (exists) {
205
- console.log(`\nNon riesco a modificare ${file} senza rischiare di rovinarlo (contiene commenti o non e' JSON valido).`);
206
- printVsCodeSettings(port);
207
- return;
263
+ function applyEditors(port) {
264
+ const found = installedEditors();
265
+ // Nessun editor ancora aperto: si prepara VS Code, che e' il caso piu' comune.
266
+ const targets = found.length > 0 ? found : knownEditors().slice(0, 1);
267
+ for (const editor of targets) {
268
+ const result = applyEditorSettings(editor, port);
269
+ if (result.status === 'unparsable') {
270
+ console.log(`\n${editor.name}: non riesco a modificare ${editor.settingsFile} senza rischiare di rovinarlo (contiene commenti o non e' JSON valido).`);
271
+ printEditorSettings(port, editor);
272
+ continue;
208
273
  }
274
+ console.log(`${editor.name}: aggiornato ${editor.settingsFile}${result.backup ? ` (backup in ${result.backup})` : ''}`);
209
275
  }
210
- mkdirSync(dirname(file), { recursive: true });
211
- writeFileSync(file, `${JSON.stringify({ ...current, ...vsCodeSnippet(port) }, null, 2)}\n`);
212
- console.log(`Aggiornato ${file}${exists ? ` (backup in ${file}.bak)` : ''}`);
213
- console.log("Riavvia VS Code perche' le impostazioni abbiano effetto.");
276
+ console.log("Riavvia gli editor perche' le impostazioni abbiano effetto.");
214
277
  }
215
278
  async function commandStart(flags) {
216
279
  const overrides = {};
@@ -246,7 +309,10 @@ async function commandStart(flags) {
246
309
  async function commandDoctor() {
247
310
  const results = [];
248
311
  const check = (ok, message) => {
249
- results.push([ok, message]);
312
+ results.push([ok ? 'ok' : 'fail', message]);
313
+ };
314
+ const warn = (message) => {
315
+ results.push(['warn', message]);
250
316
  };
251
317
  let config;
252
318
  try {
@@ -291,46 +357,127 @@ async function commandDoctor() {
291
357
  catch (error) {
292
358
  check(false, `servizio di raccolta irraggiungibile: ${error.message}`);
293
359
  }
294
- // Ricerca testuale e non `JSON.parse`: `settings.json` ammette commenti e
295
- // virgole finali, e una diagnostica non deve fallire per quello.
296
- try {
297
- const raw = readFileSync(vscodeSettingsFile(), 'utf-8');
298
- const match = new RegExp(`"${OVERRIDE_KEY.replace(/\./g, '\\.')}"\\s*:\\s*"([^"]*)"`).exec(raw);
299
- const expected = `http://127.0.0.1:${config.port}`;
300
- // `localhost` e `127.0.0.1` sono equivalenti: non vale la pena segnalarlo.
301
- const ok = match !== null && new RegExp(`^https?://(127\\.0\\.0\\.1|localhost):${config.port}/?$`).test(match[1] ?? '');
302
- check(ok, ok
303
- ? `VS Code punta al proxy (${match?.[1] ?? expected})`
304
- : match
305
- ? `VS Code punta a ${match[1]}, atteso ${expected}`
306
- : "VS Code non e' configurato per passare dal proxy");
360
+ // Basta un editor che punti al proxy; gli altri sono solo segnalati, perche'
361
+ // non tutti usano Copilot in ogni editor installato.
362
+ const editors = installedEditors();
363
+ const expected = `http://127.0.0.1:${config.port}`;
364
+ const configured = [];
365
+ const notConfigured = [];
366
+ for (const editor of editors) {
367
+ const value = readOverride(editor);
368
+ if (value && pointsToProxy(value, config.port))
369
+ configured.push(editor.name);
370
+ else
371
+ notConfigured.push(value ? `${editor.name} (punta a ${value}, atteso ${expected})` : editor.name);
307
372
  }
308
- catch {
309
- check(false, `impostazioni di VS Code non trovate in ${vscodeSettingsFile()}`);
373
+ if (configured.length > 0) {
374
+ check(true, `passano dal proxy: ${configured.join(', ')}`);
375
+ if (notConfigured.length > 0)
376
+ warn(`non passano dal proxy: ${notConfigured.join(', ')}`);
377
+ }
378
+ else {
379
+ check(false, editors.length > 0
380
+ ? `nessun editor passa dal proxy (${notConfigured.join(', ')}): rilancia "setup --vscode --force"`
381
+ : 'nessun editor della famiglia VS Code trovato su questa macchina');
310
382
  }
311
383
  const pending = spoolSize(config.spoolPath);
312
384
  check(pending === 0, pending === 0 ? 'nessun evento in attesa di consegna' : `${pending} byte di eventi in attesa nello spool`);
313
385
  // L'avvio automatico interessa solo se il proxy non sta gia' girando: chi
314
386
  // preferisce lanciarlo a mano non deve vedere un errore.
315
- let proxyUp = false;
387
+ const health = await readHealth(config.port);
388
+ const proxyUp = health !== undefined;
389
+ check(proxyUp, proxyUp
390
+ ? `proxy ${health.version ?? ''} in ascolto${isAutostartInstalled() ? ', con avvio automatico' : ''}`
391
+ : `nessun proxy in ascolto sulla porta ${config.port}: attivalo per sempre con "setup --autostart --force", o una volta sola con "start"`);
392
+ if (health) {
393
+ if (health.version && health.version !== CLIENT_VERSION) {
394
+ warn(`il proxy in esecuzione e' la ${health.version}, questo comando la ${CLIENT_VERSION}: lancia "restart"`);
395
+ }
396
+ // L'impostazione di VS Code e' di debug: un aggiornamento dell'estensione
397
+ // puo' ignorarla senza avvisare, e lo si vede solo dall'assenza di traffico.
398
+ const silence = trafficSilence(health);
399
+ if (silence)
400
+ warn(silence);
401
+ }
402
+ report(results);
403
+ return results.every(([level]) => level !== 'fail') ? 0 : 1;
404
+ }
405
+ async function readHealth(port) {
316
406
  try {
317
- const health = await fetch(`http://127.0.0.1:${config.port}/_health`, {
407
+ const response = await fetch(`http://127.0.0.1:${port}/_health`, {
318
408
  signal: AbortSignal.timeout(2000),
319
409
  });
320
- proxyUp = health.ok;
410
+ return response.ok ? (await response.json()) : undefined;
321
411
  }
322
412
  catch {
323
- proxyUp = false;
413
+ return undefined;
324
414
  }
325
- check(proxyUp, proxyUp
326
- ? `proxy in ascolto${isAutostartInstalled() ? ', con avvio automatico' : ''}`
327
- : `nessun proxy in ascolto sulla porta ${config.port}: attivalo per sempre con "setup --autostart --force", o una volta sola con "start"`);
328
- report(results);
329
- return results.every(([ok]) => ok) ? 0 : 1;
415
+ }
416
+ const HOUR_MS = 60 * 60 * 1000;
417
+ function trafficSilence(health, now = Date.now()) {
418
+ const last = health.usage?.lastEventAt;
419
+ if (last) {
420
+ const hours = Math.floor((now - Date.parse(last)) / HOUR_MS);
421
+ return hours >= 24 ? `nessun traffico Copilot misurato nelle ultime ${hours} ore` : undefined;
422
+ }
423
+ const uptime = health.startedAt ? now - Date.parse(health.startedAt) : 0;
424
+ return uptime >= HOUR_MS
425
+ ? "nessun traffico Copilot misurato dall'avvio del proxy: controlla che l'editor passi davvero di qui"
426
+ : undefined;
427
+ }
428
+ async function commandStatus() {
429
+ let config;
430
+ try {
431
+ config = loadConfig(mergedSource({}));
432
+ }
433
+ catch (error) {
434
+ console.error(error.message);
435
+ return 1;
436
+ }
437
+ const health = await readHealth(config.port);
438
+ console.log(`copilot-proxy ${CLIENT_VERSION}`);
439
+ console.log(`nome in dashboard: ${config.developerName ?? 'nessuno (pseudonimo)'}`);
440
+ console.log(`avvio automatico: ${isAutostartInstalled() ? 'attivo' : 'non installato'}`);
441
+ if (!health) {
442
+ console.log(`proxy: non in ascolto sulla porta ${config.port}`);
443
+ return 1;
444
+ }
445
+ const usage = health.usage;
446
+ console.log(`proxy: ${health.version ?? '?'} in ascolto dal ${health.startedAt ?? '?'}`);
447
+ if (usage) {
448
+ console.log(`eventi misurati: ${usage.recorded}, consegnati: ${usage.delivered}, scartati: ${usage.dropped}`);
449
+ console.log(`ultimo evento: ${usage.lastEventAt ?? 'nessuno'}`);
450
+ console.log(`ultima consegna: ${usage.lastDeliveryAt ?? 'nessuna'}`);
451
+ if (usage.lastError)
452
+ console.log(`ultimo errore del collector: ${usage.lastError}`);
453
+ }
454
+ console.log(`spool: ${health.spoolBytes ?? 0} byte in attesa`);
455
+ return 0;
456
+ }
457
+ function commandRestart() {
458
+ try {
459
+ restartAutostart();
460
+ }
461
+ catch (error) {
462
+ console.error(`Riavvio non riuscito: ${error.message}`);
463
+ return 1;
464
+ }
465
+ console.log('Proxy riavviato.');
466
+ return 0;
467
+ }
468
+ function commandUninstall() {
469
+ if (!uninstallAutostart()) {
470
+ console.log('Avvio automatico non installato: niente da rimuovere.');
471
+ return 0;
472
+ }
473
+ console.log(`Avvio automatico rimosso. Configurazione lasciata in ${configFile()}.`);
474
+ console.log("Ricordati di togliere le impostazioni del proxy da VS Code, altrimenti Copilot non raggiunge piu' l'upstream.");
475
+ return 0;
330
476
  }
331
477
  function report(results) {
332
- for (const [ok, message] of results)
333
- console.log(`${ok ? 'OK ' : 'FAIL'} ${message}`);
478
+ const labels = { ok: 'OK ', warn: 'WARN', fail: 'FAIL' };
479
+ for (const [level, message] of results)
480
+ console.log(`${labels[level]} ${message}`);
334
481
  }
335
482
  function existsDir(path) {
336
483
  try {
@@ -359,13 +506,21 @@ async function main() {
359
506
  port: { type: 'string' },
360
507
  upstream: { type: 'string' },
361
508
  'log-level': { type: 'string' },
509
+ name: { type: 'string' },
510
+ email: { type: 'string' },
511
+ anonymous: { type: 'boolean' },
362
512
  vscode: { type: 'boolean' },
363
513
  autostart: { type: 'boolean' },
364
514
  force: { type: 'boolean' },
365
515
  help: { type: 'boolean', short: 'h' },
516
+ version: { type: 'boolean', short: 'v' },
366
517
  },
367
518
  });
368
519
  const command = positionals[0] ?? 'start';
520
+ if (values.version === true || command === 'version') {
521
+ console.log(CLIENT_VERSION);
522
+ return;
523
+ }
369
524
  if (values.help === true || command === 'help') {
370
525
  console.log(USAGE);
371
526
  return;
@@ -380,6 +535,15 @@ async function main() {
380
535
  case 'doctor':
381
536
  process.exitCode = await commandDoctor();
382
537
  return;
538
+ case 'status':
539
+ process.exitCode = await commandStatus();
540
+ return;
541
+ case 'restart':
542
+ process.exitCode = commandRestart();
543
+ return;
544
+ case 'uninstall':
545
+ process.exitCode = commandUninstall();
546
+ return;
383
547
  default:
384
548
  console.error(`Comando sconosciuto: ${command}\n`);
385
549
  console.log(USAGE);