linkgravity 1.3.0 → 1.4.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/bin/cli.js +160 -80
- package/bin/platforms.js +63 -0
- package/bin/setup.js +172 -76
- package/hooks/hook.py +3 -4
- package/package.json +1 -1
- package/requirements.txt +1 -0
- package/src/api/server.py +2 -4
- package/src/api/ui_routes.py +46 -16
- package/src/api/voice_routes.py +5 -8
- package/src/cogs/general_cog.py +4 -1
- package/src/cogs/voice_cog.py +4 -7
- package/src/config.py +9 -4
- package/src/core/agy_runner.py +3 -3
- package/src/core/session_manager.py +25 -0
- package/src/handlers/message_router.py +6 -9
- package/src/handlers/thread_reply.py +62 -34
- package/src/main.py +35 -305
- package/src/main_discord.py +311 -0
- package/src/main_telegram.py +250 -0
- package/src/messengers/base.py +46 -7
- package/src/messengers/discord_adapter.py +72 -5
- package/src/messengers/registry.py +28 -12
- package/src/messengers/telegram_adapter.py +372 -0
- package/src/services/audio_service.py +2 -2
- package/src/services/discord_helpers.py +3 -4
- package/src/services/response.py +2 -2
- package/src/services/streaming.py +12 -11
- package/src/services/discord_mcp.py +0 -50
package/bin/cli.js
CHANGED
|
@@ -2,9 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
const { spawn, spawnSync } = require('child_process');
|
|
4
4
|
const path = require('path');
|
|
5
|
+
const fs = require('fs');
|
|
6
|
+
const {
|
|
7
|
+
PLATFORMS,
|
|
8
|
+
getSettings,
|
|
9
|
+
platformState,
|
|
10
|
+
getSessions,
|
|
11
|
+
LGY_PM2_NAME,
|
|
12
|
+
LGY_SCRIPT_PATH,
|
|
13
|
+
} = require('./platforms');
|
|
5
14
|
|
|
6
|
-
// Find the absolute path to the Python bot script
|
|
7
|
-
const botPath = path.join(__dirname, '..', 'src', 'main.py');
|
|
8
15
|
const { python: pythonExe, isWin } = require('../npm-scripts/venv-paths');
|
|
9
16
|
|
|
10
17
|
const cmd = process.argv[2];
|
|
@@ -31,11 +38,7 @@ function runPm2(args, silent = true) {
|
|
|
31
38
|
const result = spawnSync('npx', ['-y', 'pm2', ...args], {
|
|
32
39
|
stdio: stdioOpt,
|
|
33
40
|
cwd: path.join(__dirname, '..'),
|
|
34
|
-
// pm2
|
|
35
|
-
// TTY, so Python defaults to block-buffering it - occasional
|
|
36
|
-
// log lines (like a single WARNING) can sit in that buffer
|
|
37
|
-
// indefinitely instead of reaching `pm2 logs`/bot.log. This
|
|
38
|
-
// forces line-by-line flushing regardless of interpreter/OS.
|
|
41
|
+
// pm2 gives Python a pipe not a TTY, so it block-buffers stdout and can sit on log lines indefinitely - force line buffering.
|
|
39
42
|
env: { ...process.env, PYTHONUNBUFFERED: '1' },
|
|
40
43
|
});
|
|
41
44
|
|
|
@@ -71,11 +74,7 @@ function runPm2(args, silent = true) {
|
|
|
71
74
|
}
|
|
72
75
|
}
|
|
73
76
|
|
|
74
|
-
// Matches a leading timestamp
|
|
75
|
-
// "2026-07-19 19:11:25 INFO ..." (loguru)
|
|
76
|
-
// "[2026-07-19 14:31:53] [INFO ] ..." (aiohttp access log)
|
|
77
|
-
// Only strips the FIRST bracket group if present, so aiohttp's second
|
|
78
|
-
// "[INFO ]" bracket (not a timestamp) is left alone.
|
|
77
|
+
// Matches a leading timestamp from either loguru or aiohttp's access-log format; only strips the first bracket group so aiohttp's second "[INFO ]" bracket is left alone.
|
|
79
78
|
const TIMESTAMP_PREFIX = /^\[?\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}\]?\s*/;
|
|
80
79
|
// loguru's colorize=True puts an ANSI code before the timestamp digits, breaking the '^' anchor above.
|
|
81
80
|
// eslint-disable-next-line no-control-regex
|
|
@@ -97,25 +96,22 @@ function colorizeLevel(line) {
|
|
|
97
96
|
});
|
|
98
97
|
}
|
|
99
98
|
|
|
100
|
-
function
|
|
99
|
+
function runPm2LogsStream(args, printLine) {
|
|
101
100
|
const cp = spawn('npx', ['-y', 'pm2', ...args], { cwd: path.join(__dirname, '..') });
|
|
102
101
|
|
|
103
|
-
const
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const clean = line.replace(ANSI_ESCAPE, '');
|
|
117
|
-
const displayLine = colorizeLevel(showStamps ? clean : clean.replace(TIMESTAMP_PREFIX, ''));
|
|
118
|
-
console.log(displayLine);
|
|
102
|
+
const isNoise = (line) =>
|
|
103
|
+
line.trim().length === 0 ||
|
|
104
|
+
line.includes('In-memory PM2') ||
|
|
105
|
+
line.includes('pm2 update') ||
|
|
106
|
+
line.includes('[TAILING]') ||
|
|
107
|
+
line.includes('.pm2/logs/lgy') ||
|
|
108
|
+
line.includes('In memory PM2 version') ||
|
|
109
|
+
line.includes('Local PM2 version') ||
|
|
110
|
+
line.match(/^>+ /);
|
|
111
|
+
|
|
112
|
+
const handleLine = (line) => {
|
|
113
|
+
if (isNoise(line)) return;
|
|
114
|
+
printLine(line);
|
|
119
115
|
};
|
|
120
116
|
|
|
121
117
|
// A line can arrive split across two 'data' events, so buffer until '\n' is seen.
|
|
@@ -125,10 +121,10 @@ function runPm2LogsClean(args, showStamps = false) {
|
|
|
125
121
|
buffer += data.toString();
|
|
126
122
|
const lines = buffer.split('\n');
|
|
127
123
|
buffer = lines.pop(); // last element: '' if buffer ended in '\n', else the incomplete tail
|
|
128
|
-
for (const line of lines)
|
|
124
|
+
for (const line of lines) handleLine(line);
|
|
129
125
|
};
|
|
130
126
|
handler.flush = () => {
|
|
131
|
-
if (buffer)
|
|
127
|
+
if (buffer) handleLine(buffer);
|
|
132
128
|
buffer = '';
|
|
133
129
|
};
|
|
134
130
|
return handler;
|
|
@@ -144,53 +140,99 @@ function runPm2LogsClean(args, showStamps = false) {
|
|
|
144
140
|
});
|
|
145
141
|
}
|
|
146
142
|
|
|
147
|
-
function
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
let cp = spawn('npx', ['-y', 'pm2', 'logs', 'lgy', '--raw', '--lines', '0'], {
|
|
153
|
-
cwd: path.join(__dirname, '..'),
|
|
143
|
+
function runPm2LogsClean(args, showStamps = false) {
|
|
144
|
+
runPm2LogsStream(args, (line) => {
|
|
145
|
+
const clean = line.replace(ANSI_ESCAPE, '');
|
|
146
|
+
console.log(colorizeLevel(showStamps ? clean : clean.replace(TIMESTAMP_PREFIX, '')));
|
|
154
147
|
});
|
|
148
|
+
}
|
|
155
149
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
150
|
+
function verifyStartup() {
|
|
151
|
+
return new Promise((resolve) => {
|
|
152
|
+
process.stdout.write(
|
|
153
|
+
`${color.cyan}▶${color.reset} Verifying startup status (waiting for bot to come online)...`,
|
|
159
154
|
);
|
|
160
|
-
cp.kill();
|
|
161
|
-
process.exit(1);
|
|
162
|
-
}, 15000);
|
|
163
155
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
156
|
+
let cp = spawn('npx', ['-y', 'pm2', 'logs', LGY_PM2_NAME, '--raw', '--lines', '20'], {
|
|
157
|
+
cwd: path.join(__dirname, '..'),
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
let settled = false;
|
|
161
|
+
const finish = (ok) => {
|
|
162
|
+
if (settled) return;
|
|
163
|
+
settled = true;
|
|
167
164
|
clearTimeout(timer);
|
|
165
|
+
cp.kill();
|
|
166
|
+
resolve(ok);
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
// Give the --lines 20 replay burst a moment to flush before treating error text as a fresh crash, not old log noise.
|
|
170
|
+
let errorDetectionArmed = false;
|
|
171
|
+
setTimeout(() => {
|
|
172
|
+
errorDetectionArmed = true;
|
|
173
|
+
}, 1500);
|
|
174
|
+
|
|
175
|
+
let timer = setTimeout(() => {
|
|
168
176
|
console.log(
|
|
169
|
-
`\n${color.
|
|
177
|
+
`\n\n${color.yellow}⏳ Startup verification timed out. Run 'lgy logs' to check status manually.${color.reset}`,
|
|
170
178
|
);
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
str.
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
.
|
|
183
|
-
(
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
179
|
+
finish(false);
|
|
180
|
+
}, 30000);
|
|
181
|
+
|
|
182
|
+
const checkLog = (data) => {
|
|
183
|
+
if (settled) return;
|
|
184
|
+
const str = data.toString();
|
|
185
|
+
if (str.includes('Bot is fully online and ready!')) {
|
|
186
|
+
console.log(`\n${color.green}✔${color.reset} Bot successfully came online!\n`);
|
|
187
|
+
finish(true);
|
|
188
|
+
} else if (
|
|
189
|
+
errorDetectionArmed &&
|
|
190
|
+
(str.includes('Traceback (most recent call last):') ||
|
|
191
|
+
str.includes('Error:') ||
|
|
192
|
+
str.includes('Exception:'))
|
|
193
|
+
) {
|
|
194
|
+
console.log(`\n\n${color.yellow}❌ Error detected during startup:${color.reset}`);
|
|
195
|
+
const errorLines = str
|
|
196
|
+
.split('\n')
|
|
197
|
+
.filter(
|
|
198
|
+
(l) =>
|
|
199
|
+
!l.includes('In-memory') &&
|
|
200
|
+
!l.includes('[TAILING]') &&
|
|
201
|
+
l.trim().length > 0,
|
|
202
|
+
);
|
|
203
|
+
console.log(errorLines.join('\n'));
|
|
204
|
+
finish(false);
|
|
205
|
+
}
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
cp.stdout.on('data', checkLog);
|
|
209
|
+
cp.stderr.on('data', checkLog);
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
function formatUptime(pmUptimeMs) {
|
|
214
|
+
const seconds = Math.floor((Date.now() - pmUptimeMs) / 1000);
|
|
215
|
+
if (seconds < 60) return `${seconds}s`;
|
|
216
|
+
const minutes = Math.floor(seconds / 60);
|
|
217
|
+
if (minutes < 60) return `${minutes}m`;
|
|
218
|
+
const hours = Math.floor(minutes / 60);
|
|
219
|
+
if (hours < 24) return `${hours}h ${minutes % 60}m`;
|
|
220
|
+
const days = Math.floor(hours / 24);
|
|
221
|
+
return `${days}d ${hours % 24}h`;
|
|
222
|
+
}
|
|
191
223
|
|
|
192
|
-
|
|
193
|
-
|
|
224
|
+
function renderTable(headers, rows) {
|
|
225
|
+
const widths = headers.map((h, i) =>
|
|
226
|
+
Math.max(h.length, ...rows.map((r) => String(r[i]).length)),
|
|
227
|
+
);
|
|
228
|
+
const pad = (s, w) => ` ${String(s).padEnd(w)} `;
|
|
229
|
+
const sepLine = (l, m, r) => l + widths.map((w) => '─'.repeat(w + 2)).join(m) + r;
|
|
230
|
+
const rowLine = (cells) => '│' + cells.map((c, i) => pad(c, widths[i])).join('│') + '│';
|
|
231
|
+
|
|
232
|
+
const lines = [sepLine('┌', '┬', '┐'), rowLine(headers), sepLine('├', '┼', '┤')];
|
|
233
|
+
for (const row of rows) lines.push(rowLine(row));
|
|
234
|
+
lines.push(sepLine('└', '┴', '┘'));
|
|
235
|
+
return lines.join('\n');
|
|
194
236
|
}
|
|
195
237
|
|
|
196
238
|
if (cmd === 'version' || cmd === '-v' || cmd === '--version') {
|
|
@@ -198,16 +240,16 @@ if (cmd === 'version' || cmd === '-v' || cmd === '--version') {
|
|
|
198
240
|
console.log(`linkgravity v${pkg.version}`);
|
|
199
241
|
} else if (cmd === 'start') {
|
|
200
242
|
info('Starting LinkGravity daemon...');
|
|
201
|
-
runPm2(['start',
|
|
202
|
-
verifyStartup();
|
|
243
|
+
runPm2(['start', LGY_SCRIPT_PATH, '--interpreter', pythonExe, '--name', LGY_PM2_NAME]);
|
|
244
|
+
verifyStartup().then((ok) => process.exit(ok ? 0 : 1));
|
|
203
245
|
} else if (cmd === 'stop') {
|
|
204
246
|
info('Stopping LinkGravity daemon...');
|
|
205
|
-
runPm2(['stop',
|
|
247
|
+
runPm2(['stop', LGY_PM2_NAME]);
|
|
206
248
|
success('Daemon stopped successfully.\n');
|
|
207
249
|
} else if (cmd === 'restart') {
|
|
208
250
|
info('Restarting LinkGravity daemon...');
|
|
209
|
-
runPm2(['restart',
|
|
210
|
-
verifyStartup();
|
|
251
|
+
runPm2(['restart', LGY_PM2_NAME, '--update-env']);
|
|
252
|
+
verifyStartup().then((ok) => process.exit(ok ? 0 : 1));
|
|
211
253
|
} else if (cmd === 'logs') {
|
|
212
254
|
const SHORT_FLAGS = ['-f', '-n', '-t'];
|
|
213
255
|
let args = process.argv.slice(3).flatMap((arg) => {
|
|
@@ -217,7 +259,7 @@ if (cmd === 'version' || cmd === '-v' || cmd === '--version') {
|
|
|
217
259
|
if (!chars.every((c) => SHORT_FLAGS.includes(`-${c}`))) return [arg];
|
|
218
260
|
return chars.map((c) => `-${c}`);
|
|
219
261
|
});
|
|
220
|
-
let pm2Args = ['logs',
|
|
262
|
+
let pm2Args = ['logs', LGY_PM2_NAME];
|
|
221
263
|
let isFollow = false;
|
|
222
264
|
let showStamps = false;
|
|
223
265
|
|
|
@@ -244,6 +286,38 @@ if (cmd === 'version' || cmd === '-v' || cmd === '--version') {
|
|
|
244
286
|
}
|
|
245
287
|
pm2Args.push('--raw');
|
|
246
288
|
runPm2LogsClean(pm2Args, showStamps);
|
|
289
|
+
} else if (cmd === 'status') {
|
|
290
|
+
const settings = getSettings();
|
|
291
|
+
const sessions = getSessions();
|
|
292
|
+
|
|
293
|
+
let pm2Procs = [];
|
|
294
|
+
const jlist = spawnSync('npx', ['-y', 'pm2', 'jlist'], { stdio: 'pipe' });
|
|
295
|
+
if (jlist.status === 0) {
|
|
296
|
+
try {
|
|
297
|
+
pm2Procs = JSON.parse(jlist.stdout.toString());
|
|
298
|
+
} catch (e) {}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
const proc = pm2Procs.find((p) => p.name === LGY_PM2_NAME);
|
|
302
|
+
if (!proc) {
|
|
303
|
+
console.log(`\n${color.cyan}▶${color.reset} daemon: not running\n`);
|
|
304
|
+
} else {
|
|
305
|
+
const mem = proc.monit ? `${Math.round(proc.monit.memory / 1024 / 1024)}mb` : '?';
|
|
306
|
+
const cpu = proc.monit ? `${proc.monit.cpu}%` : '?';
|
|
307
|
+
const uptime =
|
|
308
|
+
proc.pm2_env.status === 'online' ? formatUptime(proc.pm2_env.pm_uptime) : '-';
|
|
309
|
+
console.log(
|
|
310
|
+
`\n${color.cyan}▶${color.reset} daemon: ${proc.pm2_env.status} (uptime: ${uptime}, restarts: ${proc.pm2_env.restart_time}, cpu: ${cpu}, mem: ${mem})\n`,
|
|
311
|
+
);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const rows = Object.entries(PLATFORMS).map(([key, def]) => {
|
|
315
|
+
const { enabled } = platformState(key, settings);
|
|
316
|
+
const sessionCount = Object.values(sessions).filter((s) => s.platform === key).length;
|
|
317
|
+
return [def.label, enabled ? 'yes' : 'no', String(sessionCount)];
|
|
318
|
+
});
|
|
319
|
+
console.log(renderTable(['platform', 'enabled', 'sessions'], rows));
|
|
320
|
+
console.log();
|
|
247
321
|
} else if (cmd === 'enable') {
|
|
248
322
|
if (isWin) {
|
|
249
323
|
console.log(
|
|
@@ -304,19 +378,19 @@ if (cmd === 'version' || cmd === '-v' || cmd === '--version') {
|
|
|
304
378
|
success(`Installed v${latestVersion}.`);
|
|
305
379
|
|
|
306
380
|
info('Restarting daemon to apply the update...');
|
|
307
|
-
const restartResult = spawnSync('npx', ['-y', 'pm2', 'restart',
|
|
381
|
+
const restartResult = spawnSync('npx', ['-y', 'pm2', 'restart', LGY_PM2_NAME, '--update-env'], {
|
|
308
382
|
stdio: 'pipe',
|
|
309
383
|
cwd: path.join(__dirname, '..'),
|
|
310
384
|
env: { ...process.env, PYTHONUNBUFFERED: '1' },
|
|
311
385
|
});
|
|
312
386
|
|
|
313
387
|
if (restartResult.status === 0) {
|
|
314
|
-
verifyStartup();
|
|
388
|
+
verifyStartup().then((ok) => process.exit(ok ? 0 : 1));
|
|
315
389
|
} else if ((restartResult.stderr || '').toString().includes('not found')) {
|
|
316
390
|
// Wasn't running before the update - start fresh instead of a false "restarted".
|
|
317
391
|
info("Daemon wasn't running - starting it fresh...");
|
|
318
|
-
runPm2(['start',
|
|
319
|
-
verifyStartup();
|
|
392
|
+
runPm2(['start', LGY_SCRIPT_PATH, '--interpreter', pythonExe, '--name', LGY_PM2_NAME]);
|
|
393
|
+
verifyStartup().then((ok) => process.exit(ok ? 0 : 1));
|
|
320
394
|
} else {
|
|
321
395
|
console.error((restartResult.stderr || '').toString().trim());
|
|
322
396
|
console.error(
|
|
@@ -338,6 +412,7 @@ if (cmd === 'version' || cmd === '-v' || cmd === '--version') {
|
|
|
338
412
|
' stop Stop the background bot',
|
|
339
413
|
' restart Restart the background bot',
|
|
340
414
|
' logs View bot logs (Options: --tail, -n, -f, -t/--timestamp)',
|
|
415
|
+
' status Show daemon status and per-platform enabled/session counts',
|
|
341
416
|
' enable Register bot to start automatically on system boot',
|
|
342
417
|
' disable Remove bot from system boot',
|
|
343
418
|
' setup Run the configuration wizard (init)',
|
|
@@ -359,6 +434,11 @@ if (cmd === 'version' || cmd === '-v' || cmd === '--version') {
|
|
|
359
434
|
{ label: 'Stop', value: 'stop', hint: 'Stop the running daemon' },
|
|
360
435
|
{ label: 'Restart', value: 'restart', hint: 'Restart the running daemon' },
|
|
361
436
|
{ label: 'Logs', value: 'logs', hint: 'View the live console logs' },
|
|
437
|
+
{
|
|
438
|
+
label: 'Status',
|
|
439
|
+
value: 'status',
|
|
440
|
+
hint: 'Show daemon status and per-platform sessions',
|
|
441
|
+
},
|
|
362
442
|
{ label: 'Setup', value: 'setup', hint: 'Configure bot tokens and settings' },
|
|
363
443
|
{
|
|
364
444
|
label: 'Update',
|
package/bin/platforms.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
|
|
5
|
+
const workspaceDir = path.join(os.homedir(), '.gemini', 'linkgravity');
|
|
6
|
+
const settingsPath = path.join(workspaceDir, 'lgy.json');
|
|
7
|
+
const sessionsPath = path.join(workspaceDir, 'data', 'sessions.json');
|
|
8
|
+
|
|
9
|
+
if (!fs.existsSync(workspaceDir)) fs.mkdirSync(workspaceDir, { recursive: true });
|
|
10
|
+
|
|
11
|
+
function getSettings() {
|
|
12
|
+
if (fs.existsSync(settingsPath)) {
|
|
13
|
+
try {
|
|
14
|
+
return JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
|
|
15
|
+
} catch (e) {}
|
|
16
|
+
}
|
|
17
|
+
return {};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function updateSettings(updates) {
|
|
21
|
+
const settings = getSettings();
|
|
22
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
23
|
+
settings[key] = value;
|
|
24
|
+
}
|
|
25
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 4));
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function getSessions() {
|
|
29
|
+
if (fs.existsSync(sessionsPath)) {
|
|
30
|
+
try {
|
|
31
|
+
return JSON.parse(fs.readFileSync(sessionsPath, 'utf8'));
|
|
32
|
+
} catch (e) {}
|
|
33
|
+
}
|
|
34
|
+
return {};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Both platforms now run in one shared pm2 process - main.py checks discord_enabled/telegram_enabled at startup.
|
|
38
|
+
const LGY_PM2_NAME = 'lgy';
|
|
39
|
+
const LGY_SCRIPT_PATH = path.join(__dirname, '..', 'src', 'main.py');
|
|
40
|
+
|
|
41
|
+
const PLATFORMS = {
|
|
42
|
+
discord: { label: 'Discord' },
|
|
43
|
+
telegram: { label: 'Telegram' },
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
function platformState(key, settings) {
|
|
47
|
+
const configured = !!settings[`${key}_token`];
|
|
48
|
+
const enabled = settings[`${key}_enabled`] ?? (key === 'discord' && configured);
|
|
49
|
+
return { configured, enabled };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
module.exports = {
|
|
53
|
+
workspaceDir,
|
|
54
|
+
settingsPath,
|
|
55
|
+
sessionsPath,
|
|
56
|
+
getSettings,
|
|
57
|
+
updateSettings,
|
|
58
|
+
getSessions,
|
|
59
|
+
LGY_PM2_NAME,
|
|
60
|
+
LGY_SCRIPT_PATH,
|
|
61
|
+
PLATFORMS,
|
|
62
|
+
platformState,
|
|
63
|
+
};
|