linkgravity 1.0.1 → 1.1.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/bin/cli.js CHANGED
@@ -222,6 +222,58 @@ if (cmd === 'start') {
222
222
  runSetup().catch((err) => {
223
223
  console.error('Setup wizard crashed:', err.message);
224
224
  });
225
+ } else if (cmd === 'update') {
226
+ const pkg = require('../package.json');
227
+ const currentVersion = pkg.version;
228
+
229
+ info('Checking npm for the latest version...');
230
+ const viewResult = spawnSync('npm', ['view', 'linkgravity', 'version'], { stdio: 'pipe' });
231
+ if (viewResult.error || viewResult.status !== 0) {
232
+ console.error(
233
+ (viewResult.stderr || '').toString().trim() ||
234
+ 'Failed to check the latest version on npm.',
235
+ );
236
+ process.exit(1);
237
+ }
238
+ const latestVersion = viewResult.stdout.toString().trim();
239
+
240
+ if (latestVersion === currentVersion) {
241
+ success(`Already up to date (v${currentVersion}).\n`);
242
+ process.exit(0);
243
+ }
244
+
245
+ info(`Updating: v${currentVersion} -> v${latestVersion}...`);
246
+ const installResult = spawnSync('npm', ['install', '-g', 'linkgravity@latest'], {
247
+ stdio: 'inherit',
248
+ });
249
+ if (installResult.status !== 0) {
250
+ console.error('npm install failed - update aborted, still on the old version.');
251
+ process.exit(1);
252
+ }
253
+ success(`Installed v${latestVersion}.`);
254
+
255
+ info('Restarting daemon to apply the update...');
256
+ const restartResult = spawnSync('npx', ['-y', 'pm2', 'restart', 'lgy', '--update-env'], {
257
+ stdio: 'pipe',
258
+ cwd: path.join(__dirname, '..'),
259
+ env: { ...process.env, PYTHONUNBUFFERED: '1' },
260
+ });
261
+
262
+ if (restartResult.status === 0) {
263
+ verifyStartup();
264
+ } else if ((restartResult.stderr || '').toString().includes('not found')) {
265
+ // Daemon wasn't running before the update - start it fresh instead
266
+ // of reporting a restart that never had anything to restart.
267
+ info("Daemon wasn't running - starting it fresh...");
268
+ runPm2(['start', botPath, '--interpreter', pythonExe, '--name', 'lgy']);
269
+ verifyStartup();
270
+ } else {
271
+ console.error((restartResult.stderr || '').toString().trim());
272
+ console.error(
273
+ `\n${color.yellow}⚠${color.reset} Update installed, but restarting the daemon failed - run 'lgy restart' manually.`,
274
+ );
275
+ process.exit(1);
276
+ }
225
277
  } else if (cmd === 'help') {
226
278
  console.log(
227
279
  [
@@ -238,6 +290,7 @@ if (cmd === 'start') {
238
290
  ' enable Register bot to start automatically on system boot',
239
291
  ' disable Remove bot from system boot',
240
292
  ' setup Run the configuration wizard (init)',
293
+ ' update Check npm for a newer version and install + restart if found',
241
294
  ' help Show this help message',
242
295
  '',
243
296
  ].join('\n'),
@@ -256,6 +309,11 @@ if (cmd === 'start') {
256
309
  { label: 'Restart', value: 'restart', hint: 'Restart the running daemon' },
257
310
  { label: 'Logs', value: 'logs', hint: 'View the live console logs' },
258
311
  { label: 'Setup', value: 'setup', hint: 'Configure bot tokens and settings' },
312
+ {
313
+ label: 'Update',
314
+ value: 'update',
315
+ hint: 'Check npm for a newer version and install it',
316
+ },
259
317
  {
260
318
  label: 'Enable Auto-start',
261
319
  value: 'enable',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linkgravity",
3
- "version": "1.0.1",
3
+ "version": "1.1.0",
4
4
  "description": "Discord bot bridge for the Antigravity (agy) CLI, with voice interaction support",
5
5
  "scripts": {
6
6
  "postinstall": "node npm-scripts/postinstall.js",
@@ -6,7 +6,7 @@ import discord
6
6
  from discord import app_commands
7
7
  from discord.ext import commands, tasks
8
8
 
9
- from config import logger
9
+ from config import allowed, logger
10
10
 
11
11
  from .voice.enrollment import EnrollmentManager
12
12
  from .voice.stt_session import SttSessionTracker
@@ -152,6 +152,10 @@ class VoiceCog(commands.Cog):
152
152
 
153
153
  @app_commands.command(name="join", description="Summon the bot to your current voice channel")
154
154
  async def cmd_join(self, interaction: discord.Interaction):
155
+ if not allowed(interaction.user.id):
156
+ await interaction.response.send_message("❌ Permission Denied", ephemeral=True)
157
+ return
158
+
155
159
  if not isinstance(interaction.channel, discord.Thread):
156
160
  await interaction.response.send_message(
157
161
  "❌ This command can only be used inside a thread created with `/new`.", ephemeral=True
@@ -260,6 +264,10 @@ class VoiceCog(commands.Cog):
260
264
  ):
261
265
  import aiohttp
262
266
 
267
+ if not allowed(interaction.user.id):
268
+ await interaction.response.send_message("❌ Permission Denied", ephemeral=True)
269
+ return
270
+
263
271
  if (
264
272
  wake_word is None
265
273
  and active_times is None
@@ -330,6 +338,10 @@ class VoiceCog(commands.Cog):
330
338
 
331
339
  @app_commands.command(name="leave", description="Make the bot leave the voice channel")
332
340
  async def cmd_leave(self, interaction: discord.Interaction):
341
+ if not allowed(interaction.user.id):
342
+ await interaction.response.send_message("❌ Permission Denied", ephemeral=True)
343
+ return
344
+
333
345
  guild_id = interaction.guild_id
334
346
  await interaction.response.send_message("👋 Disconnected from voice channel.")
335
347
  try:
@@ -365,6 +377,12 @@ class VoiceCog(commands.Cog):
365
377
  try:
366
378
  guild_id = data.get("guild_id")
367
379
  user_id = data.get("user_id")
380
+
381
+ if not allowed(int(user_id)):
382
+ self.logger.debug(f"STT: ignoring speech from non-allowed user {user_id}")
383
+ await self.stt_session.clear_partial_msg(str(guild_id))
384
+ return
385
+
368
386
  # Node runs STT itself before calling this endpoint, so this is
369
387
  # already-recognized text, not raw audio - keeps STT off every VAD hit.
370
388
  text = data.get("text")