Merge pull request #65 from outis1one/claude/read-repo-yLNcT

Claude/read repo y l nc t
This commit is contained in:
Outis
2026-04-01 00:31:16 -04:00
committed by GitHub
7 changed files with 42 additions and 8 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"manifest_version": 3, "manifest_version": 3,
"name": "Silent Send", "name": "Silent Send",
"version": "0.9.32", "version": "0.9.33",
"description": "Intercepts personal info and substitutes it with user-defined replacements before sending to AI services.", "description": "Intercepts personal info and substitutes it with user-defined replacements before sending to AI services.",
"browser_specific_settings": { "browser_specific_settings": {
"gecko": { "gecko": {
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"manifest_version": 3, "manifest_version": 3,
"name": "Silent Send", "name": "Silent Send",
"version": "0.9.32", "version": "0.9.33",
"description": "Intercepts personal info and substitutes it with user-defined replacements before sending to AI services.", "description": "Intercepts personal info and substitutes it with user-defined replacements before sending to AI services.",
"permissions": [ "permissions": [
"storage", "storage",
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "silent-send", "name": "silent-send",
"version": "0.9.32", "version": "0.9.33",
"private": true, "private": true,
"license": "MIT", "license": "MIT",
"description": "Browser extension that substitutes personal data before sending to AI services", "description": "Browser extension that substitutes personal data before sending to AI services",
+17
View File
@@ -184,6 +184,23 @@ const messageHandlers = {
sendResponse({ locked }); sendResponse({ locked });
}, },
// Content script requests decrypted data on page load when it sees
// encrypted storage but the key is already cached (e.g. after a sync import).
async 'vault:request-unlock'(_message, sender) {
const locked = await Storage.isLocked();
if (locked) return; // key not available — user must unlock via popup
const mappings = await Storage.getMappings();
const identity = await Storage.getIdentity();
const settings = await Storage.getSettings();
api.tabs.sendMessage(sender.tab.id, {
type: 'vault:unlocked',
mappings,
identity,
settings,
}).catch(() => {});
},
async 'vault:unlocked'() { async 'vault:unlocked'() {
// User unlocked the vault — clear the LOCK badge and refresh icon // User unlocked the vault — clear the LOCK badge and refresh icon
api.action.setBadgeText({ text: '' }); api.action.setBadgeText({ text: '' });
+7 -1
View File
@@ -60,13 +60,19 @@
const result = await api.storage.local.get(['ss_mappings', 'ss_identity', 'ss_settings']); const result = await api.storage.local.get(['ss_mappings', 'ss_identity', 'ss_settings']);
const settings = result.ss_settings || { enabled: true }; const settings = result.ss_settings || { enabled: true };
// Check if data is encrypted (locked) — pass empty config // Check if data is encrypted — pass empty config and request decryption
// The background will send decrypted data via vault:unlocked when ready // The background will send decrypted data via vault:unlocked when ready
const isLocked = result.ss_mappings?._ssLocalEncrypted || const isLocked = result.ss_mappings?._ssLocalEncrypted ||
result.ss_identity?._ssLocalEncrypted; result.ss_identity?._ssLocalEncrypted;
const mappings = isLocked ? [] : (result.ss_mappings || []); const mappings = isLocked ? [] : (result.ss_mappings || []);
const identityData = isLocked ? {} : (result.ss_identity || {}); const identityData = isLocked ? {} : (result.ss_identity || {});
// If data is encrypted but the vault key may already be cached
// (e.g. after a sync import), ask the background to push decrypted data
if (isLocked) {
api.runtime.sendMessage({ type: 'vault:request-unlock' }).catch(() => {});
}
// Merge active profiles into a flat identity object for the content script // Merge active profiles into a flat identity object for the content script
const identity = mergeProfiles(identityData); const identity = mergeProfiles(identityData);
+5 -2
View File
@@ -901,7 +901,7 @@ const SilentSendSync = {
const result = await api.storage.local.get('ss_lastModified'); const result = await api.storage.local.get('ss_lastModified');
return { return {
version: '1', version: '1',
lastModified: result.ss_lastModified || Date.now(), lastModified: result.ss_lastModified || 0,
identity: identity || {}, identity: identity || {},
mappings: mappings || [], mappings: mappings || [],
settings: settings || {}, settings: settings || {},
@@ -924,9 +924,12 @@ const SilentSendSync = {
} }
// Metadata stays plaintext // Metadata stays plaintext
const now = Date.now();
await api.storage.local.set({ await api.storage.local.set({
ss_lastModified: data.lastModified, ss_lastModified: data.lastModified,
ss_sync_notification: { source, time: Date.now() }, ss_sync_notification: { source, time: now },
ss_last_pull_time: now,
ss_last_pull_source: source,
}); });
}, },
+10 -2
View File
@@ -161,9 +161,13 @@ document.addEventListener('DOMContentLoaded', async () => {
// --- GitHub Gist sync --- // --- GitHub Gist sync ---
// Restore saved token (session only — never persisted to storage) // Restore saved token (session only — never persisted to storage)
{ {
const stored = await api.storage.local.get('ss_gist_id'); const stored = await api.storage.local.get(['ss_gist_id', 'ss_last_pull_time', 'ss_last_pull_source']);
if (stored.ss_gist_id) { if (stored.ss_gist_id) {
setGistSyncStatus(`Gist ID: ${stored.ss_gist_id.slice(0, 12)}`, 'ok'); let status = `Gist ID: ${stored.ss_gist_id.slice(0, 12)}`;
if (stored.ss_last_pull_time && stored.ss_last_pull_source === 'gist') {
status += ` · Last pulled: ${new Date(stored.ss_last_pull_time).toLocaleString()}`;
}
setGistSyncStatus(status, 'ok');
} }
} }
@@ -197,6 +201,7 @@ document.addEventListener('DOMContentLoaded', async () => {
setGistSyncStatus('Pull failed: ' + r2.reason, 'error'); setGistSyncStatus('Pull failed: ' + r2.reason, 'error');
} else if (r2.imported) { } else if (r2.imported) {
setGistSyncStatus(`Pulled (${r2.time}). Refreshing…`, 'ok'); setGistSyncStatus(`Pulled (${r2.time}). Refreshing…`, 'ok');
api.runtime.sendMessage({ type: 'vault:unlocked' }).catch(() => {});
mappings = await Storage.getMappings(); mappings = await Storage.getMappings();
settings = await Storage.getSettings(); settings = await Storage.getSettings();
renderMappings(); renderMappings();
@@ -211,6 +216,7 @@ document.addEventListener('DOMContentLoaded', async () => {
setGistSyncStatus('Pull failed: ' + r.reason, 'error'); setGistSyncStatus('Pull failed: ' + r.reason, 'error');
} else if (r.imported) { } else if (r.imported) {
setGistSyncStatus(`Pulled (${r.time}). Refreshing…`, 'ok'); setGistSyncStatus(`Pulled (${r.time}). Refreshing…`, 'ok');
api.runtime.sendMessage({ type: 'vault:unlocked' }).catch(() => {});
mappings = await Storage.getMappings(); mappings = await Storage.getMappings();
settings = await Storage.getSettings(); settings = await Storage.getSettings();
renderMappings(); renderMappings();
@@ -254,6 +260,7 @@ document.addEventListener('DOMContentLoaded', async () => {
setUrlSyncStatus('Pull failed: ' + r2.reason, 'error'); setUrlSyncStatus('Pull failed: ' + r2.reason, 'error');
} else if (r2.imported) { } else if (r2.imported) {
setUrlSyncStatus(`Pulled (${r2.time}). Refreshing…`, 'ok'); setUrlSyncStatus(`Pulled (${r2.time}). Refreshing…`, 'ok');
api.runtime.sendMessage({ type: 'vault:unlocked' }).catch(() => {});
mappings = await Storage.getMappings(); mappings = await Storage.getMappings();
settings = await Storage.getSettings(); settings = await Storage.getSettings();
renderMappings(); renderMappings();
@@ -268,6 +275,7 @@ document.addEventListener('DOMContentLoaded', async () => {
setUrlSyncStatus('Pull failed: ' + r.reason, 'error'); setUrlSyncStatus('Pull failed: ' + r.reason, 'error');
} else if (r.imported) { } else if (r.imported) {
setUrlSyncStatus(`Pulled (${r.time}). Refreshing…`, 'ok'); setUrlSyncStatus(`Pulled (${r.time}). Refreshing…`, 'ok');
api.runtime.sendMessage({ type: 'vault:unlocked' }).catch(() => {});
mappings = await Storage.getMappings(); mappings = await Storage.getMappings();
settings = await Storage.getSettings(); settings = await Storage.getSettings();
renderMappings(); renderMappings();