nothumanallowed 10.3.0 → 10.3.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nothumanallowed",
3
- "version": "10.3.0",
3
+ "version": "10.3.2",
4
4
  "description": "NotHumanAllowed — 38 AI agents, 53 tools. Email, calendar, browser automation, screen capture, canvas, cron/heartbeat, GitHub, Notion, Slack, voice chat, 28 languages. Zero-dependency CLI.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -383,17 +383,43 @@ export async function cmdUI(args) {
383
383
 
384
384
  if (collabAction === 'send' && method === 'POST') {
385
385
  const body = await parseBody(req);
386
- // For simplicity in web UI, send as plaintext (public channel mode)
387
- // In private mode, encryption happens client-side
386
+ // Encrypt with the same channel key used by CLI
387
+ const channelKey = crypto.createHash('sha256').update('alexandria-channel-key-v1').update(body.channelId).digest();
388
+ const nonce = crypto.randomBytes(12);
389
+ const cipher = crypto.createCipheriv('aes-256-gcm', channelKey, nonce);
390
+ const encrypted = Buffer.concat([cipher.update(body.message, 'utf-8'), cipher.final()]);
391
+ const tag = cipher.getAuthTag();
392
+ const ciphertext = Buffer.concat([encrypted, tag]).toString('base64');
393
+
388
394
  const r = await fetch(ALEX_API + '/channels/' + body.channelId + '/messages', {
389
395
  method: 'POST', headers: { 'Content-Type': 'application/json' },
390
- body: JSON.stringify({ senderFingerprint: identity.fingerprint, nonce: 'webui', ciphertext: Buffer.from(body.message).toString('base64'), type: 'text' }),
396
+ body: JSON.stringify({ senderFingerprint: identity.fingerprint, nonce: nonce.toString('base64'), ciphertext, type: 'text' }),
391
397
  });
392
398
  sendJSON(res, 200, await r.json());
393
399
  logRequest(method, pathname, 200, Date.now() - start);
394
400
  return;
395
401
  }
396
402
 
403
+ if (collabAction === 'delete' && method === 'POST') {
404
+ const body = await parseBody(req);
405
+ // Remove from local channels file
406
+ const chFile = path.join(collabDir, 'channels.json');
407
+ let localChannels = [];
408
+ if (fs.existsSync(chFile)) { try { localChannels = JSON.parse(fs.readFileSync(chFile, 'utf-8')); } catch {} }
409
+ localChannels = localChannels.filter(c => c.id !== body.channelId);
410
+ fs.writeFileSync(chFile, JSON.stringify(localChannels, null, 2), { mode: 0o600 });
411
+ // Try to delete from server too
412
+ try {
413
+ await fetch(ALEX_API + '/channels/' + body.channelId, {
414
+ method: 'DELETE', headers: { 'Content-Type': 'application/json' },
415
+ body: JSON.stringify({ fingerprint: identity.fingerprint }),
416
+ });
417
+ } catch {}
418
+ sendJSON(res, 200, { ok: true });
419
+ logRequest(method, pathname, 200, Date.now() - start);
420
+ return;
421
+ }
422
+
397
423
  if (collabAction === 'publish' && method === 'POST') {
398
424
  const body = await parseBody(req);
399
425
  // Load conversation and publish as public channel
package/src/constants.mjs CHANGED
@@ -5,7 +5,7 @@ import { fileURLToPath } from 'url';
5
5
  const __filename = fileURLToPath(import.meta.url);
6
6
  const __dirname = path.dirname(__filename);
7
7
 
8
- export const VERSION = '10.3.0';
8
+ export const VERSION = '10.3.2';
9
9
  export const BASE_URL = 'https://nothumanallowed.com/cli';
10
10
  export const API_BASE = 'https://nothumanallowed.com/api/v1';
11
11
 
@@ -1644,6 +1644,7 @@ function renderCollabChannelList(){
1644
1644
  h+='<div onclick="collabSelect(\\x27'+ch.id+'\\x27)" style="padding:8px 12px;cursor:pointer;border-left:3px solid '+(active?'var(--amber)':'transparent')+';background:'+(active?'var(--bg2)':'transparent')+';margin-bottom:2px;border-radius:0 6px 6px 0">';
1645
1645
  h+='<span style="font-size:12px;color:var(--fg)">'+esc(ch.name)+'</span>';
1646
1646
  h+='<span style="font-size:9px;color:var(--dim);margin-left:8px">'+ch.id.slice(0,8)+'...</span>';
1647
+ h+='<button onclick="event.stopPropagation();collabDeleteChannel(\\x27'+ch.id+'\\x27)" style="float:right;background:none;border:none;color:var(--red);cursor:pointer;font-size:10px;font-family:var(--mono);opacity:0.5" title="Delete channel">del</button>';
1647
1648
  h+='</div>';
1648
1649
  }
1649
1650
  el.innerHTML=h;
@@ -1675,6 +1676,18 @@ function collabJoinChannel(){
1675
1676
  });
1676
1677
  }
1677
1678
 
1679
+ function collabDeleteChannel(id){
1680
+ if(!confirm('Delete this channel? Messages will be lost.'))return;
1681
+ // Remove from local file
1682
+ collabChannels=collabChannels.filter(function(c){return c.id!==id});
1683
+ apiPost('/api/collab/channels',{id:'__delete__'+id}).catch(function(){});
1684
+ // Delete from server
1685
+ fetch(API+'/api/collab/delete',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({channelId:id})}).catch(function(){});
1686
+ if(collabActiveChannel===id)collabActiveChannel=null;
1687
+ renderCollabChannelList();
1688
+ var msgEl=document.getElementById('collabMessages');
1689
+ if(msgEl)msgEl.innerHTML='<div style="text-align:center;color:var(--dim);padding:40px;font-size:12px">Channel deleted</div>';
1690
+ }
1678
1691
  function collabSelect(id){
1679
1692
  collabActiveChannel=id;
1680
1693
  collabLoadMessages();