diff --git a/eslint.config.js b/eslint.config.js
index b783bb4..4bb5395 100644
--- a/eslint.config.js
+++ b/eslint.config.js
@@ -1,7 +1,5 @@
import js from '@eslint/js'
import globals from 'globals'
-import reactHooks from 'eslint-plugin-react-hooks'
-import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
export default tseslint.config(
@@ -13,17 +11,6 @@ export default tseslint.config(
ecmaVersion: 2020,
globals: globals.browser,
},
- plugins: {
- 'react-hooks': reactHooks,
- 'react-refresh': reactRefresh,
- },
- rules: {
- ...reactHooks.configs.recommended.rules,
- 'react-refresh/only-export-components': [
- 'warn',
- { allowConstantExport: true },
- ],
- },
},
{
// Special rules for test files
diff --git a/index.html b/index.html
deleted file mode 100644
index e4b78ea..0000000
--- a/index.html
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
-
- Vite + React + TS
-
-
-
-
-
-
diff --git a/package.json b/package.json
index f6c2520..c722dbe 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@agicash/opensecret-sdk",
- "version": "0.1.0",
+ "version": "0.2.0",
"publishConfig": {
"access": "public"
},
@@ -35,10 +35,6 @@
"docs:clear": "cd website && bun run clear",
"docs:deploy": "cd website && bun run deploy"
},
- "peerDependencies": {
- "react": "^18.0.0 || ^19.0.0",
- "react-dom": "^18.0.0 || ^19.0.0"
- },
"dependencies": {
"@peculiar/x509": "^1.12.2",
"@stablelib/base64": "^2.0.0",
@@ -53,12 +49,7 @@
"@noble/curves": "^1.6.0",
"@noble/hashes": "^1.5.0",
"@types/bun": "latest",
- "@types/react": "^19.0.0",
- "@types/react-dom": "^19.0.0",
- "@vitejs/plugin-react": "^4.3.3",
"eslint": "^9.13.0",
- "eslint-plugin-react-hooks": "^5.0.0",
- "eslint-plugin-react-refresh": "^0.4.14",
"globals": "^15.11.0",
"openai": "^5.20.0",
"prettier": "^3.3.3",
diff --git a/src/AI.tsx b/src/AI.tsx
deleted file mode 100644
index f28e552..0000000
--- a/src/AI.tsx
+++ /dev/null
@@ -1,110 +0,0 @@
-import { useOpenSecret } from "./lib";
-import { useState, FormEvent } from "react";
-import OpenAI from "openai";
-
-type ChatMessage = {
- role: "user" | "assistant";
- content: string;
-};
-
-export function AI() {
- const os = useOpenSecret();
- const [query, setQuery] = useState("");
- const [response, setResponse] = useState("");
- const [loading, setLoading] = useState(false);
-
- const handleSubmit = async (e: FormEvent) => {
- e.preventDefault();
-
- setLoading(true);
- setResponse("");
-
- if (!os.auth.user) {
- alert("Please log in to use the AI chat.");
- return;
- }
-
- const customFetch = os.aiCustomFetch;
-
- if (!query.trim() || loading || !customFetch) return;
-
- try {
- console.log("Starting chat request to URL:", `${os.apiUrl}/v1/`);
-
- const openai = new OpenAI({
- baseURL: `${os.apiUrl}/v1/`,
- dangerouslyAllowBrowser: true,
- apiKey: "api-key-doesnt-matter",
- defaultHeaders: {
- "Accept-Encoding": "identity",
- "Content-Type": "application/json"
- },
- fetch: customFetch
- });
-
- console.log("Created OpenAI client");
-
- const model = "hugging-quants/Meta-Llama-3.1-70B-Instruct-AWQ-INT4";
- const messages = [{ role: "user", content: query } as ChatMessage];
-
- console.log("Starting stream request");
- const stream = await openai.chat.completions.create({
- model,
- messages,
- stream: true
- });
-
- console.log("Stream created successfully");
-
- let fullResponse = "";
- for await (const chunk of stream) {
- console.log("Received chunk:", chunk);
- const content = chunk.choices[0]?.delta?.content || "";
- fullResponse += content;
- setResponse(fullResponse);
- }
- } catch (error) {
- console.error("Chat error:", error);
- if (error instanceof Error) {
- console.error("Error details:", {
- message: error.message,
- stack: error.stack,
- name: error.name
- });
- }
- setResponse("Error: Failed to get response. " + (error as Error).message);
- } finally {
- setLoading(false);
- }
- };
-
- if (!os.auth.user) {
- return Please log in to use the AI chat.
;
- }
-
- return (
-
-
-
- {response && (
-
{response}
- )}
-
- );
-}
diff --git a/src/App.css b/src/App.css
deleted file mode 100644
index 4930879..0000000
--- a/src/App.css
+++ /dev/null
@@ -1,103 +0,0 @@
-#root {
- max-width: 1280px;
- margin: 0 auto;
- padding: 2rem;
-}
-
-.docs-container {
- text-align: left;
- max-width: 800px;
- margin: 0 auto;
-}
-
-section {
- margin-bottom: 2rem;
-}
-
-h2 {
- margin-top: 0;
-}
-
-p {
- margin-bottom: 1.5rem;
-}
-
-.auth-form {
- display: flex;
- flex-direction: column;
- gap: 0.5rem;
- max-width: 300px;
-}
-
-.auth-form input {
- padding: 0.5rem;
- color: inherit;
- background: rgba(127, 127, 127, 0.1);
- border: 1px solid rgba(127, 127, 127, 0.2);
- border-radius: 4px;
-}
-
-button {
- padding: 0.5rem;
-}
-
-.data-display {
- background: rgba(127, 127, 127, 0.1);
- padding: 1rem;
- border-radius: 4px;
- overflow-x: auto;
-}
-
-.data-display pre {
- margin: 0;
- white-space: pre-wrap;
- word-wrap: break-word;
-}
-
-.json-input {
- padding: 0.5rem;
- color: inherit;
- background: rgba(127, 127, 127, 0.1);
- border: 1px solid rgba(127, 127, 127, 0.2);
- border-radius: 4px;
- font-family: monospace;
- resize: vertical;
-}
-
-.api-message {
- margin-top: 0.5rem;
- padding: 0.5rem;
- border-radius: 4px;
- background: rgba(127, 127, 127, 0.1);
-}
-
-.grid-container {
- display: grid;
- grid-template-columns: minmax(min-content, 150px) 1fr minmax(min-content, 120px) minmax(
- min-content,
- 120px
- );
- gap: 1rem;
-}
-
-.grid-header {
- font-weight: bold;
- border-bottom: 1px solid rgba(127, 127, 127, 0.2);
- padding-bottom: 0.5rem;
-}
-
-.grid-item {
- padding: 0.5rem 0;
- border-bottom: 1px solid rgba(127, 127, 127, 0.1);
-}
-
-.grid-item.value {
- max-height: 4.5rem; /* Approx. 4 lines of text */
- overflow-y: auto;
- font-family: monospace;
-}
-
-.grid-item.timestamp {
- font-size: 0.8rem;
- color: rgba(127, 127, 127, 0.7);
-}
diff --git a/src/App.tsx b/src/App.tsx
deleted file mode 100644
index e110afd..0000000
--- a/src/App.tsx
+++ /dev/null
@@ -1,697 +0,0 @@
-import "./App.css";
-import { useState } from "react";
-import React from "react";
-import { useOpenSecret } from "./lib";
-import type { KVListItem } from "./lib";
-import { schnorr, secp256k1 } from "@noble/curves/secp256k1";
-import { AI } from "./AI";
-
-function TokenGenerator() {
- const [audience, setAudience] = useState("http://localhost:3001");
- const [token, setToken] = useState("");
- const [error, setError] = useState("");
- const os = useOpenSecret();
-
- const generateToken = async () => {
- try {
- setError("");
- const response = await os.generateThirdPartyToken(audience);
- setToken(response.token);
- } catch (err) {
- setError(err instanceof Error ? err.message : "Failed to generate token");
- }
- };
-
- return (
-
-
- setAudience(e.target.value)}
- placeholder="Enter audience URL"
- />
- Generate Token
-
- {error && (
-
- {error}
-
- )}
- {token && (
-
-
Generated Token:
-
- navigator.clipboard.writeText(token)}
- style={{ marginTop: "0.5rem" }}
- >
- Copy to Clipboard
-
-
- )}
-
- );
-}
-
-function App() {
- const os = useOpenSecret();
- const [listData, setListData] = useState([]);
- const [algorithm, setAlgorithm] = useState<"schnorr" | "ecdsa">("schnorr");
- const [publicKey, setPublicKey] = useState("");
- const [lastSignature, setLastSignature] = useState<{
- signature: string;
- messageHash: string;
- message: string;
- } | null>(null);
- const [verificationResult, setVerificationResult] = useState(null);
- const [derivationPath, setDerivationPath] = useState("");
-
- const handleSignup = async (e: React.FormEvent) => {
- e.preventDefault();
- const formData = new FormData(e.currentTarget);
- const name = formData.get("name") as string;
- const email = formData.get("email") as string;
- const password = formData.get("password") as string;
- const inviteCode = formData.get("inviteCode") as string;
-
- console.log("Signup request:", { name, email, password, inviteCode });
-
- try {
- const response = await os.signUp(email, password, inviteCode, name);
- console.log("Signup response:", response);
- alert("Signup successful!");
- } catch (error) {
- console.error("Signup error:", error);
- alert("Signup failed: " + (error as Error).message);
- }
- };
-
- const handleGuestSignup = async (e: React.FormEvent) => {
- e.preventDefault();
- const formData = new FormData(e.currentTarget);
- const password = formData.get("password") as string;
- const inviteCode = formData.get("inviteCode") as string;
-
- console.log("Guest Signup request:", { password, inviteCode });
-
- try {
- const response = await os.signUpGuest(password, inviteCode);
- console.log("Guest Signup response:", response);
- alert("Guest Signup successful! Your ID is: " + response.id);
- } catch (error) {
- console.error("Guest Signup error:", error);
- alert("Guest Signup failed: " + (error as Error).message);
- }
- };
-
- const handleGuestLogin = async (e: React.FormEvent) => {
- e.preventDefault();
- const formData = new FormData(e.currentTarget);
- const id = formData.get("id") as string;
- const password = formData.get("password") as string;
-
- console.log("Guest Login request:", { id, password });
-
- try {
- const response = await os.signInGuest(id, password);
- console.log("Guest Login response:", response);
- alert("Guest Login successful!");
- } catch (error) {
- console.error("Guest Login error:", error);
- alert("Guest Login failed: " + (error as Error).message);
- }
- };
-
- const handleGuestConversion = async (e: React.FormEvent) => {
- e.preventDefault();
- const formData = new FormData(e.currentTarget);
- const email = formData.get("email") as string;
- const password = formData.get("password") as string;
- const name = formData.get("name") as string;
-
- console.log("Guest Conversion request:", { email, password, name });
-
- try {
- await os.convertGuestToUserAccount(email, password, name);
- console.log("Guest Conversion successful");
- alert("Account successfully converted to email login!");
- } catch (error) {
- console.error("Guest Conversion error:", error);
- alert("Guest Conversion failed: " + (error as Error).message);
- }
- };
-
- const handleLogin = async (e: React.FormEvent) => {
- e.preventDefault();
- const formData = new FormData(e.currentTarget);
- const email = formData.get("email") as string;
- const password = formData.get("password") as string;
-
- console.log("Login request:", { email, password });
-
- try {
- const response = await os.signIn(email, password);
- console.log("Login response:", response);
- alert("Login successful!");
- } catch (error) {
- console.error("Login error:", error);
- alert("Login failed: " + (error as Error).message);
- }
- };
-
- const handleGet = async (e: React.FormEvent) => {
- e.preventDefault();
- const formData = new FormData(e.currentTarget);
- const key = formData.get("key") as string;
-
- console.log("Get request for key:", key);
-
- try {
- const data = await os.get(key);
- console.log("Get response:", data);
- alert(`Data for key "${key}": ${data}`);
- } catch (error) {
- console.error("Get error:", error);
- alert("Get failed: " + (error as Error).message);
- }
- };
-
- const handlePut = async (e: React.FormEvent) => {
- e.preventDefault();
- const formData = new FormData(e.currentTarget);
- const key = formData.get("key") as string;
- const value = formData.get("value") as string;
-
- console.log("Put request:", { key, value });
-
- try {
- const response = await os.put(key, value);
- console.log("Put response:", response);
- alert("Put successful!");
- } catch (error) {
- console.error("Put error:", error);
- alert("Put failed: " + (error as Error).message);
- }
- };
-
- const handleList = async (e: React.FormEvent) => {
- e.preventDefault();
- console.log("List request");
-
- try {
- const data = await os.list();
- console.log("List response:", data);
- setListData(data);
- } catch (error) {
- console.error("List error:", error);
- alert("List failed: " + (error as Error).message);
- }
- };
-
- const handleLogout = async () => {
- console.log("Logout request");
-
- try {
- await os.signOut();
- console.log("Logout successful");
- } catch (error) {
- console.error("Logout error:", error);
- alert("Logout failed: " + (error as Error).message);
- }
- };
-
- const handleDelete = async (e: React.FormEvent) => {
- e.preventDefault();
- const formData = new FormData(e.currentTarget);
- const key = formData.get("key") as string;
-
- console.log("Delete request for key:", key);
-
- try {
- await os.del(key);
- console.log("Delete successful");
- alert(`Key "${key}" deleted successfully`);
- } catch (error) {
- console.error("Delete error:", error);
- alert("Delete failed: " + (error as Error).message);
- }
- };
-
- const handleSignMessage = async (e: React.FormEvent) => {
- e.preventDefault();
- const formData = new FormData(e.currentTarget);
- const message = formData.get("message") as string;
-
- try {
- const messageBytes = new TextEncoder().encode(message);
- const keyOptions = derivationPath
- ? { private_key_derivation_path: derivationPath }
- : undefined;
- const response = await os.signMessage(messageBytes, algorithm, keyOptions);
-
- setLastSignature({
- signature: response.signature,
- messageHash: response.message_hash,
- message
- });
- setVerificationResult(null); // Reset verification on new signature
- } catch (error) {
- console.error("Failed to sign message:", error);
- alert("Failed to sign message: " + (error as Error).message);
- }
- };
-
- const handleVerifySignature = async () => {
- if (!lastSignature || !publicKey) {
- alert("Please get public key and sign a message first");
- return;
- }
-
- try {
- let isValid: boolean;
- if (algorithm === "schnorr") {
- isValid = schnorr.verify(lastSignature.signature, lastSignature.messageHash, publicKey);
- } else {
- isValid = secp256k1.verify(lastSignature.signature, lastSignature.messageHash, publicKey);
- }
- setVerificationResult(isValid);
- } catch (error) {
- console.error("Verification error:", error);
- alert("Verification failed: " + (error as Error).message);
- }
- };
-
- return (
-
-
- Current User
- Displays the current authenticated user's data from the auth state.
-
- {os.auth.loading ? (
- "Loading..."
- ) : os.auth.user ? (
-
{JSON.stringify(os.auth.user, null, 2)}
- ) : (
- "No user authenticated"
- )}
-
- {os.auth.user && (
-
- Logout
- os.refetchUser()}>Refetch User
-
- )}
-
-
-
- Login
- Sign in to your existing account with email and password.
-
-
-
-
- Sign Up
- Create a new account with name, email, password, and invite code.
-
-
-
-
- Guest Sign Up
- Create a new guest account with just a password and invite code.
-
-
-
-
- Guest Login
- Sign in to your guest account with ID and password.
-
-
-
- {os.auth.user?.user &&
- (os.auth.user.user.email === undefined || os.auth.user.user.email === null) && (
-
- Convert Guest Account
- Convert your guest account to a regular account with email.
-
-
- )}
-
-
- Get Data
- Retrieve string data by key.
-
-
-
-
- Put Data
- Store string data with a key.
-
-
-
-
- List All
- Retrieve all available keys/values in your storage.
-
- List All
-
- {listData.length > 0 && (
-
-
-
Key
-
Value
-
Created At
-
Updated At
- {listData.map((item, index) => (
-
- {item.key}
- {item.value}
-
- {new Date(item.created_at).toLocaleString()}
-
-
- {new Date(item.updated_at).toLocaleString()}
-
-
- ))}
-
-
- )}
-
-
-
-
-
- Private Key
- Retrieve your private key mnemonic phrase. Keep this secure and never share it.
-
- {
- try {
- const response = await os.getPrivateKey();
- alert(
- `Your mnemonic phrase is:\n\n${response.mnemonic}\n\nPlease store this securely and never share it with anyone.`
- );
- } catch (error) {
- console.error("Failed to get private key:", error);
- alert("Failed to get private key: " + (error as Error).message);
- }
- }}
- style={{ marginBottom: "1rem" }}
- >
- Show Private Key Mnemonic
-
-
-
-
Private Key Bytes
-
Get private key bytes for a specific BIP32 derivation path.
-
- Common derivation paths
-
- BIP44 (Legacy): m/44'/0'/0'/0/0
- BIP49 (SegWit): m/49'/0'/0'/0/0
- BIP84 (Native SegWit): m/84'/0'/0'/0/0
- BIP86 (Taproot): m/86'/0'/0'/0/0
-
-
-
- Note: Supports both absolute (starting with "m/") and relative paths. Supports
- hardened derivation using either ' or h notation.
-
-
-
-
- Examples:
-
- Absolute path: "m/44'/0'/0'/0/0"
- Relative path: "0'/0'/0'/0/0"
- Hardened notation: "44'" or "44h"
-
-
-
-
-
{
- e.preventDefault();
- const formData = new FormData(e.currentTarget);
- const derivationPath = formData.get("derivationPath") as string;
-
- try {
- const keyOptions = derivationPath
- ? { private_key_derivation_path: derivationPath }
- : undefined;
- const response = await os.getPrivateKeyBytes(keyOptions);
- alert(`Private key bytes (hex):\n\n${response.private_key}`);
- } catch (error) {
- console.error("Failed to get private key bytes:", error);
- alert("Failed to get private key bytes: " + (error as Error).message);
- }
- }}
- className="auth-form"
- >
-
- Get Private Key Bytes
-
-
-
-
-
- Cryptographic Signing
- Demonstrate message signing and verification using your key pair.
-
-
-
- Algorithm:
- setAlgorithm(e.target.value as "schnorr" | "ecdsa")}
- style={{ marginLeft: "0.5rem" }}
- >
- Schnorr
- ECDSA
-
-
-
-
-
-
- About derivation paths
-
-
- The derivation path determines which private key is used for signing. Different
- paths generate different key pairs from the same master key, useful for separating
- keys by purpose or application.
-
-
-
- Common paths and their uses:
-
-
-
- BIP44 (Legacy): m/44'/0'/0'/0/0 - For legacy Bitcoin addresses
-
-
- BIP49 (SegWit): m/49'/0'/0'/0/0 - For SegWit addresses
-
-
- BIP84 (Native SegWit): m/84'/0'/0'/0/0 - For Native SegWit
-
-
- BIP86 (Taproot): m/86'/0'/0'/0/0 - For Taproot addresses
-
-
-
-
- Path format examples:
-
- Absolute: "m/44'/0'/0'/0/0"
- Relative: "0'/0'/0'/0/0"
- Hardened: "44'" or "44h"
-
- Leave empty to use the master key.
-
-
-
-
setDerivationPath(e.target.value)}
- placeholder="Derivation path (optional)"
- style={{ marginRight: "0.5rem", padding: "0.5rem" }}
- />
-
{
- try {
- const keyOptions = derivationPath
- ? { private_key_derivation_path: derivationPath }
- : undefined;
- const response = await os.getPublicKey(algorithm, keyOptions);
- setPublicKey(response.public_key);
- setVerificationResult(null);
- } catch (error) {
- console.error("Failed to get public key:", error);
- alert("Failed to get public key: " + (error as Error).message);
- }
- }}
- >
- Get Public Key
-
-
-
- {publicKey && (
-
- Public Key: {publicKey}
-
- )}
-
-
-
- About message signing
-
- Messages are converted to bytes before signing. Examples:
-
-
- {`// From string
-const messageBytes = new TextEncoder().encode("Hello, World!");
-
-// From hex
-const messageBytes = new Uint8Array(Buffer.from("deadbeef", "hex"));`}
-
-
-
- Sign Message
-
-
- {lastSignature && (
-
-
- Message: {lastSignature.message}
-
-
- Message Hash: {lastSignature.messageHash}
-
-
- Signature: {lastSignature.signature}
-
-
- Verify Signature
-
- {verificationResult !== null && (
-
- Signature is {verificationResult ? "valid" : "invalid"}!
-
- )}
-
- )}
-
-
-
- AI Chat Demo
- Try out the AI chat functionality with encrypted communication.
-
-
-
-
- Third Party Token
- Generate JWT tokens for authorized third-party services (URL-based).
-
-
-
- );
-}
-
-export default App;
diff --git a/src/index.css b/src/index.css
deleted file mode 100644
index 3236cc6..0000000
--- a/src/index.css
+++ /dev/null
@@ -1,24 +0,0 @@
-:root {
- font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif;
- line-height: 1.5;
- font-weight: 400;
-
- color-scheme: light dark;
- color: rgba(255, 255, 255, 0.87);
- background-color: #242424;
-
- font-synthesis: none;
- text-rendering: optimizeLegibility;
- -webkit-font-smoothing: antialiased;
- -moz-osx-font-smoothing: grayscale;
-}
-
-.token-display {
- margin-top: 20px;
-}
-
-.token-display textarea {
- width: 100%;
- margin: 10px 0;
- padding: 8px;
-}
diff --git a/src/lib/context.ts b/src/lib/context.ts
deleted file mode 100644
index 1b4d9fe..0000000
--- a/src/lib/context.ts
+++ /dev/null
@@ -1,28 +0,0 @@
-import { useContext } from "react";
-import { OpenSecretContext, OpenSecretContextType } from "./main";
-
-/**
- * Hook to access OpenSecret context within the provider
- *
- * @deprecated The useOpenSecret hook is deprecated along with OpenSecretProvider.
- * Instead, import API functions directly and use them without a provider.
- *
- * Migration guide:
- * ```tsx
- * // Old approach (deprecated)
- * const os = useOpenSecret();
- * await os.signIn(email, password);
- *
- * // New approach
- * import { signIn } from '@opensecret/react';
- * await signIn(email, password);
- * ```
- *
- * @returns The OpenSecret context containing auth state and API methods
- * @throws {Error} If used outside of OpenSecretProvider
- */
-export function useOpenSecret(): OpenSecretContextType {
- const context = useContext(OpenSecretContext);
- // React 19 compatibility: Don't check for nullish context since the default value is provided
- return context;
-}
diff --git a/src/lib/developer.tsx b/src/lib/developer.tsx
deleted file mode 100644
index 3d524f0..0000000
--- a/src/lib/developer.tsx
+++ /dev/null
@@ -1,670 +0,0 @@
-import React, { createContext, useState, useEffect } from "react";
-import * as platformApi from "./platformApi";
-import { setPlatformApiUrl } from "./platformApi";
-import { apiConfig } from "./apiConfig";
-import { getAttestation } from "./getAttestation";
-import { authenticate } from "./attestation";
-import {
- parseAttestationForView,
- AWS_ROOT_CERT_DER,
- EXPECTED_ROOT_CERT_HASH,
- ParsedAttestationView
-} from "./attestationForView";
-import type { AttestationDocument } from "./attestation";
-import { PcrConfig } from "./pcr";
-import type {
- Organization,
- Project,
- ProjectSecret,
- ProjectSettings,
- EmailSettings,
- OAuthSettings,
- OrganizationMember,
- PlatformOrg,
- PlatformUser,
- OrganizationInvite
-} from "./platformApi";
-
-export type DeveloperRole = "owner" | "admin" | "developer" | "viewer";
-
-export type OrganizationDetails = Organization;
-
-export type ProjectDetails = Project;
-
-export { type ProjectSettings };
-
-export type DeveloperResponse = PlatformUser & { organizations: PlatformOrg[] };
-
-export type OpenSecretDeveloperAuthState = {
- loading: boolean;
- developer?: DeveloperResponse;
-};
-
-export type OpenSecretDeveloperContextType = {
- auth: OpenSecretDeveloperAuthState;
-
- /**
- * Signs in a developer with email and password
- * @param email - Developer's email address
- * @param password - Developer's password
- * @returns A promise that resolves to the login response with access and refresh tokens
- *
- *
- * - Calls the login API endpoint
- * - Stores access_token and refresh_token in localStorage
- * - Updates the developer state with user information
- * - Throws an error if authentication fails
- */
- signIn: (email: string, password: string) => Promise;
-
- /**
- * Verifies a platform user's email using the verification code
- * @param code - The verification code sent to the user's email
- * @returns A promise that resolves when verification is complete
- * @throws {Error} If verification fails
- *
- *
- * - Takes the verification code from the verification email link
- * - Calls the verification API endpoint
- * - Updates email_verified status if successful
- */
- verifyEmail: typeof platformApi.verifyPlatformEmail;
-
- /**
- * Requests a new verification email for the current user
- * @returns A promise that resolves to a success message
- * @throws {Error} If the user is already verified or request fails
- *
- *
- * - Used when the user needs a new verification email
- * - Requires the user to be authenticated
- * - Sends a new verification email to the user's registered email address
- */
- requestNewVerificationCode: typeof platformApi.requestNewPlatformVerificationCode;
-
- /**
- * Alias for requestNewVerificationCode - for consistency with OpenSecretContext
- */
- requestNewVerificationEmail: typeof platformApi.requestNewPlatformVerificationCode;
-
- /**
- * Initiates the password reset process for a platform developer account
- * @param email - Developer's email address
- * @param hashedSecret - Hashed secret used for additional security verification
- * @returns A promise that resolves when the reset request is successfully processed
- * @throws {Error} If the request fails or the email doesn't exist
- *
- *
- * - Sends a password reset request for a platform developer
- * - The server will send an email with an alphanumeric code
- * - The email and hashed_secret are paired for the reset process
- * - Use confirmPasswordReset to complete the process
- */
- requestPasswordReset: typeof platformApi.requestPlatformPasswordReset;
-
- /**
- * Completes the password reset process for a platform developer account
- * @param email - Developer's email address
- * @param alphanumericCode - Code received via email
- * @param plaintextSecret - The plaintext secret that corresponds to the hashed_secret sent in the request
- * @param newPassword - New password to set
- * @returns A promise that resolves when the password is successfully reset
- * @throws {Error} If the verification fails or the request is invalid
- *
- *
- * - Completes the password reset process using the code from the email
- * - Requires the plaintext_secret that matches the previously sent hashed_secret
- * - Sets the new password if all verification succeeds
- * - The user can then log in with the new password
- */
- confirmPasswordReset: typeof platformApi.confirmPlatformPasswordReset;
-
- /**
- * Changes password for a platform developer account
- * @param currentPassword - Current password for verification
- * @param newPassword - New password to set
- * @returns A promise that resolves when the password is successfully changed
- * @throws {Error} If current password is incorrect or the request fails
- *
- *
- * - Requires the user to be authenticated
- * - Verifies the current password before allowing the change
- * - Updates to the new password if verification succeeds
- */
- changePassword: typeof platformApi.changePlatformPassword;
-
- /**
- * Registers a new developer account
- * @param email - Developer's email address
- * @param password - Developer's password
- * @param invite_code - Required invitation code in UUID format
- * @param name - Optional developer name
- * @returns A promise that resolves to the login response with access and refresh tokens
- *
- *
- * - Calls the registration API endpoint
- * - Stores access_token and refresh_token in localStorage
- * - Updates the developer state with new user information
- * - Throws an error if account creation fails
- */
- signUp: (
- email: string,
- password: string,
- invite_code: string,
- name?: string
- ) => Promise;
-
- /**
- * Signs out the current developer by removing authentication tokens
- *
- *
- * - Calls the logout API endpoint with the current refresh_token
- * - Removes access_token, refresh_token from localStorage
- * - Resets the developer state to show no user is authenticated
- */
- signOut: () => Promise;
-
- /**
- * Refreshes the developer's authentication state
- * @returns A promise that resolves when the refresh is complete
- * @throws {Error} If the refresh fails
- *
- *
- * - Retrieves the latest developer information from the server
- * - Updates the developer state with fresh data
- * - Useful after making changes that affect developer profile or organization membership
- */
- refetchDeveloper: () => Promise;
-
- /**
- * Additional PCR0 hashes to validate against
- */
- pcrConfig: PcrConfig;
-
- /**
- * Gets attestation from the enclave
- */
- getAttestation: typeof getAttestation;
-
- /**
- * Authenticates an attestation document
- */
- authenticate: typeof authenticate;
-
- /**
- * Parses an attestation document for viewing
- */
- parseAttestationForView: (
- document: AttestationDocument,
- cabundle: Uint8Array[],
- pcrConfig?: PcrConfig
- ) => Promise;
-
- /**
- * AWS root certificate in DER format
- */
- awsRootCertDer: typeof AWS_ROOT_CERT_DER;
-
- /**
- * Expected hash of the AWS root certificate
- */
- expectedRootCertHash: typeof EXPECTED_ROOT_CERT_HASH;
-
- /**
- * Gets and verifies an attestation document from the enclave
- * @returns A promise resolving to the parsed attestation document
- * @throws {Error} If attestation fails or is invalid
- *
- *
- * This is a convenience function that:
- * 1. Fetches the attestation document with a random nonce
- * 2. Authenticates the document
- * 3. Parses it for viewing
- */
- getAttestationDocument: () => Promise;
-
- /**
- * Creates a new organization
- * @param name - Organization name
- * @returns A promise that resolves to the created organization
- */
- createOrganization: (name: string) => Promise;
-
- /**
- * Lists all organizations the developer has access to
- * @returns A promise resolving to array of organization details
- */
- listOrganizations: () => Promise;
-
- /**
- * Deletes an organization (requires owner role)
- * @param orgId - Organization ID
- */
- deleteOrganization: (orgId: string) => Promise;
-
- /**
- * Creates a new project within an organization
- * @param orgId - Organization ID
- * @param name - Project name
- * @param description - Optional project description
- * @returns A promise that resolves to the project details including client ID
- */
- createProject: (orgId: string, name: string, description?: string) => Promise;
-
- /**
- * Lists all projects within an organization
- * @param orgId - Organization ID
- * @returns A promise resolving to array of project details
- */
- listProjects: (orgId: string) => Promise;
-
- /**
- * Gets a single project by ID
- * @param orgId - Organization ID
- * @param projectId - Project ID
- * @returns A promise resolving to the project details
- */
- getProject: (orgId: string, projectId: string) => Promise;
-
- /**
- * Updates project details
- * @param orgId - Organization ID
- * @param projectId - Project ID
- * @param updates - Object containing fields to update
- */
- updateProject: (
- orgId: string,
- projectId: string,
- updates: { name?: string; description?: string; status?: string }
- ) => Promise;
-
- /**
- * Deletes a project
- * @param orgId - Organization ID
- * @param projectId - Project ID
- */
- deleteProject: (orgId: string, projectId: string) => Promise;
-
- /**
- * Creates a new secret for a project
- * @param orgId - Organization ID
- * @param projectId - Project ID
- * @param keyName - Secret key name (must be alphanumeric)
- * @param secret - Secret value (must be base64 encoded by the caller)
- *
- * Example:
- * ```typescript
- * // To encode a string secret
- * import { encode } from "@stablelib/base64";
- * const encodedSecret = encode(new TextEncoder().encode("my-secret-value"));
- *
- * // Now pass the encoded secret to the function
- * createProjectSecret(orgId, projectId, "mySecretKey", encodedSecret);
- * ```
- */
- createProjectSecret: (
- orgId: string,
- projectId: string,
- keyName: string,
- secret: string
- ) => Promise;
-
- /**
- * Lists all secrets for a project
- * @param orgId - Organization ID
- * @param projectId - Project ID
- */
- listProjectSecrets: (orgId: string, projectId: string) => Promise;
-
- /**
- * Deletes a project secret
- * @param orgId - Organization ID
- * @param projectId - Project ID
- * @param keyName - Secret key name
- */
- deleteProjectSecret: (orgId: string, projectId: string, keyName: string) => Promise;
-
- /**
- * Gets email configuration for a project
- * @param orgId - Organization ID
- * @param projectId - Project ID
- */
- getEmailSettings: (orgId: string, projectId: string) => Promise;
-
- /**
- * Updates email configuration
- * @param orgId - Organization ID
- * @param projectId - Project ID
- * @param settings - Email settings
- */
- updateEmailSettings: (
- orgId: string,
- projectId: string,
- settings: EmailSettings
- ) => Promise;
-
- /**
- * Gets OAuth settings for a project
- * @param orgId - Organization ID
- * @param projectId - Project ID
- */
- getOAuthSettings: (orgId: string, projectId: string) => Promise;
-
- /**
- * Updates OAuth configuration
- * @param orgId - Organization ID
- * @param projectId - Project ID
- * @param settings - OAuth settings
- */
- updateOAuthSettings: (
- orgId: string,
- projectId: string,
- settings: OAuthSettings
- ) => Promise;
-
- /**
- * Creates an invitation to join an organization
- * @param orgId - Organization ID
- * @param email - Developer's email address
- * @param role - Role to assign (defaults to "admin")
- */
- inviteDeveloper: (orgId: string, email: string, role?: string) => Promise;
-
- /**
- * Lists all members of an organization
- * @param orgId - Organization ID
- */
- listOrganizationMembers: (orgId: string) => Promise;
-
- /**
- * Lists all pending invitations for an organization
- * @param orgId - Organization ID
- */
- listOrganizationInvites: (orgId: string) => Promise;
-
- /**
- * Gets a specific invitation by code
- * @param orgId - Organization ID
- * @param inviteCode - Invitation UUID code
- */
- getOrganizationInvite: (orgId: string, inviteCode: string) => Promise;
-
- /**
- * Deletes an invitation
- * @param orgId - Organization ID
- * @param inviteCode - Invitation UUID code
- */
- deleteOrganizationInvite: (orgId: string, inviteCode: string) => Promise<{ message: string }>;
-
- /**
- * Updates a member's role
- * @param orgId - Organization ID
- * @param userId - User ID to update
- * @param role - New role to assign
- */
- updateMemberRole: (orgId: string, userId: string, role: string) => Promise;
-
- /**
- * Removes a member from the organization
- * @param orgId - Organization ID
- * @param userId - User ID to remove
- */
- removeMember: (orgId: string, userId: string) => Promise;
-
- /**
- * Accepts an organization invitation
- * @param code - Invitation UUID code
- */
- acceptInvite: (code: string) => Promise<{ message: string }>;
-
- /**
- * Returns the current OpenSecret developer API URL being used
- */
- apiUrl: string;
-};
-
-export const OpenSecretDeveloperContext = createContext({
- auth: {
- loading: true,
- developer: undefined
- },
- signIn: async () => {
- throw new Error("signIn called outside of OpenSecretDeveloper provider");
- },
- signUp: async () => {
- throw new Error("signUp called outside of OpenSecretDeveloper provider");
- },
- signOut: async () => {
- throw new Error("signOut called outside of OpenSecretDeveloper provider");
- },
- refetchDeveloper: async () => {
- throw new Error("refetchDeveloper called outside of OpenSecretDeveloper provider");
- },
- verifyEmail: platformApi.verifyPlatformEmail,
- requestNewVerificationCode: platformApi.requestNewPlatformVerificationCode,
- requestNewVerificationEmail: platformApi.requestNewPlatformVerificationCode,
- requestPasswordReset: platformApi.requestPlatformPasswordReset,
- confirmPasswordReset: platformApi.confirmPlatformPasswordReset,
- changePassword: platformApi.changePlatformPassword,
- pcrConfig: {},
- getAttestation,
- authenticate,
- parseAttestationForView,
- awsRootCertDer: AWS_ROOT_CERT_DER,
- expectedRootCertHash: EXPECTED_ROOT_CERT_HASH,
- getAttestationDocument: async () => {
- throw new Error("getAttestationDocument called outside of OpenSecretDeveloper provider");
- },
- createOrganization: platformApi.createOrganization,
- listOrganizations: platformApi.listOrganizations,
- deleteOrganization: platformApi.deleteOrganization,
- createProject: platformApi.createProject,
- listProjects: platformApi.listProjects,
- getProject: platformApi.getProject,
- updateProject: platformApi.updateProject,
- deleteProject: platformApi.deleteProject,
- createProjectSecret: platformApi.createProjectSecret,
- listProjectSecrets: platformApi.listProjectSecrets,
- deleteProjectSecret: platformApi.deleteProjectSecret,
- getEmailSettings: platformApi.getEmailSettings,
- updateEmailSettings: platformApi.updateEmailSettings,
- getOAuthSettings: platformApi.getOAuthSettings,
- updateOAuthSettings: platformApi.updateOAuthSettings,
- inviteDeveloper: platformApi.inviteDeveloper,
- listOrganizationMembers: platformApi.listOrganizationMembers,
- listOrganizationInvites: platformApi.listOrganizationInvites,
- getOrganizationInvite: platformApi.getOrganizationInvite,
- deleteOrganizationInvite: platformApi.deleteOrganizationInvite,
- updateMemberRole: platformApi.updateMemberRole,
- removeMember: platformApi.removeMember,
- acceptInvite: platformApi.acceptInvite,
- apiUrl: ""
-});
-
-/**
- * Provider component for OpenSecret developer operations.
- * This provider is used for managing organizations, projects, and developer access.
- *
- * @param props - Configuration properties for the OpenSecret developer provider
- * @param props.children - React child components to be wrapped by the provider
- * @param props.apiUrl - URL of OpenSecret developer API
- *
- * @example
- * ```tsx
- *
- *
- *
- * ```
- */
-export function OpenSecretDeveloper({
- children,
- apiUrl,
- pcrConfig = {}
-}: {
- children: React.ReactNode;
- apiUrl: string;
- pcrConfig?: PcrConfig;
-}) {
- const [auth, setAuth] = useState({
- loading: true,
- developer: undefined
- });
-
- useEffect(() => {
- if (!apiUrl || apiUrl.trim() === "") {
- throw new Error(
- "OpenSecretDeveloper requires a non-empty apiUrl. Please provide a valid API endpoint URL."
- );
- }
- setPlatformApiUrl(apiUrl);
- apiConfig.configurePlatform(apiUrl);
- }, [apiUrl]);
-
- async function fetchDeveloper() {
- const access_token = window.localStorage.getItem("access_token");
- const refresh_token = window.localStorage.getItem("refresh_token");
- if (!access_token || !refresh_token) {
- setAuth({
- loading: false,
- developer: undefined
- });
- return;
- }
-
- try {
- const response = await platformApi.platformMe();
- setAuth({
- loading: false,
- developer: {
- ...response.user,
- organizations: response.organizations
- }
- });
- } catch (error) {
- console.error("Failed to fetch developer:", error);
- setAuth({
- loading: false,
- developer: undefined
- });
- }
- }
-
- const getAttestationDocument = async () => {
- const nonce = window.crypto.randomUUID();
- const response = await fetch(`${apiUrl}/attestation/${nonce}`);
- if (!response.ok) {
- throw new Error("Failed to fetch attestation document");
- }
-
- const data = await response.json();
- const verifiedDocument = await authenticate(
- data.attestation_document,
- AWS_ROOT_CERT_DER,
- nonce
- );
- return parseAttestationForView(verifiedDocument, verifiedDocument.cabundle, pcrConfig);
- };
-
- useEffect(() => {
- fetchDeveloper();
- }, []);
-
- async function signIn(email: string, password: string) {
- try {
- const { access_token, refresh_token } = await platformApi.platformLogin(email, password);
- window.localStorage.setItem("access_token", access_token);
- window.localStorage.setItem("refresh_token", refresh_token);
- await fetchDeveloper();
- return { access_token, refresh_token, id: "", email };
- } catch (error) {
- console.error("Login error:", error);
- throw error;
- }
- }
-
- async function signUp(email: string, password: string, invite_code: string, name?: string) {
- try {
- const { access_token, refresh_token } = await platformApi.platformRegister(
- email,
- password,
- invite_code,
- name
- );
- window.localStorage.setItem("access_token", access_token);
- window.localStorage.setItem("refresh_token", refresh_token);
- await fetchDeveloper();
- return { access_token, refresh_token, id: "", email, name };
- } catch (error) {
- console.error("Registration error:", error);
- throw error;
- }
- }
-
- const value: OpenSecretDeveloperContextType = {
- auth,
- signIn,
- signUp,
- refetchDeveloper: fetchDeveloper,
- signOut: async () => {
- const refresh_token = window.localStorage.getItem("refresh_token");
- if (refresh_token) {
- try {
- await platformApi.platformLogout(refresh_token);
- } catch (error) {
- console.error("Error during logout:", error);
- }
- }
- localStorage.removeItem("access_token");
- localStorage.removeItem("refresh_token");
- setAuth({
- loading: false,
- developer: undefined
- });
- },
- verifyEmail: platformApi.verifyPlatformEmail,
- requestNewVerificationCode: platformApi.requestNewPlatformVerificationCode,
- requestNewVerificationEmail: platformApi.requestNewPlatformVerificationCode,
- requestPasswordReset: platformApi.requestPlatformPasswordReset,
- confirmPasswordReset: platformApi.confirmPlatformPasswordReset,
- changePassword: platformApi.changePlatformPassword,
- pcrConfig,
- getAttestation,
- authenticate,
- parseAttestationForView,
- awsRootCertDer: AWS_ROOT_CERT_DER,
- expectedRootCertHash: EXPECTED_ROOT_CERT_HASH,
- getAttestationDocument,
- createOrganization: platformApi.createOrganization,
- listOrganizations: platformApi.listOrganizations,
- deleteOrganization: platformApi.deleteOrganization,
- createProject: platformApi.createProject,
- listProjects: platformApi.listProjects,
- getProject: platformApi.getProject,
- updateProject: platformApi.updateProject,
- deleteProject: platformApi.deleteProject,
- createProjectSecret: platformApi.createProjectSecret,
- listProjectSecrets: platformApi.listProjectSecrets,
- deleteProjectSecret: platformApi.deleteProjectSecret,
- getEmailSettings: platformApi.getEmailSettings,
- updateEmailSettings: platformApi.updateEmailSettings,
- getOAuthSettings: platformApi.getOAuthSettings,
- updateOAuthSettings: platformApi.updateOAuthSettings,
- inviteDeveloper: platformApi.inviteDeveloper,
- listOrganizationMembers: platformApi.listOrganizationMembers,
- listOrganizationInvites: platformApi.listOrganizationInvites,
- getOrganizationInvite: platformApi.getOrganizationInvite,
- deleteOrganizationInvite: platformApi.deleteOrganizationInvite,
- updateMemberRole: platformApi.updateMemberRole,
- removeMember: platformApi.removeMember,
- acceptInvite: platformApi.acceptInvite,
- apiUrl
- };
-
- return (
-
- {children}
-
- );
-}
diff --git a/src/lib/developerContext.ts b/src/lib/developerContext.ts
deleted file mode 100644
index 3298909..0000000
--- a/src/lib/developerContext.ts
+++ /dev/null
@@ -1,8 +0,0 @@
-import { useContext } from "react";
-import { OpenSecretDeveloperContext, OpenSecretDeveloperContextType } from "./developer";
-
-export function useOpenSecretDeveloper(): OpenSecretDeveloperContextType {
- const context = useContext(OpenSecretDeveloperContext);
- // React 19 compatibility: Don't check for nullish context since the default value is provided
- return context;
-}
diff --git a/src/lib/index.ts b/src/lib/index.ts
index f74da29..f07f6e0 100644
--- a/src/lib/index.ts
+++ b/src/lib/index.ts
@@ -127,24 +127,6 @@ export {
EXPECTED_ROOT_CERT_HASH as expectedRootCertHash
} from "./attestationForView";
-// Export the provider and context
-export { OpenSecretProvider, OpenSecretContext } from "./main";
-export { OpenSecretDeveloper, OpenSecretDeveloperContext } from "./developer";
-
-// Export the hooks
-export { useOpenSecret } from "./context";
-export { useOpenSecretDeveloper } from "./developerContext";
-
-// Export types needed by consumers
-export type { OpenSecretAuthState, OpenSecretContextType } from "./main";
-export type {
- OpenSecretDeveloperAuthState,
- OpenSecretDeveloperContextType,
- DeveloperRole,
- OrganizationDetails,
- ProjectDetails,
- ProjectSettings
-} from "./developer";
export type { AttestationDocument } from "./attestation";
export type { ParsedAttestationView } from "./attestationForView";
export type { PcrConfig, Pcr0ValidationResult } from "./pcr";
diff --git a/src/lib/main.tsx b/src/lib/main.tsx
deleted file mode 100644
index a1418cd..0000000
--- a/src/lib/main.tsx
+++ /dev/null
@@ -1,1324 +0,0 @@
-import React, { createContext, useState, useEffect } from "react";
-import * as api from "./api";
-import { createCustomFetch } from "./ai";
-import { getAttestation } from "./getAttestation";
-import type { Model } from "openai/resources/models.js";
-import { authenticate } from "./attestation";
-import {
- parseAttestationForView,
- AWS_ROOT_CERT_DER,
- EXPECTED_ROOT_CERT_HASH,
- ParsedAttestationView
-} from "./attestationForView";
-import type { AttestationDocument } from "./attestation";
-import type { LoginResponse, ThirdPartyTokenResponse, DocumentResponse } from "./api";
-import { PcrConfig } from "./pcr";
-import { configure } from "./config";
-
-export type OpenSecretAuthState = {
- loading: boolean;
- user?: api.UserResponse;
-};
-
-export type OpenSecretContextType = {
- auth: OpenSecretAuthState;
-
- /**
- * The client ID for this project/tenant.
- * A UUID that identifies which project/tenant this instance belongs to.
- */
- clientId: string;
-
- /**
- * Optional API key for OpenAI endpoints.
- * When set, this will be used instead of JWT for /v1/* endpoints.
- */
- apiKey?: string;
-
- /**
- * Sets the API key to use for OpenAI endpoints.
- * @param key - The API key (UUID format) or undefined to clear
- */
- setApiKey: (key: string | undefined) => void;
-
- /**
- * Authenticates a user with email and password.
- *
- * - Calls the login API endpoint with the configured clientId
- * - Stores access_token and refresh_token in localStorage
- * - Updates the auth state with user information
- * - Throws an error if authentication fails
- *
- * @param email - User's email address
- * @param password - User's password
- * @returns A promise that resolves when authentication is complete
- * @throws {Error} If login fails
- */
- signIn: (email: string, password: string) => Promise;
-
- /**
- * Creates a new user account
- * @param email - User's email address
- * @param password - User's chosen password
- * @param inviteCode - Invitation code for registration
- * @param name - Optional user's full name
- * @returns A promise that resolves when account creation is complete
- * @throws {Error} If signup fails
- *
- *
- * - Calls the registration API endpoint
- * - Stores access_token and refresh_token in localStorage
- * - Updates the auth state with new user information
- * - Throws an error if account creation fails
- */
- signUp: (email: string, password: string, inviteCode: string, name?: string) => Promise;
-
- /**
- * Authenticates a guest user with user id and password
- * @param id - User's unique id
- * @param password - User's password
- * @returns A promise that resolves when authentication is complete
- * @throws {Error} If login fails
- *
- *
- * - Calls the login API endpoint
- * - Stores access_token and refresh_token in localStorage
- * - Updates the auth state with user information
- * - Throws an error if authentication fails
- */
- signInGuest: (id: string, password: string) => Promise;
-
- /**
- * Creates a new guest account, which can be upgraded to a normal account later with email.
- * @param password - User's chosen password, cannot be changed or recovered without adding email address.
- * @param inviteCode - Invitation code for registration
- * @returns A promise that resolves to the login response containing the guest ID
- * @throws {Error} If signup fails
- *
- *
- * - Calls the registration API endpoint
- * - Stores access_token and refresh_token in localStorage
- * - Updates the auth state with new user information
- * - Throws an error if account creation fails
- */
- signUpGuest: (password: string, inviteCode: string) => Promise;
-
- /**
- * Upgrades a guest account to a user account with email and password authentication.
- * @param email - User's email address
- * @param password - User's chosen password
- * @param name - Optional user's full name
- * @returns A promise that resolves when account creation is complete
- * @throws {Error} If:
- * - The current user is not a guest account
- * - The email address is already in use
- * - The user is not authenticated
- *
- *
- * - Upgrades the currently signed-in guest account (identified by their UUID) to a full email account
- * - Requires the user to be currently authenticated as a guest
- * - Updates the auth state with new user information
- * - Preserves all existing data associated with the guest account
- */
- convertGuestToUserAccount: (
- email: string,
- password: string,
- name?: string | null
- ) => Promise;
-
- /**
- * Logs out the current user
- * @returns A promise that resolves when logout is complete
- * @throws {Error} If logout fails
- *
- *
- * - Calls the logout API endpoint with the current refresh_token
- * - Removes access_token, refresh_token from localStorage
- * - Removes session-related items from sessionStorage
- * - Resets the auth state to show no user is authenticated
- */
- signOut: () => Promise;
-
- /**
- * Retrieves a value from key-value storage
- * @param key - The unique identifier for the stored value
- * @returns A promise resolving to the stored value
- * @throws {Error} If the key cannot be retrieved
- *
- *
- * - Calls the authenticated API endpoint to fetch a value
- * - Returns undefined if the key does not exist
- * - Requires an active authentication session
- * - Logs any retrieval errors
- */
- get: typeof api.fetchGet;
-
- /**
- * Stores a key-value pair in the user's storage
- * @param key - The unique identifier for the value
- * @param value - The string value to be stored
- * @returns A promise resolving to the server's response
- * @throws {Error} If the value cannot be stored
- *
- *
- * - Calls the authenticated API endpoint to store a value
- * - Requires an active authentication session
- * - Overwrites any existing value for the given key
- * - Logs any storage errors
- */
- put: typeof api.fetchPut;
-
- /**
- * Retrieves all key-value pairs stored by the user
- * @returns A promise resolving to an array of stored items
- * @throws {Error} If the list cannot be retrieved
- *
- *
- * - Calls the authenticated API endpoint to fetch all stored items
- * - Returns an array of key-value pairs with metadata
- * - Requires an active authentication session
- * - Each item includes key, value, creation, and update timestamps
- * - Logs any listing errors
- */
- list: typeof api.fetchList;
-
- /**
- * Deletes a key-value pair from the user's storage
- * @param key - The unique identifier for the value to be deleted
- * @returns A promise resolving when the deletion is complete
- * @throws {Error} If the key cannot be deleted
- *
- *
- * - Calls the authenticated API endpoint to remove a specific key
- * - Requires an active authentication session
- * - Throws an error if the deletion fails (including for non-existent keys)
- * - Propagates any server-side errors directly
- */
- del: typeof api.fetchDelete;
-
- /**
- * Deletes all key-value pairs from the user's storage
- * @returns A promise resolving when the deletion is complete
- * @throws {Error} If the deletion fails
- */
- delAll: typeof api.fetchDeleteAllKV;
-
- verifyEmail: typeof api.verifyEmail;
- requestNewVerificationCode: typeof api.requestNewVerificationCode;
- requestNewVerificationEmail: typeof api.requestNewVerificationCode;
- refetchUser: () => Promise;
- changePassword: typeof api.changePassword;
- refreshAccessToken: typeof api.refreshToken;
- requestPasswordReset: (email: string, hashedSecret: string) => Promise;
- confirmPasswordReset: (
- email: string,
- alphanumericCode: string,
- plaintextSecret: string,
- newPassword: string
- ) => Promise;
- /**
- * Initiates the account deletion process for logged-in users
- * @param hashedSecret - Client-side hashed secret for verification
- * @returns A promise resolving to void
- * @throws {Error} If request fails
- *
- * This function:
- * 1. Requires the user to be logged in (uses authenticatedApiCall)
- * 2. Sends a verification email to the user's email address
- * 3. The email contains a confirmation code that will be needed for confirmation
- * 4. The client must store the plaintext secret for confirmation
- */
- requestAccountDeletion: (hashedSecret: string) => Promise;
-
- /**
- * Confirms and completes the account deletion process
- * @param confirmationCode - The confirmation code from the verification email
- * @param plaintextSecret - The plaintext secret that was hashed in the request step
- * @returns A promise resolving to void
- * @throws {Error} If confirmation fails
- *
- * This function:
- * 1. Requires the user to be logged in (uses authenticatedApiCall)
- * 2. Verifies both the confirmation code from email and the secret known only to the client
- * 3. Permanently deletes the user account and all associated data
- * 4. After successful deletion, the client should clear all local storage and tokens
- */
- confirmAccountDeletion: (confirmationCode: string, plaintextSecret: string) => Promise;
- initiateGitHubAuth: (inviteCode: string) => Promise;
- handleGitHubCallback: (code: string, state: string, inviteCode: string) => Promise;
- initiateGoogleAuth: (inviteCode: string) => Promise;
- handleGoogleCallback: (code: string, state: string, inviteCode: string) => Promise;
- initiateAppleAuth: (inviteCode: string) => Promise;
- handleAppleCallback: (code: string, state: string, inviteCode: string) => Promise;
- handleAppleNativeSignIn: (appleUser: api.AppleUser, inviteCode?: string) => Promise;
-
- /**
- * Retrieves the user's private key mnemonic phrase
- * @param options - Optional key derivation options
- * @returns A promise resolving to the private key response
- * @throws {Error} If the private key cannot be retrieved
- *
- *
- * This function supports two modes:
- *
- * 1. Master mnemonic (no parameters)
- * - Returns the user's master 12-word BIP39 mnemonic
- *
- * 2. BIP-85 derived mnemonic
- * - Derives a child mnemonic using BIP-85
- * - Requires seed_phrase_derivation_path in options
- * - Example: "m/83696968'/39'/0'/12'/0'"
- */
- getPrivateKey: typeof api.fetchPrivateKey;
-
- /**
- * Retrieves the private key bytes for the given derivation options
- * @param options - Optional key derivation options or legacy BIP32 derivation path string
- * @returns A promise resolving to the private key bytes response
- * @throws {Error} If:
- * - The private key bytes cannot be retrieved
- * - The derivation paths are invalid
- *
- *
- * This function supports multiple derivation approaches:
- *
- * 1. Master key only (no parameters)
- * - Returns the master private key bytes
- *
- * 2. BIP-32 derivation only
- * - Uses a single derivation path to derive a child key from the master seed
- * - Supports both absolute and relative paths with hardened derivation:
- * - Absolute path: "m/44'/0'/0'/0/0"
- * - Relative path: "0'/0'/0'/0/0"
- * - Hardened notation: "44'" or "44h"
- * - Common paths:
- * - BIP44 (Legacy): m/44'/0'/0'/0/0
- * - BIP49 (SegWit): m/49'/0'/0'/0/0
- * - BIP84 (Native SegWit): m/84'/0'/0'/0/0
- * - BIP86 (Taproot): m/86'/0'/0'/0/0
- *
- * 3. BIP-85 derivation only
- * - Derives a child mnemonic from the master seed using BIP-85
- * - Then returns the master private key of that derived seed
- * - Example path: "m/83696968'/39'/0'/12'/0'"
- *
- * 4. Combined BIP-85 and BIP-32 derivation
- * - First derives a child mnemonic via BIP-85
- * - Then applies BIP-32 derivation to that derived seed
- */
- getPrivateKeyBytes: typeof api.fetchPrivateKeyBytes;
-
- /**
- * Retrieves the user's public key for the specified algorithm
- * @param algorithm - The signing algorithm ('schnorr' or 'ecdsa')
- * @param options - Optional key derivation options or legacy BIP32 derivation path string
- * @returns A promise resolving to the public key response
- * @throws {Error} If the public key cannot be retrieved
- *
- *
- * The derivation paths determine which key is used to generate the public key:
- *
- * 1. Master key (no derivation parameters)
- * - Returns the public key corresponding to the master private key
- *
- * 2. BIP-32 derived key
- * - Returns the public key for a derived child key
- *
- * 3. BIP-85 derived key
- * - Returns the public key for the master key of a BIP-85 derived seed
- *
- * 4. Combined BIP-85 and BIP-32 derivation
- * - First derives a child mnemonic via BIP-85
- * - Then applies BIP-32 derivation to get the corresponding public key
- */
- getPublicKey: typeof api.fetchPublicKey;
-
- /**
- * Signs a message using the specified algorithm.
- * This function supports multiple signing approaches: master key (no derivation),
- * BIP-32 derived key, BIP-85 derived key, or combined BIP-85 and BIP-32 derivation.
- *
- * @param messageBytes - The message to sign as a Uint8Array
- * @param algorithm - The signing algorithm ('schnorr' or 'ecdsa')
- * @param options - Optional key derivation options or legacy BIP32 derivation path string
- * @returns A promise resolving to the signature response
- * @throws {Error} If the message signing fails
- */
- signMessage: typeof api.signMessage;
-
- /**
- * Custom fetch function for AI requests that handles encryption
- * and token refreshing.
- *
- * Meant to be used with the OpenAI JS library
- *
- * Example:
- * ```tsx
- * const openai = new OpenAI({
- * baseURL: `${os.apiUrl}/v1/`,
- * dangerouslyAllowBrowser: true,
- * apiKey: "the-api-key-doesnt-matter",
- * defaultHeaders: {
- * "Accept-Encoding": "identity"
- * },
- * fetch: os.aiCustomFetch
- * });
- * ```
- */
- aiCustomFetch: (input: string | URL | Request, init?: RequestInit) => Promise;
-
- /**
- * Returns the current OpenSecret enclave API URL being used
- * @returns The current API URL
- */
- apiUrl: string;
-
- /**
- * Additional PCR0 hashes to validate against
- */
- pcrConfig: PcrConfig;
-
- /**
- * Gets attestation from the enclave
- */
- getAttestation: typeof getAttestation;
-
- /**
- * Authenticates an attestation document
- */
- authenticate: typeof authenticate;
-
- /**
- * Parses an attestation document for viewing
- */
- parseAttestationForView: (
- document: AttestationDocument,
- cabundle: Uint8Array[],
- pcrConfig?: PcrConfig
- ) => Promise;
-
- /**
- * AWS root certificate in DER format
- */
- awsRootCertDer: typeof AWS_ROOT_CERT_DER;
-
- /**
- * Expected hash of the AWS root certificate
- */
- expectedRootCertHash: typeof EXPECTED_ROOT_CERT_HASH;
-
- /**
- * Gets and verifies an attestation document from the enclave
- * @returns A promise resolving to the parsed attestation document
- * @throws {Error} If attestation fails or is invalid
- *
- *
- * This is a convenience function that:
- * 1. Fetches the attestation document with a random nonce
- * 2. Authenticates the document
- * 3. Parses it for viewing
- */
- getAttestationDocument: () => Promise;
-
- /**
- * Generates a JWT token for use with third-party services
- * @param audience - Optional URL of the service (e.g. "https://billing.opensecret.cloud")
- * @returns A promise resolving to the token response
- * @throws {Error} If:
- * - The user is not authenticated
- * - The audience URL is invalid (if provided)
- *
- *
- * - Generates a signed JWT token for use with third-party services
- * - If audience is provided, it can be any valid URL
- * - If audience is omitted, a token with no audience restriction will be generated
- * - Requires an active authentication session
- * - Token can be used to authenticate with the specified service
- */
- generateThirdPartyToken: (audience?: string) => Promise;
-
- /**
- * Encrypts arbitrary string data using the user's private key
- * @param data - String content to be encrypted
- * @param options - Optional key derivation options or legacy BIP32 derivation path string
- * @returns A promise resolving to the encrypted data response
- * @throws {Error} If:
- * - The derivation paths are invalid
- * - Authentication fails
- * - Server-side encryption error occurs
- *
- *
- * This function supports multiple encryption approaches:
- *
- * 1. Encrypt with master key (no derivation parameters)
- *
- * 2. Encrypt with BIP-32 derived key
- * - Derives a child key from the master seed using BIP-32
- * - Example: "m/44\'/0\'/0\'/0/0"
- *
- * 3. Encrypt with BIP-85 derived key
- * - Derives a child mnemonic using BIP-85, then uses its master key
- * - Example: { seed_phrase_derivation_path: "m/83696968\'/39\'/0\'/12\'/0\'" }
- *
- * 4. Encrypt with combined BIP-85 and BIP-32 derivation
- * - First derives a child mnemonic via BIP-85
- * - Then applies BIP-32 derivation to derive a key from that seed
- * - Example: {
- * seed_phrase_derivation_path: "m/83696968\'/39\'/0\'/12\'/0\'",
- * private_key_derivation_path: "m/44\'/0\'/0\'/0/0"
- * }
- *
- * Technical details:
- * - Encrypts data with AES-256-GCM
- * - A random nonce is generated for each encryption operation (included in the result)
- * - The encrypted_data format includes the nonce and is base64-encoded
- */
- encryptData: typeof api.encryptData;
-
- /**
- * Decrypts data that was previously encrypted with the user's key
- * @param encryptedData - Base64-encoded encrypted data string
- * @param options - Optional key derivation options or legacy BIP32 derivation path string
- * @returns A promise resolving to the decrypted string
- * @throws {Error} If:
- * - The encrypted data is malformed
- * - The derivation paths are invalid
- * - Authentication fails
- * - Server-side decryption error occurs
- *
- *
- * This function supports multiple decryption approaches:
- *
- * 1. Decrypt with master key (no derivation parameters)
- *
- * 2. Decrypt with BIP-32 derived key
- * - Derives a child key from the master seed using BIP-32
- *
- * 3. Decrypt with BIP-85 derived key
- * - Derives a child mnemonic using BIP-85, then uses its master key
- *
- * 4. Decrypt with combined BIP-85 and BIP-32 derivation
- * - First derives a child mnemonic via BIP-85
- * - Then applies BIP-32 derivation to derive a key from that seed
- *
- * IMPORTANT: You must use the exact same derivation options for decryption
- * that were used for encryption.
- */
- decryptData: typeof api.decryptData;
-
- /**
- * Fetches available AI models from the OpenAI-compatible API
- * @returns A promise resolving to an array of Model objects
- * @throws {Error} If:
- * - The user is not authenticated
- * - The request fails
- *
- *
- * - Returns a list of available AI models from the configured OpenAI-compatible API
- * - Response is encrypted and automatically decrypted
- * - Guest users will receive a 401 Unauthorized error
- * - Requires an active authentication session
- */
- fetchModels: () => Promise;
-
- /**
- * Uploads a document for text extraction and processing
- * @param file - The file to upload (File or Blob object)
- * @returns A promise resolving to the task ID and initial metadata
- * @throws {Error} If:
- * - The file exceeds 10MB size limit
- * - The user is not authenticated (or is a guest user)
- * - Usage limits are exceeded (403)
- * - Processing fails (500)
- *
- * @description
- * This function uploads a document to the Tinfoil processing service which:
- * 1. Accepts the document and returns a task ID immediately
- * 2. Processes the document asynchronously in the background
- * 3. Maintains end-to-end encryption using session keys
- *
- * Common supported formats include PDF, DOCX, XLSX, PPTX, TXT, RTF, and more.
- * Guest users will receive a 401 Unauthorized error.
- *
- * Example usage:
- * ```typescript
- * const file = new File(["content"], "document.pdf", { type: "application/pdf" });
- * const result = await context.uploadDocument(file);
- * console.log(result.task_id); // Task ID to check status
- * ```
- */
- uploadDocument: (file: File | Blob) => Promise;
-
- /**
- * Checks the status of a document processing task
- * @param taskId - The task ID returned from uploadDocument
- * @returns A promise resolving to the current status and optionally the processed document
- * @throws {Error} If:
- * - The user is not authenticated
- * - The task ID is not found (404)
- * - The user doesn't have access to the task (403)
- *
- * @description
- * This function checks the status of an async document processing task.
- * Status values include:
- * - "pending": Document is queued for processing
- * - "started": Document processing has begun
- * - "success": Processing completed successfully (document field will be populated)
- * - "failure": Processing failed (error field will contain details)
- *
- * Example usage:
- * ```typescript
- * const status = await context.checkDocumentStatus(taskId);
- * if (status.status === "success" && status.document) {
- * console.log(status.document.text);
- * }
- * ```
- */
- checkDocumentStatus: (taskId: string) => Promise;
-
- /**
- * Uploads a document and polls for completion
- * @param file - The file to upload (File or Blob object)
- * @param options - Optional configuration for polling behavior
- * @returns A promise resolving to the processed document
- * @throws {Error} If:
- * - Upload fails (see uploadDocument errors)
- * - Processing fails (error from server)
- * - Processing times out (exceeds maxAttempts)
- *
- * @description
- * This is a convenience function that combines uploadDocument and checkDocumentStatus
- * to provide a simple interface that handles the async processing automatically.
- *
- * Options:
- * - pollInterval: Time between status checks in milliseconds (default: 2000)
- * - maxAttempts: Maximum number of status checks before timeout (default: 150 = 5 minutes)
- * - onProgress: Callback function called on each status update
- *
- * Example usage:
- * ```typescript
- * const file = new File(["content"], "document.pdf", { type: "application/pdf" });
- * const result = await context.uploadDocumentWithPolling(file, {
- * onProgress: (status, progress) => {
- * console.log(`Status: ${status}, Progress: ${progress || 0}%`);
- * }
- * });
- * console.log(result.text);
- * ```
- */
- uploadDocumentWithPolling: (
- file: File | Blob,
- options?: {
- pollInterval?: number;
- maxAttempts?: number;
- onProgress?: (status: string, progress?: number) => void;
- }
- ) => Promise;
-
- /**
- * Creates a new API key for the authenticated user
- * @param name - A descriptive name for the API key
- * @returns A promise resolving to the API key details with the key value (only shown once)
- * @throws {Error} If the user is not authenticated or the request fails
- *
- * IMPORTANT: The `key` field is only returned once during creation and cannot be retrieved again.
- * The SDK consumer should prompt users to save the key immediately.
- */
- createApiKey: typeof api.createApiKey;
-
- /**
- * Lists all API keys for the authenticated user
- * @returns A promise resolving to an object containing an array of API key metadata (without the actual keys)
- * @throws {Error} If the user is not authenticated or the request fails
- *
- * Returns metadata about all API keys associated with the user's account.
- * Note that the actual key values are never returned - they are only shown once during creation.
- * The keys are sorted by created_at in descending order (newest first).
- */
- listApiKeys: typeof api.listApiKeys;
-
- /**
- * Deletes an API key by its name
- * @param name - The name of the API key to delete
- * @returns A promise that resolves when the key is deleted
- * @throws {Error} If the user is not authenticated or the API key is not found
- *
- * Permanently deletes an API key. This action cannot be undone.
- * Any requests using the deleted key will immediately fail with 401 Unauthorized.
- * Names are unique per user, so this uniquely identifies the key to delete.
- */
- deleteApiKey: typeof api.deleteApiKey;
-
- /**
- * Transcribes audio using the Whisper API
- * @param file - The audio file to transcribe (File or Blob object)
- * @param options - Optional transcription parameters
- * @returns A promise resolving to the transcription response
- * @throws {Error} If the user is not authenticated or transcription fails
- *
- * @description
- * This function transcribes audio using OpenAI's Whisper model via the encrypted API.
- *
- * Options:
- * - model: Model to use (default: "whisper-large-v3", routes to Tinfoil's whisper-large-v3-turbo)
- * - language: Optional ISO-639-1 language code (e.g., "en", "es", "fr")
- * - prompt: Optional context or previous segment transcript
- * - temperature: Sampling temperature between 0 and 1 (default: 0.0)
- *
- * Supported audio formats: MP3, WAV, MP4, M4A, FLAC, OGG, WEBM
- *
- * Example usage:
- * ```typescript
- * const audioFile = new File([audioData], "recording.mp3", { type: "audio/mpeg" });
- * const result = await context.transcribeAudio(audioFile, {
- * language: "en",
- * prompt: "This is a technical discussion about AI"
- * });
- * console.log(result.text);
- * ```
- */
- transcribeAudio: typeof api.transcribeAudio;
-
- /**
- * Lists user's responses with pagination
- * @param params - Optional parameters for pagination and filtering
- * @returns A promise resolving to a paginated list of responses
- * @throws {Error} If:
- * - The user is not authenticated
- * - The request fails
- * - Invalid pagination parameters
- *
- * @description
- * This function fetches a paginated list of the user's responses.
- * In list view, the usage and output fields are always null for performance reasons.
- *
- * Query Parameters:
- * - limit: Number of results per page (1-100, default: 20)
- * - after: UUID cursor for forward pagination
- * - before: UUID cursor for backward pagination
- * - order: Sort order (currently not implemented, reserved for future use)
- *
- * Pagination Examples:
- * ```typescript
- * // First page
- * const responses = await context.fetchResponsesList({ limit: 20 });
- *
- * // Next page
- * const nextPage = await context.fetchResponsesList({
- * limit: 20,
- * after: responses.last_id
- * });
- *
- * // Previous page
- * const prevPage = await context.fetchResponsesList({
- * limit: 20,
- * before: responses.first_id
- * });
- * ```
- */
- fetchResponsesList: (params?: api.ResponsesListParams) => Promise;
-
- /**
- * Retrieves a single response by ID
- * @param responseId - The UUID of the response to retrieve
- * @returns A promise resolving to the response details
- */
- fetchResponse: (responseId: string) => Promise;
-
- /**
- * Cancels an in-progress response
- * @param responseId - The UUID of the response to cancel
- * @returns A promise resolving to the cancelled response
- */
- cancelResponse: (responseId: string) => Promise;
-
- /**
- * Deletes a response permanently
- * @param responseId - The UUID of the response to delete
- * @returns A promise resolving to deletion confirmation
- */
- deleteResponse: (responseId: string) => Promise;
-
- /**
- * Creates a new response with conversation support
- * @param request - The request parameters for creating a response
- * @returns A promise resolving to the created response or a stream
- */
- createResponse: (request: api.ResponsesCreateRequest) => Promise;
-
- /**
- * Lists all conversations with pagination (non-standard endpoint)
- * @param params - Optional pagination parameters
- * @returns A promise resolving to a paginated list of conversations
- * @description
- * This is a custom extension not part of the standard OpenAI API.
- * For standard conversation operations, use the OpenAI client directly:
- * - openai.conversations.create()
- * - openai.conversations.retrieve()
- * - openai.conversations.update()
- * - openai.conversations.delete()
- * - openai.conversations.items.list()
- * - openai.conversations.items.retrieve()
- */
- listConversations: (params?: {
- limit?: number;
- after?: string;
- before?: string;
- }) => Promise;
-
- /**
- * Deletes all conversations
- * @returns A promise resolving to deletion confirmation
- */
- deleteConversations: typeof api.deleteConversations;
-
- /**
- * Batch deletes multiple conversations by their IDs
- * @param ids - Array of conversation UUIDs to delete
- * @returns A promise resolving to per-item deletion results
- */
- batchDeleteConversations: typeof api.batchDeleteConversations;
-
- /**
- * Creates a new instruction
- * @param request - The instruction creation parameters
- * @returns A promise resolving to the created instruction
- * @throws {Error} If the user is not authenticated or the request fails
- *
- * @description
- * Creates a new user instruction (system prompt).
- * If is_default is set to true, all other instructions are automatically set to is_default: false.
- * The prompt_tokens field is automatically calculated.
- */
- createInstruction: typeof api.createInstruction;
-
- /**
- * Lists user's instructions with pagination
- * @param params - Optional parameters for pagination and ordering
- * @returns A promise resolving to a paginated list of instructions
- * @throws {Error} If the user is not authenticated or the request fails
- *
- * @description
- * Fetches a paginated list of the user's instructions.
- * Results are ordered by updated_at by default (most recently updated first).
- */
- listInstructions: typeof api.listInstructions;
-
- /**
- * Retrieves a single instruction by ID
- * @param instructionId - The UUID of the instruction to retrieve
- * @returns A promise resolving to the instruction details
- * @throws {Error} If the instruction is not found or user doesn't have access
- */
- getInstruction: typeof api.getInstruction;
-
- /**
- * Updates an existing instruction
- * @param instructionId - The UUID of the instruction to update
- * @param request - The fields to update
- * @returns A promise resolving to the updated instruction
- * @throws {Error} If the instruction is not found or validation fails
- *
- * @description
- * At least one field must be provided.
- * If is_default: true is set, all other instructions are automatically set to is_default: false.
- * The prompt_tokens field is recalculated automatically if prompt changes.
- */
- updateInstruction: typeof api.updateInstruction;
-
- /**
- * Deletes an instruction permanently
- * @param instructionId - The UUID of the instruction to delete
- * @returns A promise resolving to deletion confirmation
- * @throws {Error} If the instruction is not found or user doesn't have access
- *
- * @description
- * Permanently deletes an instruction. This action cannot be undone.
- */
- deleteInstruction: typeof api.deleteInstruction;
-
- /**
- * Sets an instruction as the default
- * @param instructionId - The UUID of the instruction to set as default
- * @returns A promise resolving to the updated instruction
- * @throws {Error} If the instruction is not found
- *
- * @description
- * Sets the specified instruction as the default.
- * All other instructions for this user are automatically set to is_default: false.
- * This operation is idempotent.
- */
- setDefaultInstruction: typeof api.setDefaultInstruction;
-};
-
-export const OpenSecretContext = createContext({
- auth: {
- loading: true,
- user: undefined
- },
- clientId: "",
- apiKey: undefined,
- setApiKey: () => {},
- signIn: async () => {},
- signUp: async () => {},
- signInGuest: async () => {},
- signUpGuest: async (): Promise => ({
- id: "",
- email: undefined,
- access_token: "",
- refresh_token: ""
- }),
- convertGuestToUserAccount: async () => {},
- signOut: async () => {},
- get: api.fetchGet,
- put: api.fetchPut,
- list: api.fetchList,
- del: api.fetchDelete,
- delAll: api.fetchDeleteAllKV,
- verifyEmail: api.verifyEmail,
- requestNewVerificationCode: api.requestNewVerificationCode,
- requestNewVerificationEmail: api.requestNewVerificationCode,
- refetchUser: async () => {},
- changePassword: api.changePassword,
- refreshAccessToken: api.refreshToken,
- requestPasswordReset: async () => {},
- confirmPasswordReset: async () => {},
- requestAccountDeletion: async () => {},
- confirmAccountDeletion: async () => {},
- initiateGitHubAuth: async () => ({ auth_url: "", csrf_token: "" }),
- handleGitHubCallback: async () => {},
- initiateGoogleAuth: async () => ({ auth_url: "", csrf_token: "" }),
- handleGoogleCallback: async () => {},
- initiateAppleAuth: async () => ({ auth_url: "", state: "" }),
- handleAppleCallback: async () => {},
- handleAppleNativeSignIn: async () => {},
- getPrivateKey: api.fetchPrivateKey,
- getPrivateKeyBytes: api.fetchPrivateKeyBytes,
- getPublicKey: api.fetchPublicKey,
- signMessage: api.signMessage,
- aiCustomFetch: async () => new Response(),
- apiUrl: "",
- pcrConfig: {},
- getAttestation,
- authenticate,
- parseAttestationForView,
- awsRootCertDer: AWS_ROOT_CERT_DER,
- expectedRootCertHash: EXPECTED_ROOT_CERT_HASH,
- getAttestationDocument: async () => {
- throw new Error("getAttestationDocument called outside of OpenSecretProvider");
- },
- generateThirdPartyToken: async () => ({ token: "" }),
- encryptData: api.encryptData,
- decryptData: api.decryptData,
- fetchModels: api.fetchModels,
- uploadDocument: api.uploadDocument,
- checkDocumentStatus: api.checkDocumentStatus,
- uploadDocumentWithPolling: api.uploadDocumentWithPolling,
- createApiKey: api.createApiKey,
- listApiKeys: api.listApiKeys,
- deleteApiKey: api.deleteApiKey,
- transcribeAudio: api.transcribeAudio,
- fetchResponsesList: api.fetchResponsesList,
- fetchResponse: api.fetchResponse,
- cancelResponse: api.cancelResponse,
- deleteResponse: api.deleteResponse,
- createResponse: api.createResponse,
- listConversations: api.listConversations,
- deleteConversations: api.deleteConversations,
- batchDeleteConversations: api.batchDeleteConversations,
- createInstruction: api.createInstruction,
- listInstructions: api.listInstructions,
- getInstruction: api.getInstruction,
- updateInstruction: api.updateInstruction,
- deleteInstruction: api.deleteInstruction,
- setDefaultInstruction: api.setDefaultInstruction
-});
-
-/**
- * Provider component for OpenSecret authentication and key-value storage.
- *
- * @deprecated The OpenSecretProvider is deprecated. Instead, use the `configure` function
- * and import API functions directly. This provider will be removed in a future version.
- *
- * Migration guide:
- * ```tsx
- * // Old approach (deprecated)
- *
- *
- *
- *
- * // New approach
- * import { configure, signIn, get, put } from '@opensecret/react';
- *
- * configure({ apiUrl: '...', clientId: '...' });
- *
- * // Use functions directly
- * await signIn(email, password);
- * const value = await get('key');
- * ```
- *
- * @param props - Configuration properties for the OpenSecret provider
- * @param props.children - React child components to be wrapped by the provider
- * @param props.apiUrl - URL of OpenSecret enclave backend
- * @param props.clientId - UUID identifying which project/tenant this instance belongs to
- * @param props.pcrConfig - Optional PCR configuration for attestation validation
- *
- * @remarks
- * This provider manages:
- * - User authentication state
- * - Authentication methods (sign in, sign up, sign out)
- * - Key-value storage operations
- * - Project/tenant identification via clientId
- *
- * @example
- * ```tsx
- *
- *
- *
- * ```
- */
-export function OpenSecretProvider({
- children,
- apiUrl,
- clientId,
- pcrConfig = {}
-}: {
- children: React.ReactNode;
- apiUrl: string;
- clientId: string;
- pcrConfig?: PcrConfig;
-}) {
- const [auth, setAuth] = useState({
- loading: true,
- user: undefined
- });
- const [apiKey, setApiKeyState] = useState();
- const [aiCustomFetch, setAiCustomFetch] = useState();
-
- // Validates UUID-with-dashes (v1–v5) and trims input; set undefined to clear
- const setApiKey = (key: string | undefined) => {
- if (key === undefined) {
- setApiKeyState(undefined);
- return;
- }
- const trimmed = key.trim();
- const uuidWithDashes =
- /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
- if (!uuidWithDashes.test(trimmed)) {
- console.warn("setApiKey: provided key does not look like a UUID; clearing apiKey");
- setApiKeyState(undefined);
- return;
- }
- setApiKeyState(trimmed);
- };
-
- useEffect(() => {
- if (!apiUrl || apiUrl.trim() === "") {
- throw new Error(
- "OpenSecretProvider requires a non-empty apiUrl. Please provide a valid API endpoint URL."
- );
- }
- if (!clientId || clientId.trim() === "") {
- throw new Error(
- "OpenSecretProvider requires a non-empty clientId. Please provide a valid project UUID."
- );
- }
-
- // Configure the SDK with the provided values
- configure({ apiUrl, clientId });
- }, [apiUrl, clientId]);
-
- // Create aiCustomFetch when API is configured (supports JWT or API key internally)
- useEffect(() => {
- if (apiUrl) {
- // Pass API key if available, otherwise falls back to JWT
- setAiCustomFetch(() => createCustomFetch(apiKey ? { apiKey } : undefined));
- } else {
- setAiCustomFetch(undefined);
- }
- }, [apiUrl, apiKey]);
-
- async function fetchUser() {
- const access_token = window.localStorage.getItem("access_token");
- const refresh_token = window.localStorage.getItem("refresh_token");
- if (!access_token || !refresh_token) {
- setAuth({
- loading: false,
- user: undefined
- });
- return;
- }
-
- try {
- const user = await api.fetchUser();
- setAuth({
- loading: false,
- user
- });
- } catch (error) {
- console.error("Failed to fetch user:", error);
- setAuth({
- loading: false,
- user: undefined
- });
- }
- }
-
- useEffect(() => {
- fetchUser();
- }, []);
-
- async function signIn(email: string, password: string) {
- console.log("Signing in");
- try {
- await api.fetchLogin(email, password);
- setApiKey(undefined);
- await fetchUser();
- } catch (error) {
- console.error(error);
- throw error;
- }
- }
-
- async function signUp(email: string, password: string, inviteCode: string, name?: string) {
- try {
- await api.fetchSignUp(
- email,
- password,
- inviteCode,
- name || null
- );
- setApiKey(undefined);
- await fetchUser();
- } catch (error) {
- console.error(error);
- throw error;
- }
- }
-
- async function signInGuest(id: string, password: string) {
- console.log("Signing in Guest");
- try {
- await api.fetchGuestLogin(id, password);
- setApiKey(undefined);
- await fetchUser();
- } catch (error) {
- console.error(error);
- throw error;
- }
- }
-
- async function signUpGuest(password: string, inviteCode: string) {
- try {
- const response = await api.fetchGuestSignUp(
- password,
- inviteCode
- );
- setApiKey(undefined);
- await fetchUser();
- return response;
- } catch (error) {
- console.error(error);
- throw error;
- }
- }
-
- async function convertGuestToUserAccount(email: string, password: string, name?: string | null) {
- try {
- await api.convertGuestToEmailAccount(email, password, name);
- await fetchUser();
- } catch (error) {
- console.error(error);
- throw error;
- }
- }
-
- async function signOut() {
- await api.fetchLogout();
- setApiKey(undefined);
- setAuth({
- loading: false,
- user: undefined
- });
- }
-
- const initiateGitHubAuth = async (inviteCode: string) => {
- try {
- return await api.initiateGitHubAuth(inviteCode);
- } catch (error) {
- console.error("Failed to initiate GitHub auth:", error);
- throw error;
- }
- };
-
- const handleGitHubCallback = async (code: string, state: string, inviteCode: string) => {
- try {
- await api.handleGitHubCallback(
- code,
- state,
- inviteCode
- );
- setApiKey(undefined);
- await fetchUser();
- } catch (error) {
- console.error("GitHub callback error:", error);
- throw error;
- }
- };
-
- const initiateGoogleAuth = async (inviteCode: string) => {
- try {
- return await api.initiateGoogleAuth(inviteCode);
- } catch (error) {
- console.error("Failed to initiate Google auth:", error);
- throw error;
- }
- };
-
- const handleGoogleCallback = async (code: string, state: string, inviteCode: string) => {
- try {
- await api.handleGoogleCallback(
- code,
- state,
- inviteCode
- );
- setApiKey(undefined);
- await fetchUser();
- } catch (error) {
- console.error("Google callback error:", error);
- throw error;
- }
- };
-
- const initiateAppleAuth = async (inviteCode: string) => {
- try {
- return await api.initiateAppleAuth(inviteCode);
- } catch (error) {
- console.error("Failed to initiate Apple auth:", error);
- throw error;
- }
- };
-
- const handleAppleCallback = async (code: string, state: string, inviteCode: string) => {
- try {
- await api.handleAppleCallback(
- code,
- state,
- inviteCode
- );
- setApiKey(undefined);
- await fetchUser();
- } catch (error) {
- console.error("Apple callback error:", error);
- throw error;
- }
- };
-
- const handleAppleNativeSignIn = async (appleUser: api.AppleUser, inviteCode?: string) => {
- try {
- await api.handleAppleNativeSignIn(
- appleUser,
- inviteCode
- );
- setApiKey(undefined);
- await fetchUser();
- } catch (error) {
- console.error("Apple native sign-in error:", error);
- throw error;
- }
- };
-
- const getAttestationDocument = async () => {
- const nonce = window.crypto.randomUUID();
- const response = await fetch(`${apiUrl}/attestation/${nonce}`);
- if (!response.ok) {
- throw new Error("Failed to fetch attestation document");
- }
-
- const data = await response.json();
- const verifiedDocument = await authenticate(
- data.attestation_document,
- AWS_ROOT_CERT_DER,
- nonce
- );
- return parseAttestationForView(verifiedDocument, verifiedDocument.cabundle, pcrConfig);
- };
-
- const value: OpenSecretContextType = {
- auth,
- clientId,
- apiKey,
- setApiKey,
- signIn,
- signInGuest,
- signOut,
- signUp,
- signUpGuest,
- convertGuestToUserAccount,
- get: api.fetchGet,
- put: api.fetchPut,
- list: api.fetchList,
- del: api.fetchDelete,
- delAll: api.fetchDeleteAllKV,
- refetchUser: fetchUser,
- verifyEmail: api.verifyEmail,
- requestNewVerificationCode: api.requestNewVerificationCode,
- requestNewVerificationEmail: api.requestNewVerificationCode,
- changePassword: api.changePassword,
- refreshAccessToken: api.refreshToken,
- requestPasswordReset: api.requestPasswordReset,
- confirmPasswordReset: api.confirmPasswordReset,
- requestAccountDeletion: api.requestAccountDeletion,
- confirmAccountDeletion: api.confirmAccountDeletion,
- initiateGitHubAuth,
- handleGitHubCallback,
- initiateGoogleAuth,
- handleGoogleCallback,
- initiateAppleAuth,
- handleAppleCallback,
- handleAppleNativeSignIn,
- getPrivateKey: api.fetchPrivateKey,
- getPrivateKeyBytes: api.fetchPrivateKeyBytes,
- getPublicKey: api.fetchPublicKey,
- signMessage: api.signMessage,
- aiCustomFetch: aiCustomFetch || (async () => new Response()),
- apiUrl,
- pcrConfig,
- getAttestation,
- authenticate,
- parseAttestationForView,
- awsRootCertDer: AWS_ROOT_CERT_DER,
- expectedRootCertHash: EXPECTED_ROOT_CERT_HASH,
- getAttestationDocument,
- generateThirdPartyToken: api.generateThirdPartyToken,
- encryptData: api.encryptData,
- decryptData: api.decryptData,
- fetchModels: api.fetchModels,
- uploadDocument: api.uploadDocument,
- checkDocumentStatus: api.checkDocumentStatus,
- uploadDocumentWithPolling: api.uploadDocumentWithPolling,
- createApiKey: api.createApiKey,
- listApiKeys: api.listApiKeys,
- deleteApiKey: api.deleteApiKey,
- transcribeAudio: api.transcribeAudio,
- fetchResponsesList: api.fetchResponsesList,
- fetchResponse: api.fetchResponse,
- cancelResponse: api.cancelResponse,
- deleteResponse: api.deleteResponse,
- createResponse: api.createResponse,
- listConversations: api.listConversations,
- deleteConversations: api.deleteConversations,
- batchDeleteConversations: api.batchDeleteConversations,
- createInstruction: api.createInstruction,
- listInstructions: api.listInstructions,
- getInstruction: api.getInstruction,
- updateInstruction: api.updateInstruction,
- deleteInstruction: api.deleteInstruction,
- setDefaultInstruction: api.setDefaultInstruction
- };
-
- return {children} ;
-}
diff --git a/src/lib/util.ts b/src/lib/util.ts
deleted file mode 100644
index fb1ede1..0000000
--- a/src/lib/util.ts
+++ /dev/null
@@ -1,19 +0,0 @@
-import { useEffect, useRef } from "react";
-
-// Implementation that ensures callback only runs once, even in React strict mode
-export function useOnMount(callback: () => void) {
- const hasRun = useRef(false);
-
- // eslint-disable-next-line react-hooks/exhaustive-deps
- useEffect(() => {
- if (!hasRun.current) {
- hasRun.current = true;
- callback();
- }
- }, []);
-}
-
-// Helper function to sleep for a specified duration
-export function sleep(ms: number): Promise {
- return new Promise((resolve) => setTimeout(resolve, ms));
-}
diff --git a/src/main.tsx b/src/main.tsx
deleted file mode 100644
index f8e8d12..0000000
--- a/src/main.tsx
+++ /dev/null
@@ -1,16 +0,0 @@
-import { StrictMode } from "react";
-import { createRoot } from "react-dom/client";
-import "./index.css";
-import App from "./App.tsx";
-import { OpenSecretProvider } from "./lib";
-
-createRoot(document.getElementById("root")!).render(
-
-
-
-
-
-);
diff --git a/vite.config.ts b/vite.config.ts
index 34fefba..d5783eb 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,5 +1,4 @@
import { defineConfig } from "vite";
-import react from "@vitejs/plugin-react";
import path from "path";
import fs from "fs";
import dts from 'vite-plugin-dts'
@@ -29,7 +28,6 @@ function derPlugin() {
// https://vite.dev/config/
export default defineConfig({
plugins: [
- react(),
derPlugin(),
dts({
rollupTypes: true,