Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions apps/desktop/src/main/agent-cli/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,8 @@ export interface CopilotSettingsView {
/** GET only. */
eligible?: boolean;
byokAvailable?: boolean;
/** Own keys are sealed under a key passphrase set at pairux.com/settings/copilot. */
passphraseSet?: boolean;
balance?: string;
balanceMicros?: number;
}
Expand Down
2 changes: 2 additions & 0 deletions apps/desktop/src/main/agent-cli/copilot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,8 @@ export function formatSettings(s: CopilotSettingsView): string[] {
);
}
if (s.byokAvailable === false) lines.push(' (own keys are not enabled on this server)');
if (s.passphraseSet === false)
lines.push(' (set a key passphrase at https://pairux.com/settings/copilot to save own keys)');
if (s.eligible === false) lines.push('The copilot is part of PairUX Pro and Team.');
if (s.balance !== undefined) lines.push(`Pay-as-you-go balance: ${s.balance}`);
return lines;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
} from '@pairux/meeting-copilot';
import { getElectronAPI } from '@/lib/ipc';
import { API_BASE_URL } from '../../../shared/config';
// The web app's seal v2 client code (WebCrypto only): own keys unlock here too.
import { dataKeyFromSecret } from '../../../../../web/src/lib/sealed/unlock-secret';

// Ask the main process for the token once a minute.
let cached: { token: string | null; until: number } = { token: null, until: 0 };
Expand All @@ -25,6 +27,7 @@ const DESKTOP: MeetingCopilotEnvironment = {
openUrl: (url) => {
void getElectronAPI().invoke('auth:openExternal', url);
},
unlockKeys: dataKeyFromSecret,
};

