squidclaw 2.0.0 → 2.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.
@@ -0,0 +1,105 @@
1
+ /**
2
+ * 🦑 Allowlist Manager
3
+ * Manage who can talk to the agent via chat commands or AI tool
4
+ */
5
+
6
+ import { loadConfig, saveConfig, getHome } from '../core/config.js';
7
+ import { logger } from '../core/logger.js';
8
+
9
+ export class AllowlistManager {
10
+ constructor(engine) {
11
+ this.engine = engine;
12
+ }
13
+
14
+ /**
15
+ * Get current allowlist for a platform
16
+ */
17
+ list(platform = 'telegram') {
18
+ const config = loadConfig();
19
+ const allowFrom = config.channels?.[platform]?.allowFrom;
20
+ if (!allowFrom || allowFrom === '*') return { mode: 'open', list: [] };
21
+ const list = Array.isArray(allowFrom) ? allowFrom : [String(allowFrom)];
22
+ return { mode: 'restricted', list };
23
+ }
24
+
25
+ /**
26
+ * Add someone to the allowlist
27
+ */
28
+ add(platform, id) {
29
+ const config = loadConfig();
30
+ if (!config.channels) config.channels = {};
31
+ if (!config.channels[platform]) config.channels[platform] = {};
32
+
33
+ let allowFrom = config.channels[platform].allowFrom;
34
+
35
+ if (!allowFrom || allowFrom === '*') {
36
+ // First time — also include current entries
37
+ config.channels[platform].allowFrom = [String(id)];
38
+ } else if (Array.isArray(allowFrom)) {
39
+ if (!allowFrom.includes(String(id))) {
40
+ allowFrom.push(String(id));
41
+ }
42
+ } else {
43
+ // Single value → convert to array
44
+ config.channels[platform].allowFrom = [String(allowFrom), String(id)];
45
+ }
46
+
47
+ saveConfig(config);
48
+ this.engine.config = config;
49
+ logger.info('allowlist', `Added ${id} to ${platform} allowlist`);
50
+ return true;
51
+ }
52
+
53
+ /**
54
+ * Remove someone from the allowlist
55
+ */
56
+ remove(platform, id) {
57
+ const config = loadConfig();
58
+ const allowFrom = config.channels?.[platform]?.allowFrom;
59
+ if (!allowFrom || allowFrom === '*') return false;
60
+
61
+ if (Array.isArray(allowFrom)) {
62
+ config.channels[platform].allowFrom = allowFrom.filter(a => a !== String(id));
63
+ if (config.channels[platform].allowFrom.length === 0) {
64
+ config.channels[platform].allowFrom = '*'; // No one left = open
65
+ }
66
+ } else if (String(allowFrom) === String(id)) {
67
+ config.channels[platform].allowFrom = '*';
68
+ }
69
+
70
+ saveConfig(config);
71
+ this.engine.config = config;
72
+ logger.info('allowlist', `Removed ${id} from ${platform} allowlist`);
73
+ return true;
74
+ }
75
+
76
+ /**
77
+ * Open to everyone
78
+ */
79
+ openAll(platform) {
80
+ const config = loadConfig();
81
+ if (config.channels?.[platform]) {
82
+ config.channels[platform].allowFrom = '*';
83
+ }
84
+ saveConfig(config);
85
+ this.engine.config = config;
86
+ return true;
87
+ }
88
+
89
+ /**
90
+ * Lock down — only allowlist can message
91
+ */
92
+ lockDown(platform) {
93
+ // Just make sure allowFrom is an array (not '*')
94
+ const config = loadConfig();
95
+ if (config.channels?.[platform]) {
96
+ const cur = config.channels[platform].allowFrom;
97
+ if (!cur || cur === '*') {
98
+ config.channels[platform].allowFrom = [];
99
+ }
100
+ }
101
+ saveConfig(config);
102
+ this.engine.config = config;
103
+ return true;
104
+ }
105
+ }
@@ -16,7 +16,12 @@ export async function commandsMiddleware(ctx, next) {
16
16
  '/memories — what I remember about you',
17
17
  '/tasks — your todo list',
18
18
  '/usage — spending report',
19
- '/notesnotes',
19
+ '/allow <id> allow someone to text me',
20
+ '/block <id> — block someone',
21
+ '/allowlist — show allowed users',
22
+ '/lockdown — restrict to allowlist only',
23
+ '/open — allow everyone',
24
+ '/notes — notes',
20
25
  '/contacts — contact book',
21
26
  '/briefing — daily briefing',
22
27
  '/summary — summarize chat',
@@ -127,6 +132,65 @@ export async function commandsMiddleware(ctx, next) {
127
132
  return;
128
133
  }
129
134
 
135
+ if (cmd === '/allow') {
136
+ const args = msg.slice(7).trim();
137
+ if (!args) { await ctx.reply('Usage: /allow <user_id or phone>'); return; }
138
+ try {
139
+ const { AllowlistManager } = await import('../features/allowlist-manager.js');
140
+ const am = new AllowlistManager(ctx.engine);
141
+ am.add(ctx.platform, args);
142
+ await ctx.reply('✅ Allowed *' + args + '* to message me');
143
+ } catch (err) { await ctx.reply('❌ ' + err.message); }
144
+ return;
145
+ }
146
+
147
+ if (cmd === '/block') {
148
+ const args = msg.slice(7).trim();
149
+ if (!args) { await ctx.reply('Usage: /block <user_id or phone>'); return; }
150
+ try {
151
+ const { AllowlistManager } = await import('../features/allowlist-manager.js');
152
+ const am = new AllowlistManager(ctx.engine);
153
+ am.remove(ctx.platform, args);
154
+ await ctx.reply('🚫 Blocked *' + args + '* from messaging me');
155
+ } catch (err) { await ctx.reply('❌ ' + err.message); }
156
+ return;
157
+ }
158
+
159
+ if (cmd === '/allowlist') {
160
+ try {
161
+ const { AllowlistManager } = await import('../features/allowlist-manager.js');
162
+ const am = new AllowlistManager(ctx.engine);
163
+ const { mode, list } = am.list(ctx.platform);
164
+ if (mode === 'open') {
165
+ await ctx.reply('🔓 *Open Mode* — anyone can message me\n\nUse /allow <id> to start restricting');
166
+ } else {
167
+ const lines = list.map(id => '• ' + id).join('\n');
168
+ await ctx.reply('🔒 *Restricted Mode*\n\nAllowed:\n' + lines + '\n\nUse /allow <id> or /block <id>');
169
+ }
170
+ } catch (err) { await ctx.reply('❌ ' + err.message); }
171
+ return;
172
+ }
173
+
174
+ if (cmd === '/open') {
175
+ try {
176
+ const { AllowlistManager } = await import('../features/allowlist-manager.js');
177
+ const am = new AllowlistManager(ctx.engine);
178
+ am.openAll(ctx.platform);
179
+ await ctx.reply('🔓 Open mode — anyone can message me now');
180
+ } catch (err) { await ctx.reply('❌ ' + err.message); }
181
+ return;
182
+ }
183
+
184
+ if (cmd === '/lockdown') {
185
+ try {
186
+ const { AllowlistManager } = await import('../features/allowlist-manager.js');
187
+ const am = new AllowlistManager(ctx.engine);
188
+ am.lockDown(ctx.platform);
189
+ await ctx.reply('🔒 Locked down — only allowlisted users can message me');
190
+ } catch (err) { await ctx.reply('❌ ' + err.message); }
191
+ return;
192
+ }
193
+
130
194
  if (cmd === '/notes') {
131
195
  try {
132
196
  const { NotesManager } = await import('../tools/notes.js');
@@ -59,6 +59,16 @@ export class ToolRouter {
59
59
  'Send an email.');
60
60
  }
61
61
 
62
+ tools.push('', '### Allow User',
63
+ '---TOOL:allow:user_id_or_phone---',
64
+ 'Add someone to the allowlist so they can message you.',
65
+ '', '### Block User',
66
+ '---TOOL:block:user_id_or_phone---',
67
+ 'Remove someone from the allowlist.',
68
+ '', '### Show Allowlist',
69
+ '---TOOL:allowlist:show---',
70
+ 'Show who is currently allowed to message.');
71
+
62
72
  tools.push('', '### Save Note',
63
73
  '---TOOL:note:content of the note---',
64
74
  'Save a personal note for the user.',
@@ -211,6 +221,36 @@ export class ToolRouter {
211
221
  }
212
222
  break;
213
223
  }
224
+ case 'allow': {
225
+ try {
226
+ const { AllowlistManager } = await import('../features/allowlist-manager.js');
227
+ const am = new AllowlistManager(this._engine);
228
+ const platform = this._currentPlatform || 'telegram';
229
+ am.add(platform, toolArg);
230
+ toolResult = 'Allowed ' + toolArg + ' to message you ✅';
231
+ } catch (err) { toolResult = 'Failed: ' + err.message; }
232
+ break;
233
+ }
234
+ case 'block': {
235
+ try {
236
+ const { AllowlistManager } = await import('../features/allowlist-manager.js');
237
+ const am = new AllowlistManager(this._engine);
238
+ const platform = this._currentPlatform || 'telegram';
239
+ am.remove(platform, toolArg);
240
+ toolResult = 'Blocked ' + toolArg + ' ✅';
241
+ } catch (err) { toolResult = 'Failed: ' + err.message; }
242
+ break;
243
+ }
244
+ case 'allowlist': {
245
+ try {
246
+ const { AllowlistManager } = await import('../features/allowlist-manager.js');
247
+ const am = new AllowlistManager(this._engine);
248
+ const platform = this._currentPlatform || 'telegram';
249
+ const { mode, list } = am.list(platform);
250
+ toolResult = mode === 'open' ? 'Open — anyone can message' : 'Restricted to: ' + list.join(', ');
251
+ } catch (err) { toolResult = 'Failed: ' + err.message; }
252
+ break;
253
+ }
214
254
  case 'note': {
215
255
  const { NotesManager } = await import('./notes.js');
216
256
  const nm = new NotesManager(this.storage);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "squidclaw",
3
- "version": "2.0.0",
3
+ "version": "2.1.0",
4
4
  "description": "🦑 AI agent platform — human-like agents for WhatsApp, Telegram & more",
5
5
  "main": "lib/engine.js",
6
6
  "bin": {