"""Small, fail-closed downstream changes for the pinned upstream source. Run only inside the disposable build context, from the upstream checkout root. Upstream changes must be re-reviewed rather than silently patched by regex. """ from pathlib import Path def replace(path, old, new): file = Path(path) text = file.read_text() if text.count(old) != 1: raise SystemExit(f"Upstream contract changed: {path}: {old[:70]!r}") file.write_text(text.replace(old, new)) replace('app/index.html', ' ', ' \n ') replace('app/index.html', '', '\n ') # Build-time insertion, not response substitution: offline HTML retains the # provider reference. The provider itself lives outside /j/ and its SW scope. replace('app/src/main.ts', 'async function signInWithNostr(): Promise {', '''async function signInWithNostr(reuseSelected = false, useHost = true): Promise {''') replace('app/src/main.ts', " account = await login({ appName: 'KithMoot', relayUrls: signinRelays(RELAYS),", ''' const host = useHost ? (window as Window & { archipelagoNostr?: { selectIdentity?: () => Promise getSelectedIdentity?: () => unknown } }).archipelagoNostr : undefined if (host?.selectIdentity && (!reuseSelected || !host.getSelectedIdentity?.())) { await host.selectIdentity() } account = await login({ appName: 'KithMoot', relayUrls: signinRelays(RELAYS), preferredMethod: host?.selectIdentity ? 'nip07' : undefined,''') replace('app/src/main.ts', " methods: ['nip07', 'amber', 'remote-signet', 'local-signet', 'bunker', 'nostrconnect', 'nsec'] })", " methods: ['nip07', 'amber', 'remote-signet', 'local-signet', 'bunker', 'nostrconnect'] })") # Use the existing account-adoption/generation checks. Never auto-join a room. replace('app/src/main.ts', '// Only an explicit switch or safe update in this tab skips the door.', '''// Ask once after restoration and UI setup; cancellation leaves normal login. // An existing remote signer or remembered account is never silently replaced. void identityReady.then(() => { const host = (window as Window & { archipelagoNostr?: unknown }).archipelagoNostr if (host && !nostrSession && !expectedAccount && !loginBusy && !session && !joining) { return signInWithNostr(true).catch(error => setStatus(describeError(error))) } }) const otherSigner = document.createElement('button') otherSigner.type = 'button' otherSigner.textContent = 'Use another signer' otherSigner.className = 'quiet' otherSigner.addEventListener('click', () => { void signInWithNostr(false, false).catch(error => setStatus(describeError(error))) }) $('headerSignIn').insertAdjacentElement('afterend', otherSigner) // Only an explicit switch or safe update in this tab skips the door.''') # A static UI package must not promise a TURN credential service it lacks. replace('app/src/main.ts', "const TURN_CREDENTIAL_ENDPOINT: string | undefined = '/turn'", 'const TURN_CREDENTIAL_ENDPOINT: string | undefined = undefined') replace('app/index.html', '', '') # Explain the package boundary before users try unavailable infrastructure. replace('app/index.html', '
', '\n
')