// The desktop app is not localized: the panel's English strings.
Expand Down
23 changes: 20 additions & 3 deletions apps/web/messages/ar.json
Original file line number Diff line number Diff line change
Expand Up @@ -2401,6 +2401,15 @@
"unclear": "رسائل المتابعة غير واضحة"
},
"thin": "النص قصير جدًا لمراجعة هذه المحادثة بإنصاف.",
"ownKeys": {
"locked": "مفاتيح API الخاصة بك مقفلة. أدخل عبارة مرور المفاتيح لاستخدامها في هذا الاجتماع.",
"lockedLive": "مفاتيح API الخاصة بك مقفلة، لذا يعمل المساعد على النماذج المجانية. افتحها للعودة إليها.",
"secret": "عبارة المرور أو مفتاح الاسترداد",
"unlock": "فتح المفاتيح",
"note": "يحتفظ PairUX بالمفتاح المفتوح في الذاكرة لهذا الاجتماع فقط ويحذفه عند توقف المساعد.",
"legacy": "تحتاج مفاتيحك المحفوظة إلى عبارة مرور قبل استخدامها. عيّنها في المفاتيح والرصيد.",
"webOnly": "مفاتيح API الخاصة بك مقفلة. ابدأ المساعد على pairux.com لفتحها."
},
"fullReport": "فتح الملاحظات كاملة",
"restart": "البدء من جديد",
"roles": {
Expand Down Expand Up @@ -2454,8 +2463,7 @@
"checkoutUnavailable": "الدفع غير متاح حاليًا. حاول مرة أخرى بعد قليل.",
"defaultRole": "الدور الافتراضي",
"keysTitle": "مفاتيح API الخاصة بك",
"keysIntro": "استخدم مفاتيحك الخاصة ولن نحاسبك أبدًا على الذكاء الاصطناعي. تُشفَّر المفاتيح، ولا تُعرض مرة أخرى أبدًا، ولا يستخدمها إلا مساعدك.",
"keysDisabled": "المفاتيح الخاصة غير مفعّلة على هذا الخادم بعد.",
"keysIntro": "مع مفاتيحك الخاصة لا نحاسبك أبدًا على الذكاء الاصطناعي. المفاتيح مختومة بعبارة مرور لا يعرفها سواك، لذا لا يستطيع PairUX فتحها؛ تفتحها لاجتماع واحد في كل مرة. إن فقدت العبارة ومفتاح الاسترداد فأعد إدخال مفاتيحك.",
"providers": {
"anthropic": "Anthropic",
"openai": "OpenAI",
Expand Down Expand Up @@ -2507,6 +2515,15 @@
"connectRunnerHint": "AGENT_EXEC هو الأمر الذي يجيب: تصل المطالبة عبر stdin والرد هو ما يطبعه.",
"connectApi": "أو استدعِ واجهة API بنفسك",
"connectApiHint": "يرسل كل استدعاء المفتاح كرمز Bearer. استطلع النص بالمؤشر الذي يُعاد."
}
},
"passphraseFirst": "عيّن عبارة مرور للمفاتيح قبل حفظها. هي نفس عبارة البث التلقائي على YouTube.",
"passphraseLegacy": "مفاتيحك المحفوظة بانتظار نقلها تحت عبارة المرور الخاصة بك. عيّن واحدة لمواصلة استخدامها.",
"passphrase": "عبارة المرور ({min}+ أحرف)",
"passphraseRepeat": "كرر عبارة المرور",
"passphraseShort": "استخدم {min} أحرف على الأقل.",
"passphraseMismatch": "العبارتان مختلفتان.",
"passphraseSet": "تعيين عبارة المرور",
"recoveryTitle": "مفتاح الاسترداد (يُعرض مرة واحدة). احفظه في مكان آمن:",
"recoverySaved": "لقد حفظته"
}
}
23 changes: 20 additions & 3 deletions apps/web/messages/de.json
Original file line number Diff line number Diff line change
Expand Up @@ -2401,6 +2401,15 @@
"unclear": "Follow-ups sind unklar"
},
"thin": "Das Transkript ist zu kurz, um dieses Gespräch fair zu bewerten.",
"ownKeys": {
"locked": "Deine eigenen API-Schlüssel sind gesperrt. Gib deine Schlüssel-Passphrase ein, um sie in diesem Meeting zu nutzen.",
"lockedLive": "Deine eigenen API-Schlüssel sind gesperrt, daher nutzt der Copilot kostenlose Modelle. Entsperre sie, um zurückzuwechseln.",
"secret": "Schlüssel-Passphrase oder Wiederherstellungsschlüssel",
"unlock": "Schlüssel entsperren",
"note": "PairUX hält den entsperrten Schlüssel nur für dieses Meeting im Arbeitsspeicher und verwirft ihn, wenn der Copilot stoppt.",
"legacy": "Deine gespeicherten Schlüssel brauchen eine Schlüssel-Passphrase, bevor sie genutzt werden können. Lege sie unter Schlüssel und Guthaben fest.",
"webOnly": "Deine eigenen API-Schlüssel sind gesperrt. Starte den Copilot auf pairux.com, um sie zu entsperren."
},
"fullReport": "Vollständige Notizen öffnen",
"restart": "Neu starten",
"roles": {
Expand Down Expand Up @@ -2454,8 +2463,7 @@
"checkoutUnavailable": "Der Checkout ist gerade nicht verfügbar. Versuch es gleich noch einmal.",
"defaultRole": "Standardrolle",
"keysTitle": "Deine eigenen API-Schlüssel",
"keysIntro": "Mit deinen eigenen Schlüsseln berechnen wir dir nie etwas für KI. Schlüssel werden verschlüsselt, nie wieder angezeigt und nur von deinem Copilot verwendet.",
"keysDisabled": "Eigene Schlüssel sind auf diesem Server noch nicht aktiviert.",
"keysIntro": "Mit deinen eigenen Schlüsseln berechnen wir dir nie etwas für KI. Die Schlüssel sind mit einer Passphrase versiegelt, die nur du kennst, daher kann PairUX sie nicht öffnen; du entsperrst sie jeweils für ein Meeting. Verlierst du Passphrase und Wiederherstellungsschlüssel, gibst du deine Schlüssel neu ein.",
"providers": {
"anthropic": "Anthropic",
"openai": "OpenAI",
Expand Down Expand Up @@ -2507,6 +2515,15 @@
"connectRunnerHint": "AGENT_EXEC ist der Befehl, der antwortet: Der Prompt kommt über stdin, die Antwort ist seine Ausgabe.",
"connectApi": "Oder ruf die API selbst auf",
"connectApiHint": "Jeder Aufruf sendet den Schlüssel als Bearer-Token. Frag das Transkript mit dem zurückgegebenen Cursor ab."
}
},
"passphraseFirst": "Lege eine Schlüssel-Passphrase fest, bevor du Schlüssel speicherst. Es ist dieselbe Passphrase wie für YouTube Auto-Go-Live.",
"passphraseLegacy": "Deine gespeicherten Schlüssel warten darauf, unter deine eigene Schlüssel-Passphrase zu wechseln. Lege eine fest, um sie weiter zu nutzen.",
"passphrase": "Schlüssel-Passphrase ({min}+ Zeichen)",
"passphraseRepeat": "Passphrase wiederholen",
"passphraseShort": "Verwende mindestens {min} Zeichen.",
"passphraseMismatch": "Die beiden Passphrasen unterscheiden sich.",
"passphraseSet": "Passphrase festlegen",
"recoveryTitle": "Wiederherstellungsschlüssel (nur einmal angezeigt). Bewahre ihn sicher auf:",
"recoverySaved": "Ich habe ihn gespeichert"
}
}
23 changes: 20 additions & 3 deletions apps/web/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -2401,6 +2401,15 @@
"unclear": "Follow-ups are unclear"
},
"thin": "The transcript is too short to review this conversation fairly.",
"ownKeys": {
"locked": "Your own API keys are locked. Enter your key passphrase to use them in this meeting.",
"lockedLive": "Your own API keys are locked, so the copilot is on free models. Unlock them to switch back.",
"secret": "Key passphrase or recovery key",
"unlock": "Unlock keys",
"note": "PairUX keeps the unlocked key in memory for this meeting only and drops it when the copilot stops.",
"legacy": "Your saved keys need a key passphrase before they can be used. Set one in Keys and credit.",
"webOnly": "Your own API keys are locked. Start the copilot on pairux.com to unlock them."
},
"fullReport": "Open full notes",
"restart": "Start again",
"roles": {
Expand Down Expand Up @@ -2454,8 +2463,7 @@
"checkoutUnavailable": "Checkout is unavailable right now. Try again shortly.",
"defaultRole": "Default role",
"keysTitle": "Your own API keys",
"keysIntro": "Use your own keys and we never charge you for AI. Keys are encrypted, never shown again, and used only by your copilot.",
"keysDisabled": "Own keys are not enabled on this server yet.",
"keysIntro": "Use your own keys and we never charge you for AI. Keys are sealed under a key passphrase only you know, so PairUX cannot open them; you unlock them one meeting at a time. Lose the passphrase and the recovery key and you re-enter your keys.",
"providers": {
"anthropic": "Anthropic",
"openai": "OpenAI",
Expand Down Expand Up @@ -2507,6 +2515,15 @@
"connectRunnerHint": "AGENT_EXEC is the command that answers: the prompt arrives on stdin, the reply is what it prints.",
"connectApi": "Or call the API yourself",
"connectApiHint": "Every call sends the key as a Bearer token. Poll the transcript with the cursor it returns."
}
},
"passphraseFirst": "Set a key passphrase before saving keys. It is the same passphrase as YouTube auto go-live.",
"passphraseLegacy": "Your saved keys are waiting to move under your own key passphrase. Set one to keep using them.",
"passphrase": "Key passphrase ({min}+ characters)",
"passphraseRepeat": "Repeat passphrase",
"passphraseShort": "Use at least {min} characters.",
"passphraseMismatch": "The two passphrases differ.",
"passphraseSet": "Set passphrase",
"recoveryTitle": "Recovery key (shown once). Keep it somewhere safe:",
"recoverySaved": "I saved it"
}
}
23 changes: 20 additions & 3 deletions apps/web/messages/es.json
Original file line number Diff line number Diff line change
Expand Up @@ -2401,6 +2401,15 @@
"unclear": "Los seguimientos no están claros"
},
"thin": "La transcripción es demasiado corta para analizar esta conversación de forma justa.",
"ownKeys": {
"locked": "Tus claves de API están bloqueadas. Introduce tu frase de contraseña para usarlas en esta reunión.",
"lockedLive": "Tus claves de API están bloqueadas, así que el copiloto usa modelos gratuitos. Desbloquéalas para volver a ellas.",
"secret": "Frase de contraseña o clave de recuperación",
"unlock": "Desbloquear claves",
"note": "PairUX guarda la clave desbloqueada en memoria solo para esta reunión y la descarta cuando el copiloto se detiene.",
"legacy": "Tus claves guardadas necesitan una frase de contraseña antes de poder usarse. Configúrala en Claves y crédito.",
"webOnly": "Tus claves de API están bloqueadas. Inicia el copiloto en pairux.com para desbloquearlas."
},
"fullReport": "Abrir notas completas",
"restart": "Empezar de nuevo",
"roles": {
Expand Down Expand Up @@ -2454,8 +2463,7 @@
"checkoutUnavailable": "El pago no está disponible ahora mismo. Inténtalo de nuevo en breve.",
"defaultRole": "Rol predeterminado",
"keysTitle": "Tus propias claves de API",
"keysIntro": "Usa tus propias claves y nunca te cobraremos por la IA. Las claves se cifran, no se vuelven a mostrar y solo las usa tu copiloto.",
"keysDisabled": "Las claves propias aún no están habilitadas en este servidor.",
"keysIntro": "Con tus propias claves nunca te cobramos la IA. Las claves están selladas con una frase de contraseña que solo tú conoces, así que PairUX no puede abrirlas; las desbloqueas reunión a reunión. Si pierdes la frase y la clave de recuperación, vuelves a introducir tus claves.",
"providers": {
"anthropic": "Anthropic",
"openai": "OpenAI",
Expand Down Expand Up @@ -2507,6 +2515,15 @@
"connectRunnerHint": "AGENT_EXEC es el comando que responde: el prompt llega por stdin y la respuesta es lo que imprime.",
"connectApi": "O llama a la API tú mismo",
"connectApiHint": "Cada llamada envía la clave como token Bearer. Consulta la transcripción con el cursor que devuelve."
}
},
"passphraseFirst": "Configura una frase de contraseña antes de guardar claves. Es la misma frase que la emisión automática en YouTube.",
"passphraseLegacy": "Tus claves guardadas esperan pasar a tu propia frase de contraseña. Configura una para seguir usándolas.",
"passphrase": "Frase de contraseña ({min}+ caracteres)",
"passphraseRepeat": "Repite la frase",
"passphraseShort": "Usa al menos {min} caracteres.",
"passphraseMismatch": "Las dos frases no coinciden.",
"passphraseSet": "Configurar frase",
"recoveryTitle": "Clave de recuperación (se muestra una vez). Guárdala en un lugar seguro:",
"recoverySaved": "Ya la guardé"
}
}
23 changes: 20 additions & 3 deletions apps/web/messages/fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -2401,6 +2401,15 @@
"unclear": "Les suivis ne sont pas clairs"
},
"thin": "La transcription est trop courte pour analyser équitablement cette conversation.",
"ownKeys": {
"locked": "Vos clés API sont verrouillées. Saisissez votre phrase secrète pour les utiliser dans cette réunion.",
"lockedLive": "Vos clés API sont verrouillées, le copilote utilise donc les modèles gratuits. Déverrouillez-les pour revenir à vos clés.",
"secret": "Phrase secrète ou clé de récupération",
"unlock": "Déverrouiller les clés",
"note": "PairUX garde la clé déverrouillée en mémoire pour cette réunion uniquement et l’efface quand le copilote s’arrête.",
"legacy": "Vos clés enregistrées ont besoin d’une phrase secrète avant de pouvoir servir. Définissez-la dans Clés et crédit.",
"webOnly": "Vos clés API sont verrouillées. Démarrez le copilote sur pairux.com pour les déverrouiller."
},
"fullReport": "Ouvrir les notes complètes",
"restart": "Recommencer",
"roles": {
Expand Down Expand Up @@ -2454,8 +2463,7 @@
"checkoutUnavailable": "Le paiement est indisponible pour le moment. Réessayez dans un instant.",
"defaultRole": "Rôle par défaut",
"keysTitle": "Vos propres clés API",
"keysIntro": "Utilisez vos propres clés et nous ne vous facturerons jamais l'IA. Les clés sont chiffrées, ne sont plus jamais affichées et ne servent qu'à votre copilote.",
"keysDisabled": "Les clés personnelles ne sont pas encore activées sur ce serveur.",
"keysIntro": "Avec vos propres clés, nous ne vous facturons jamais l’IA. Les clés sont scellées par une phrase secrète que vous seul connaissez, PairUX ne peut donc pas les ouvrir ; vous les déverrouillez réunion par réunion. Si vous perdez la phrase et la clé de récupération, vous saisissez à nouveau vos clés.",
"providers": {
"anthropic": "Anthropic",
"openai": "OpenAI",
Expand Down Expand Up @@ -2507,6 +2515,15 @@
"connectRunnerHint": "AGENT_EXEC est la commande qui répond : le prompt arrive sur stdin, la réponse est ce qu’elle affiche.",
"connectApi": "Ou appelez l’API vous-même",
"connectApiHint": "Chaque appel envoie la clé comme jeton Bearer. Interrogez la transcription avec le curseur renvoyé."
}
},
"passphraseFirst": "Définissez une phrase secrète avant d’enregistrer des clés. C’est la même que pour le direct automatique YouTube.",
"passphraseLegacy": "Vos clés enregistrées attendent de passer sous votre propre phrase secrète. Définissez-en une pour continuer à les utiliser.",
"passphrase": "Phrase secrète ({min}+ caractères)",
"passphraseRepeat": "Répétez la phrase",
"passphraseShort": "Utilisez au moins {min} caractères.",
"passphraseMismatch": "Les deux phrases diffèrent.",
"passphraseSet": "Définir la phrase",
"recoveryTitle": "Clé de récupération (affichée une seule fois). Conservez-la en lieu sûr :",
"recoverySaved": "Je l’ai enregistrée"
}
}
Loading
Loading