@1presence/bridge 0.2.0 → 0.3.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 +25 -0
- package/dist/claude.js +17 -0
- package/dist/index.js +5 -0
- package/package.json +1 -1
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
|
@@ -166,6 +166,7 @@ function spawnClaude(params) {
|
|
|
166
166
|
let usage = null;
|
|
167
167
|
let extractedModel = null;
|
|
168
168
|
let buffer = '';
|
|
169
|
+
let killedForViolation = false;
|
|
169
170
|
proc.stdout.on('data', (chunk) => {
|
|
170
171
|
buffer += chunk.toString('utf-8');
|
|
171
172
|
const lines = buffer.split('\n');
|
|
@@ -231,6 +232,19 @@ function spawnClaude(params) {
|
|
|
231
232
|
const toolName = block['name'];
|
|
232
233
|
const prefix = toolName.startsWith('mcp__') ? '[mcp]' : '[tool]';
|
|
233
234
|
process.stderr.write(`[bridge] ${prefix} ${toolName}\n`);
|
|
235
|
+
// Defense-in-depth: CLI flags (--tools "", --allowedTools, --strict-mcp-config,
|
|
236
|
+
// --setting-sources "") are supposed to make this unreachable. If we see a
|
|
237
|
+
// non-1Presence tool here anyway, something has bypassed those guards — kill
|
|
238
|
+
// immediately so any side effect already in flight is the only damage done.
|
|
239
|
+
if (!toolName.startsWith('mcp__1presence__')) {
|
|
240
|
+
killedForViolation = true;
|
|
241
|
+
const violation = `bridge tool violation: ${toolName} is not allowed in Local Mode`;
|
|
242
|
+
process.stderr.write(`[bridge] FATAL ${violation} — killing\n`);
|
|
243
|
+
active.delete(conversationId);
|
|
244
|
+
proc.kill('SIGKILL');
|
|
245
|
+
onError(violation, usage, extractedModel);
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
234
248
|
}
|
|
235
249
|
else if (block['type'] === 'text') {
|
|
236
250
|
const text = block['text'];
|
|
@@ -261,6 +275,9 @@ function spawnClaude(params) {
|
|
|
261
275
|
});
|
|
262
276
|
proc.on('close', (code) => {
|
|
263
277
|
active.delete(conversationId);
|
|
278
|
+
// Violation path already called onError + killed — don't double-fire.
|
|
279
|
+
if (killedForViolation)
|
|
280
|
+
return;
|
|
264
281
|
// Flush any remaining buffer
|
|
265
282
|
if (buffer.trim()) {
|
|
266
283
|
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
|
});
|