ciphermesh 2.3.0 → 2.4.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/src/p2p/index.js CHANGED
@@ -14,6 +14,7 @@ import {
14
14
  } from '../shared/banner.js';
15
15
  import { KeyManager } from '../crypto/KeyManager.js';
16
16
  import { StateManager } from '../crypto/StateManager.js';
17
+ import { HistoryStore } from '../crypto/HistoryStore.js';
17
18
  import { questionHidden } from '../shared/prompt.js';
18
19
  import { loadConfig, startupCommands } from '../shared/config.js';
19
20
  import { randomTip } from '../shared/tips.js';
@@ -171,6 +172,16 @@ await bootSequence([
171
172
  const connManager = new PeerConnectionManager(nickname, () => keyManager.publicKeyB64);
172
173
  const discovery = new Discovery();
173
174
  const ui = new UI(nickname);
175
+ // Encrypted local history (opt-in — same passphrase that protects the session)
176
+ let historyStore = null;
177
+ if (restoredState?.passphrase) {
178
+ historyStore = new HistoryStore();
179
+ if (!historyStore.open(restoredState.passphrase)) {
180
+ console.log(promptError('History: passphrase mismatch — history disabled for this session'));
181
+ historyStore = null;
182
+ }
183
+ }
184
+
174
185
  const controller = new P2PChatController(
175
186
  nickname,
176
187
  peerServer,
@@ -180,6 +191,7 @@ const controller = new P2PChatController(
180
191
  keyManager,
181
192
  restoredState,
182
193
  pluginManager,
194
+ historyStore,
183
195
  );
184
196
 
185
197
  ui.setFingerprint(controller.fingerprint);
@@ -66,7 +66,16 @@ export function parseMessage(raw) {
66
66
  return { valid: false, error: 'Message must be an object' };
67
67
  }
68
68
  if (msg.version !== PROTOCOL_VERSION) {
69
- return { valid: false, error: `Unsupported protocol version: ${msg.version}` };
69
+ // Spell out what to do: this reaches a human staring at a chat that just
70
+ // refuses to work, and "unsupported protocol version" alone tells them
71
+ // nothing about which side is behind or how to fix it.
72
+ const side = msg.version < PROTOCOL_VERSION ? 'client is older' : 'server is older';
73
+ return {
74
+ valid: false,
75
+ error:
76
+ `Protocol mismatch: this ${side} (got v${msg.version}, expected v${PROTOCOL_VERSION}). ` +
77
+ 'Update both sides — `npx ciphermesh@latest`, or `git pull && npm install` if running from source.',
78
+ };
70
79
  }
71
80
  if (!isString(msg.type)) {
72
81
  return { valid: false, error: 'Missing message type' };
package/src/shared/dnd.js CHANGED
@@ -58,3 +58,16 @@ export function mentionsMe(text, nickname) {
58
58
  }
59
59
  return new RegExp(`(^|[^a-z0-9_-])${nick}([^a-z0-9_-]|$)`).test(t);
60
60
  }
61
+
62
+ /**
63
+ * True if `text` contains `word` as a whole word (case-insensitive) — the
64
+ * matcher behind /watch. Whole-word so "dev" doesn't fire on "development",
65
+ * and the keyword is escaped so punctuation can never build a stray regex.
66
+ */
67
+ export function matchesKeyword(text, word) {
68
+ if (typeof text !== 'string' || typeof word !== 'string' || !word) {
69
+ return false;
70
+ }
71
+ const escaped = word.toLowerCase().replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
72
+ return new RegExp(`(^|[^a-z0-9_-])${escaped}([^a-z0-9_-]|$)`).test(text.toLowerCase());
73
+ }