linkgravity 1.5.3 → 1.5.4

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
@@ -256,10 +256,27 @@ function findAgyBin() {
256
256
  function getPm2Proc() {
257
257
  const jlist = spawnSync('npx', ['-y', 'pm2', 'jlist'], { stdio: 'pipe' });
258
258
  if (jlist.status !== 0) return null;
259
+
260
+ // pm2 sometimes prints a version-mismatch banner ("In-memory PM2 is
261
+ // out-of-date...") before the JSON when the CLI version differs from
262
+ // the already-running daemon's - skip past it instead of choking on it.
263
+ const out = jlist.stdout.toString();
264
+ const jsonStart = out.indexOf('[');
265
+ if (jsonStart === -1) {
266
+ console.error(
267
+ `${color.yellow}⚠${color.reset} Couldn't read pm2 status (unexpected output, no JSON found). Raw output:\n${out.trim()}`,
268
+ );
269
+ return null;
270
+ }
271
+
259
272
  try {
260
- const procs = JSON.parse(jlist.stdout.toString());
273
+ const procs = JSON.parse(out.slice(jsonStart));
261
274
  return procs.find((p) => p.name === LGY_PM2_NAME) || null;
262
275
  } catch (e) {
276
+ console.error(
277
+ `${color.yellow}⚠${color.reset} Couldn't parse pm2 status output: ${e.message}. ` +
278
+ `If you saw a version-mismatch warning above, try ${color.cyan}npx pm2 update${color.reset}.`,
279
+ );
263
280
  return null;
264
281
  }
265
282
  }
package/bin/setup.js CHANGED
@@ -33,25 +33,39 @@ async function collectSessionScopes(existingScopes) {
33
33
  'Server / Channel Access',
34
34
  );
35
35
 
36
+ if (hasExisting) {
37
+ const summary = existingScopes
38
+ .map(
39
+ (s) =>
40
+ `${s.guild_id}${s.channel_ids.length ? ` (channels: ${s.channel_ids.join(', ')})` : ' (whole server)'}`,
41
+ )
42
+ .join('; ');
43
+ p.note(`Current server/channel settings: ${summary}`, 'Current Setting');
44
+
45
+ const change = await p.confirm({
46
+ message: 'Change the server/channel access list?',
47
+ initialValue: false,
48
+ });
49
+ if (p.isCancel(change)) {
50
+ p.cancel('Setup cancelled.');
51
+ process.exit(0);
52
+ }
53
+ if (!change) return null; // keep existing config untouched
54
+ }
55
+
36
56
  let isFirst = true;
37
57
  while (true) {
38
- const promptSuffix =
39
- isFirst && hasExisting
40
- ? ' (leave empty to keep your current server/channel settings entirely unchanged)'
41
- : ' (leave empty if you have no more servers to add)';
42
-
43
58
  const guildId = await p.text({
44
- message: `Server (Guild) ID to allow${promptSuffix}. Right-click the SERVER NAME (not a channel) → Copy Server ID:`,
59
+ message: isFirst
60
+ ? 'Server (Guild) ID to allow. Right-click the SERVER NAME (not a channel) → Copy Server ID ' +
61
+ '(leave empty if done - an empty list means /new works nowhere):'
62
+ : 'Another server ID to add (leave empty if done):',
45
63
  });
46
64
  if (p.isCancel(guildId)) {
47
65
  p.cancel('Setup cancelled.');
48
66
  process.exit(0);
49
67
  }
50
-
51
- if (!guildId) {
52
- if (isFirst) return null; // signal: user wants to keep existing config untouched
53
- break;
54
- }
68
+ if (!guildId) break;
55
69
  isFirst = false;
56
70
 
57
71
  const channelIds = [];
@@ -103,30 +117,41 @@ async function collectUserIds(existingIds, platformLabel) {
103
117
  const hasExisting = existingIds && existingIds.length > 0;
104
118
 
105
119
  p.note(
106
- 'ONLY these users can use the bot (leave completely empty on first setup to allow EVERYONE). ' +
120
+ 'ONLY these users can use the bot (leave completely empty to allow EVERYONE). ' +
107
121
  'Not related to DMs - this only gates the channel/threads configured above.',
108
122
  `Allowed ${platformLabel} Users`,
109
123
  );
110
124
 
125
+ if (hasExisting) {
126
+ p.note(
127
+ `Current allowed ${platformLabel} users: ${existingIds.join(', ')}`,
128
+ 'Current Setting',
129
+ );
130
+
131
+ const change = await p.confirm({
132
+ message: `Change the allowed-user list for ${platformLabel}?`,
133
+ initialValue: false,
134
+ });
135
+ if (p.isCancel(change)) {
136
+ p.cancel('Setup cancelled.');
137
+ process.exit(0);
138
+ }
139
+ if (!change) return null; // keep existing config untouched
140
+ }
141
+
111
142
  let isFirst = true;
112
143
  while (true) {
113
- const promptSuffix =
114
- isFirst && hasExisting
115
- ? ' (leave empty to keep your current allowed-user settings entirely unchanged)'
116
- : ' (leave empty if you have no more users to add)';
117
-
118
144
  const userId = await p.text({
119
- message: `${platformLabel} User ID to allow${promptSuffix}:`,
145
+ message: isFirst
146
+ ? `${platformLabel} User ID to allow (leave empty to allow EVERYONE):`
147
+ : `Another ${platformLabel} user ID to allow (leave empty if done):`,
120
148
  });
121
149
  if (p.isCancel(userId)) {
122
150
  p.cancel('Setup cancelled.');
123
151
  process.exit(0);
124
152
  }
125
153
 
126
- if (!userId) {
127
- if (isFirst) return null; // signal: keep existing config untouched
128
- break;
129
- }
154
+ if (!userId) break;
130
155
  isFirst = false;
131
156
 
132
157
  ids.push(userId.trim());
@@ -390,13 +415,12 @@ async function platformMenu(key) {
390
415
  );
391
416
  if (configured)
392
417
  options.push({ value: 'edit', label: 'Edit settings (token, access, etc.)' });
393
- options.push({ value: 'back', label: '← Back' });
394
418
 
395
419
  const action = await p.select({
396
- message: `${def.label} — currently ${enabled ? 'ON' : 'OFF'}${configured ? '' : ' (not configured)'}`,
420
+ message: `${def.label} — currently ${enabled ? 'ON' : 'OFF'}${configured ? '' : ' (not configured)'} (Esc to go back)`,
397
421
  options,
398
422
  });
399
- if (p.isCancel(action) || action === 'back') return;
423
+ if (p.isCancel(action)) return;
400
424
 
401
425
  if (action === 'off') {
402
426
  updateSettings({ [`${key}_enabled`]: false });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linkgravity",
3
- "version": "1.5.3",
3
+ "version": "1.5.4",
4
4
  "description": "Discord/Telegram bot bridge for the Antigravity (agy) CLI, with voice interaction support",
5
5
  "scripts": {
6
6
  "postinstall": "node npm-scripts/postinstall.js",
package/src/config.py CHANGED
@@ -112,11 +112,15 @@ TTS_VOICE = bot_settings.get("tts_voice", "ko-KR-SunHiNeural")
112
112
 
113
113
  def is_allowed_session_channel(channel) -> bool:
114
114
  """True if a new agy session may be started from this channel (via
115
- /new). A channel is allowed if its server is in SESSION_SCOPES AND
116
- either that server has no channel restriction (whole-server access)
117
- or this specific channel is in its allowed list."""
115
+ /new). DMs have no guild to scope, so they're always allowed here -
116
+ gating for DMs is handled separately via ALLOWED_IDS. For a guild
117
+ channel, it's allowed if its server is in SESSION_SCOPES AND either
118
+ that server has no channel restriction (whole-server access) or this
119
+ specific channel is in its allowed list."""
118
120
  guild = getattr(channel, "guild", None)
119
- if not guild or guild.id not in SESSION_SCOPES:
121
+ if not guild:
122
+ return True
123
+ if guild.id not in SESSION_SCOPES:
120
124
  return False
121
125
  allowed_channels = SESSION_SCOPES[guild.id]
122
126
  return allowed_channels is None or channel.id in allowed_channels