@1presence/bridge 0.2.0 → 0.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/dist/auth.js CHANGED
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AuthCancelledError = void 0;
3
4
  exports.isTokenValid = isTokenValid;
4
5
  exports.ensureFreshToken = ensureFreshToken;
5
6
  exports.getValidAuth = getValidAuth;
@@ -69,8 +70,13 @@ function openBrowser(url) {
69
70
  console.error('Could not open browser automatically. Please open this URL manually:\n' + url);
70
71
  });
71
72
  }
73
+ class AuthCancelledError extends Error {
74
+ constructor() { super('Sign-in cancelled — the browser tab was closed.'); }
75
+ }
76
+ exports.AuthCancelledError = AuthCancelledError;
72
77
  function runBrowserAuthFlow(gatewayUrl, pwaUrl) {
73
78
  return new Promise((resolve, reject) => {
79
+ let resolved = false;
74
80
  const server = (0, http_1.createServer)((req, res) => {
75
81
  // CORS headers so the PWA (https) can POST to http://localhost
76
82
  res.setHeader('Access-Control-Allow-Origin', '*');
@@ -86,6 +92,21 @@ function runBrowserAuthFlow(gatewayUrl, pwaUrl) {
86
92
  res.end();
87
93
  return;
88
94
  }
95
+ // Status beacon from the PWA — currently used so we exit early when
96
+ // the user closes the auth tab before signing in (sendBeacon path).
97
+ const path = (req.url ?? '/').split('?')[0];
98
+ if (path === '/status') {
99
+ const params = new URL(req.url ?? '/', 'http://localhost').searchParams;
100
+ const event = params.get('event');
101
+ res.writeHead(204);
102
+ res.end();
103
+ if (event === 'closed' && !resolved) {
104
+ resolved = true;
105
+ server.close();
106
+ reject(new AuthCancelledError());
107
+ }
108
+ return;
109
+ }
89
110
  let body = '';
90
111
  req.on('data', (chunk) => { body += chunk.toString(); });
91
112
  req.on('end', () => {
@@ -98,6 +119,7 @@ function runBrowserAuthFlow(gatewayUrl, pwaUrl) {
98
119
  }
99
120
  res.writeHead(200, { 'Content-Type': 'application/json' });
100
121
  res.end(JSON.stringify({ ok: true }));
122
+ resolved = true;
101
123
  server.close();
102
124
  const uid = uidFromToken(token);
103
125
  const email = emailFromToken(token);
@@ -122,6 +144,9 @@ function runBrowserAuthFlow(gatewayUrl, pwaUrl) {
122
144
  openBrowser(authUrl);
123
145
  });
124
146
  setTimeout(() => {
147
+ if (resolved)
148
+ return;
149
+ resolved = true;
125
150
  server.close();
126
151
  reject(new Error('Sign-in timed out after 5 minutes. Please try again.'));
127
152
  }, 5 * 60 * 1000);
package/dist/claude.js CHANGED
@@ -12,7 +12,7 @@ const sessions_1 = require("./sessions");
12
12
  // Claude Code always loads CLAUDE.md files from cwd upward plus the global
13
13
  // ~/.claude/CLAUDE.md. If the bridge is launched from within a development
14
14
  // repo (e.g. PresenceAI), it would pick up project CLAUDE.md instructions that
15
- // direct it to write to local vault paths and call MemPalace — both wrong in
15
+ // direct it to write to local vault paths and call mempalace — both wrong in
16
16
  // bridge/Local Mode. We run Claude in a dedicated temp dir with a LOCAL MODE
17
17
  // CLAUDE.md that explicitly overrides those behaviors.
18
18
  const BRIDGE_CWD = (0, path_1.join)((0, os_1.tmpdir)(), '1presence-bridge');
@@ -33,11 +33,20 @@ You have NO permission to write files to this machine.
33
33
  All vault reads and writes go through the \`mcp__1presence__*\` MCP tools.
34
34
  Use those tools instead of any local file paths.
35
35
 
36
- ## MemPalace / memory
36
+ ## Memory (memory map / knowledge graph / agent diary)
37
37
 
38
- MemPalace is hosted in the cloud, not locally. If mempalace tools are available
39
- via MCP, use them. If a mempalace call fails with "not initialised", skip it
40
- silently — do NOT attempt to write memory files locally as a fallback.
38
+ The user's memory is hosted in the cloud, not locally. If \`mempalace_*\` tools
39
+ are available via MCP, use them. If a call fails with "not initialised", skip
40
+ it silently — do NOT write memory files locally as a fallback.
41
+
42
+ **User-facing language.** Never say "MemPalace", "memory palace", "memory
43
+ graph", "ChromaDB", "vector database", "embeddings", or "drawer" to the user
44
+ in any context, even casually, even in progress/status updates. The internal
45
+ tool prefix is \`mempalace_\` but the product name does not exist to the user.
46
+ Use "memory map" (wings/rooms view), "knowledge graph" (facts view), "agent
47
+ diary" (session log), or "your memory" (general). Correct: "Indexed in your
48
+ memory map for semantic search." Wrong: "Indexed in MemPalace for semantic
49
+ search."
41
50
 
42
51
  ## Session notes / chat history
43
52
 
@@ -166,6 +175,7 @@ function spawnClaude(params) {
166
175
  let usage = null;
167
176
  let extractedModel = null;
168
177
  let buffer = '';
178
+ let killedForViolation = false;
169
179
  proc.stdout.on('data', (chunk) => {
170
180
  buffer += chunk.toString('utf-8');
171
181
  const lines = buffer.split('\n');
@@ -231,6 +241,19 @@ function spawnClaude(params) {
231
241
  const toolName = block['name'];
232
242
  const prefix = toolName.startsWith('mcp__') ? '[mcp]' : '[tool]';
233
243
  process.stderr.write(`[bridge] ${prefix} ${toolName}\n`);
244
+ // Defense-in-depth: CLI flags (--tools "", --allowedTools, --strict-mcp-config,
245
+ // --setting-sources "") are supposed to make this unreachable. If we see a
246
+ // non-1Presence tool here anyway, something has bypassed those guards — kill
247
+ // immediately so any side effect already in flight is the only damage done.
248
+ if (!toolName.startsWith('mcp__1presence__')) {
249
+ killedForViolation = true;
250
+ const violation = `bridge tool violation: ${toolName} is not allowed in Local Mode`;
251
+ process.stderr.write(`[bridge] FATAL ${violation} — killing\n`);
252
+ active.delete(conversationId);
253
+ proc.kill('SIGKILL');
254
+ onError(violation, usage, extractedModel);
255
+ return;
256
+ }
234
257
  }
235
258
  else if (block['type'] === 'text') {
236
259
  const text = block['text'];
@@ -261,6 +284,9 @@ function spawnClaude(params) {
261
284
  });
262
285
  proc.on('close', (code) => {
263
286
  active.delete(conversationId);
287
+ // Violation path already called onError + killed — don't double-fire.
288
+ if (killedForViolation)
289
+ return;
264
290
  // Flush any remaining buffer
265
291
  if (buffer.trim()) {
266
292
  try {
package/dist/index.js CHANGED
@@ -290,6 +290,11 @@ async function main() {
290
290
  process.on('SIGTERM', shutdown);
291
291
  }
292
292
  main().catch((err) => {
293
+ if (err instanceof auth_1.AuthCancelledError) {
294
+ console.error(`\n${err.message}`);
295
+ console.error('Run `npx @1presence/bridge` again when you are ready to sign in.');
296
+ process.exit(0);
297
+ }
293
298
  console.error('Fatal:', err.message);
294
299
  process.exit(1);
295
300
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1presence/bridge",
3
- "version": "0.2.0",
3
+ "version": "0.4.0",
4
4
  "description": "Run 1Presence on your Mac and use your Claude.ai Pro subscription from any device",
5
5
  "bin": {
6
6
  "1presence-bridge": "dist/index.js"