linkgravity 1.7.1 → 1.7.2
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/bin/cli.js +13 -25
- package/bin/pm2.js +22 -0
- package/bin/setup.js +21 -12
- package/npm-scripts/register-hook.js +6 -4
- package/package.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -32,9 +32,12 @@ function success(msg) {
|
|
|
32
32
|
console.log(`${color.green}✔${color.reset} ${msg}`);
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
const { PM2_BIN, PM2_CWD, pm2Env } = require('./pm2');
|
|
36
|
+
|
|
37
|
+
// `npm` is npm.cmd on Windows, and node refuses to spawn a .cmd without a shell.
|
|
38
|
+
function runNpm(args, options = {}) {
|
|
39
|
+
return spawnSync('npm', args, { shell: isWin, ...options });
|
|
40
|
+
}
|
|
38
41
|
|
|
39
42
|
function info(msg) {
|
|
40
43
|
console.log(`\n${color.cyan}▶${color.reset} ${msg}`);
|
|
@@ -60,18 +63,8 @@ function runPm2(args, silent = true) {
|
|
|
60
63
|
const stdioOpt = silent ? 'pipe' : 'inherit';
|
|
61
64
|
const result = spawnSync(process.execPath, [PM2_BIN, ...args], {
|
|
62
65
|
stdio: stdioOpt,
|
|
63
|
-
cwd:
|
|
64
|
-
env:
|
|
65
|
-
...process.env,
|
|
66
|
-
// pm2 gives Python a pipe not a TTY, so it block-buffers stdout and can sit on log lines indefinitely - force line buffering.
|
|
67
|
-
PYTHONUNBUFFERED: '1',
|
|
68
|
-
// pm2 merges --update-env rather than replacing, so a LOG_LEVEL from an earlier run
|
|
69
|
-
// survives unless a value is passed every time.
|
|
70
|
-
LOG_LEVEL: process.env.LOG_LEVEL || 'INFO',
|
|
71
|
-
// Version managers (fnm, nvm) put node on PATH from a shell hook the daemon never runs,
|
|
72
|
-
// so the bot's own `node` lookup for voice-service would fail without this.
|
|
73
|
-
PATH: `${path.dirname(process.execPath)}${path.delimiter}${process.env.PATH || ''}`,
|
|
74
|
-
},
|
|
66
|
+
cwd: PM2_CWD,
|
|
67
|
+
env: pm2Env(),
|
|
75
68
|
});
|
|
76
69
|
|
|
77
70
|
if (result.error) {
|
|
@@ -148,7 +141,7 @@ function colorizeLevel(line) {
|
|
|
148
141
|
}
|
|
149
142
|
|
|
150
143
|
function runPm2LogsStream(args, printLine) {
|
|
151
|
-
const cp = spawn(process.execPath, [PM2_BIN, ...args], { cwd:
|
|
144
|
+
const cp = spawn(process.execPath, [PM2_BIN, ...args], { cwd: PM2_CWD });
|
|
152
145
|
|
|
153
146
|
const isNoise = (line) =>
|
|
154
147
|
line.trim().length === 0 ||
|
|
@@ -207,7 +200,7 @@ function verifyStartup() {
|
|
|
207
200
|
);
|
|
208
201
|
|
|
209
202
|
let cp = spawn(process.execPath, [PM2_BIN, 'logs', LGY_PM2_NAME, '--raw', '--lines', '0'], {
|
|
210
|
-
cwd:
|
|
203
|
+
cwd: PM2_CWD,
|
|
211
204
|
});
|
|
212
205
|
|
|
213
206
|
let settled = false;
|
|
@@ -359,10 +352,7 @@ function isAutostartEnabled() {
|
|
|
359
352
|
}
|
|
360
353
|
|
|
361
354
|
function checkLatestVersionFast(currentVersion) {
|
|
362
|
-
const view =
|
|
363
|
-
stdio: 'pipe',
|
|
364
|
-
timeout: 3000,
|
|
365
|
-
});
|
|
355
|
+
const view = runNpm(['view', 'linkgravity', 'version'], { stdio: 'pipe', timeout: 3000 });
|
|
366
356
|
if (view.error || view.status !== 0) return null;
|
|
367
357
|
const latest = view.stdout.toString().trim();
|
|
368
358
|
if (!latest) return null;
|
|
@@ -609,7 +599,7 @@ if (cmd === 'version' || cmd === '-v' || cmd === '--version') {
|
|
|
609
599
|
const currentVersion = pkg.version;
|
|
610
600
|
|
|
611
601
|
info('Checking npm for the latest version...');
|
|
612
|
-
const viewResult =
|
|
602
|
+
const viewResult = runNpm(['view', 'linkgravity', 'version'], { stdio: 'pipe' });
|
|
613
603
|
if (viewResult.error || viewResult.status !== 0) {
|
|
614
604
|
console.error(
|
|
615
605
|
(viewResult.stderr || '').toString().trim() ||
|
|
@@ -629,9 +619,7 @@ if (cmd === 'version' || cmd === '-v' || cmd === '--version') {
|
|
|
629
619
|
const wasOnline = !!procBeforeUpdate && procBeforeUpdate.pm2_env.status === 'online';
|
|
630
620
|
|
|
631
621
|
info(`Updating: v${currentVersion} -> v${latestVersion}...`);
|
|
632
|
-
const installResult =
|
|
633
|
-
stdio: 'inherit',
|
|
634
|
-
});
|
|
622
|
+
const installResult = runNpm(['install', '-g', 'linkgravity@latest'], { stdio: 'inherit' });
|
|
635
623
|
if (installResult.status !== 0) {
|
|
636
624
|
console.error('npm install failed - update aborted, still on the old version.');
|
|
637
625
|
process.exit(1);
|
package/bin/pm2.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
|
|
3
|
+
// Not `npx pm2`: its `npm exec` + `sh -c` wrapping survives cp.kill() as a zombie, and LPM
|
|
4
|
+
// Firewall reads it as a runtime package install.
|
|
5
|
+
const PM2_BIN = require.resolve('pm2/bin/pm2');
|
|
6
|
+
const PM2_CWD = path.join(__dirname, '..');
|
|
7
|
+
|
|
8
|
+
function pm2Env() {
|
|
9
|
+
return {
|
|
10
|
+
...process.env,
|
|
11
|
+
// pm2 gives Python a pipe not a TTY, so it block-buffers stdout and can sit on log lines indefinitely - force line buffering.
|
|
12
|
+
PYTHONUNBUFFERED: '1',
|
|
13
|
+
// pm2 merges --update-env rather than replacing, so a LOG_LEVEL from an earlier run
|
|
14
|
+
// survives unless a value is passed every time.
|
|
15
|
+
LOG_LEVEL: process.env.LOG_LEVEL || 'INFO',
|
|
16
|
+
// Version managers (fnm, nvm) put node on PATH from a shell hook the daemon never runs,
|
|
17
|
+
// so the bot's own `node` lookup for voice-service would fail without this.
|
|
18
|
+
PATH: `${path.dirname(process.execPath)}${path.delimiter}${process.env.PATH || ''}`,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
module.exports = { PM2_BIN, PM2_CWD, pm2Env };
|
package/bin/setup.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const p = require('@clack/prompts');
|
|
2
2
|
const { spawnSync } = require('child_process');
|
|
3
3
|
const { python: pythonExe } = require('../npm-scripts/venv-paths');
|
|
4
|
+
const { PM2_BIN, PM2_CWD, pm2Env } = require('./pm2');
|
|
4
5
|
const {
|
|
5
6
|
getSettings,
|
|
6
7
|
updateSettings,
|
|
@@ -178,11 +179,19 @@ async function collectUserIds(existingIds, platformLabel, required = false) {
|
|
|
178
179
|
return ids;
|
|
179
180
|
}
|
|
180
181
|
|
|
182
|
+
function runPm2(args) {
|
|
183
|
+
return spawnSync(process.execPath, [PM2_BIN, ...args], {
|
|
184
|
+
stdio: 'pipe',
|
|
185
|
+
cwd: PM2_CWD,
|
|
186
|
+
env: pm2Env(),
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
181
190
|
function stopDaemon(pm2Name, label) {
|
|
182
191
|
console.log(`${color.cyan}▶${color.reset} Stopping ${label} daemon...`);
|
|
183
|
-
|
|
192
|
+
runPm2(['delete', pm2Name]);
|
|
184
193
|
|
|
185
|
-
const jlist =
|
|
194
|
+
const jlist = runPm2(['jlist']);
|
|
186
195
|
let stillRunning = false;
|
|
187
196
|
if (jlist.status === 0) {
|
|
188
197
|
try {
|
|
@@ -194,17 +203,14 @@ function stopDaemon(pm2Name, label) {
|
|
|
194
203
|
p.outro(`${label} daemon stopped.`);
|
|
195
204
|
} else {
|
|
196
205
|
p.outro(
|
|
197
|
-
`${color.yellow}⚠${color.reset} ${label} daemon is still running - run \`
|
|
206
|
+
`${color.yellow}⚠${color.reset} ${label} daemon is still running - run \`lgy stop\` manually and check \`lgy status\`.`,
|
|
198
207
|
);
|
|
199
208
|
}
|
|
200
209
|
}
|
|
201
210
|
|
|
202
211
|
function startOrRestartDaemon(pm2Name, scriptPath, label) {
|
|
203
212
|
console.log(`${color.cyan}▶${color.reset} Restarting ${label} daemon to apply changes...`);
|
|
204
|
-
const restartResult =
|
|
205
|
-
stdio: 'pipe',
|
|
206
|
-
env: { ...process.env, PYTHONUNBUFFERED: '1' },
|
|
207
|
-
});
|
|
213
|
+
const restartResult = runPm2(['restart', pm2Name, '--update-env']);
|
|
208
214
|
|
|
209
215
|
if (restartResult.status === 0) {
|
|
210
216
|
p.outro(`${label} daemon restarted.`);
|
|
@@ -214,11 +220,14 @@ function startOrRestartDaemon(pm2Name, scriptPath, label) {
|
|
|
214
220
|
const stderr = (restartResult.stderr || '').toString();
|
|
215
221
|
if (stderr.includes('not found')) {
|
|
216
222
|
// Nothing to restart yet - start it instead of a false "restarted".
|
|
217
|
-
const startResult =
|
|
218
|
-
'
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
223
|
+
const startResult = runPm2([
|
|
224
|
+
'start',
|
|
225
|
+
scriptPath,
|
|
226
|
+
'--interpreter',
|
|
227
|
+
pythonExe,
|
|
228
|
+
'--name',
|
|
229
|
+
pm2Name,
|
|
230
|
+
]);
|
|
222
231
|
if (startResult.status === 0) {
|
|
223
232
|
p.outro(`${label} daemon wasn't running yet - started it fresh instead.`);
|
|
224
233
|
} else {
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
const fs = require('fs');
|
|
4
4
|
const path = require('path');
|
|
5
5
|
const os = require('os');
|
|
6
|
-
const { repoRoot, workspaceDir } = require('./venv-paths');
|
|
6
|
+
const { repoRoot, workspaceDir, isWin } = require('./venv-paths');
|
|
7
7
|
|
|
8
8
|
const hooksJsonPath = path.join(os.homedir(), '.gemini', 'config', 'hooks.json');
|
|
9
9
|
|
|
@@ -59,12 +59,13 @@ function loadHooksConfig() {
|
|
|
59
59
|
return { hooks: {} };
|
|
60
60
|
}
|
|
61
61
|
try {
|
|
62
|
-
|
|
62
|
+
// Windows tools write a BOM, which JSON.parse rejects.
|
|
63
|
+
return JSON.parse(fs.readFileSync(hooksJsonPath, 'utf8').replace(/^\uFEFF/, ''));
|
|
63
64
|
} catch (err) {
|
|
64
65
|
const backupPath = `${hooksJsonPath}.corrupted-${Date.now()}`;
|
|
65
66
|
fs.copyFileSync(hooksJsonPath, backupPath);
|
|
66
67
|
console.warn(
|
|
67
|
-
`⚠️ ${hooksJsonPath} was invalid JSON -
|
|
68
|
+
`⚠️ ${hooksJsonPath} was invalid JSON - copied to ${backupPath}. Any hooks it held, including other tools', are about to be replaced.`,
|
|
68
69
|
);
|
|
69
70
|
return { hooks: {} };
|
|
70
71
|
}
|
|
@@ -168,7 +169,8 @@ function registerHook({ allowFirstTimeCreate = true, quiet = false } = {}) {
|
|
|
168
169
|
|
|
169
170
|
for (const reg of HOOK_REGISTRATIONS) {
|
|
170
171
|
const scriptPath = path.join(installedHooksDir, reg.fileName);
|
|
171
|
-
|
|
172
|
+
// Windows agy takes everything after the first token as one argument; POSIX agy uses a shell.
|
|
173
|
+
const command = isWin ? `${NODE_CMD} ${scriptPath}` : `${NODE_CMD} "${scriptPath}"`;
|
|
172
174
|
const hookEntry = findHookEntry(config, reg.eventType, reg.name, reg.wrapInMatcher);
|
|
173
175
|
const isNew = !hookEntry.command;
|
|
174
176
|
|