johnandersen777.bsky.social/atlibs-socialweb-computer-requester
socialweb.computer market requester and policy modules for atlibs, with its web pages and lexicons
0
0
import { BrowserOAuthClient } from '@atproto/oauth-client-browser'
import { Agent } from '@atproto/api'
import { secp256k1 } from '@noble/curves/secp256k1.js'
import * as age from 'age-encryption'
import * as devTunnelsSsh from '@microsoft/dev-tunnels-ssh'
import { Buffer } from 'buffer'
import { WebrtcCubit } from './webrtc-cubit.js'
import { createPluginContext } from 'atlibs/plugin-context/index.js'
import { loadModule, localModule } from 'atlibs/run-module/index.js'
import { pageClientId } from 'atlibs/oauth/index.js'
// Wiring only: user events go to the cubit, cubit state goes to the views.
// The requester wasm module plans every market call; ?module=<handle>/<name> (or at://...) runs a published record.
const MODULE = new URLSearchParams(location.search).get('module') ?? 'local';
const MODULES = new URL('./modules/', import.meta.url);
const T = 'com.publicdomainrelay.temp';
// The attestation key lives in this secret setting (AES-GCM, key non-extractable in IndexedDB).
// managed_records is run-module's rkey ledger: without it the module may not delete what it
// created (contract's cleanup was refused live, 2026-11-09).
const ctx = createPluginContext({ id: 'webrtc', storage: { managed_records: {} }, settingsSchema: { attestKeys: { type: 'secret' } } });
const module = MODULE === 'local' ? localModule(new URL('requester.wasm', MODULES), new URL('requester.json', MODULES)) : loadModule(ctx, MODULE);
// Create (and delete, for cleanup) on the requester's collections, service-auth for the bidder calls, and
// WebRTC offers to a bidder's ingress (its offering's webrtc endpoint).
// pageClientId: the loopback client on localhost, ./stage.json's clientId when deployed.
const WRITES = ['compute.vm', 'market.rfp', 'market.accept', 'market.event', 'compute.events.vm.delete',
'compute.events.guest.signalWebRTC', 'compute.events.guest.publicKey'];
const RPCS = ['market.submitRfp', 'market.submitAccept', 'market.submitEvent', 'requester.associateConfirm'];
const SCOPE = ['atproto', `repo:${T}.badgeBlueKeys?action=create`, ...WRITES.map((c) => `repo:${T}.${c}?action=create&action=delete`),
...RPCS.map((m) => `rpc:${T}.${m}?aud=*`), 'repo:computer.socialweb.ingress.offer?action=create&action=delete'].join(' ');
const cubit = new WebrtcCubit({
client: pageClientId(SCOPE).then((clientId) => BrowserOAuthClient.load({ clientId, handleResolver: 'https://bsky.social' })),
makeAgent: (session) => new Agent(session),
secp256k1,
age,
makePeer: (config) => new RTCPeerConnection(config),
ssh: { ...devTunnelsSsh, Buffer },
module,
ctx,
});
globalThis.webrtcCubit = cubit;
const show = (id, on) => (document.getElementById(id).style.display = on ? "inherit" : "none");
function renderPage(state) {
show("loading-error", state.session === "error");
document.getElementById("loading-error").innerText = state.error ?? "";
show("login-container", state.session === "signed-out");
show("logout-nav", state.session === "signed-in");
document.getElementById("whoami").innerText = state.handle ? `${state.handle} (${state.did})` : "";
document.getElementById("login-button").toggleAttribute("aria-busy", state.login.busy);
document.getElementById("login-form-error").innerText = state.login.error ?? "";
}
async function init() {
const names = ["webrtc-connect", "webrtc-terminal", "requester-actions"];
await Promise.all(names.map((name) => customElements.whenDefined(name)));
const [connect, terminal, actions] = names.map((name) => document.querySelector(name));
actions.setActions((await module).actions);
document.getElementById("login-form").onsubmit = (e) => {
e.preventDefault();
cubit.login(e.target.username.value);
};
document.getElementById("logout-nav").onclick = (e) => {
e.preventDefault();
cubit.logout();
};
document.addEventListener("run-entry", (e) => cubit.run(e.detail.entry, e.detail.input).catch(() => undefined));
document.addEventListener("find-guest", (e) => cubit.find(e.detail.receipt, e.detail.tag));
document.addEventListener("create-offer", () => cubit.offer());
document.addEventListener("wait-answer", () => cubit.answer());
document.addEventListener("send-line", (e) => cubit.send(e.detail.line));
document.addEventListener("ssh-key", () => cubit.sshKey());
cubit.subscribe((state, previous) => {
renderPage(state);
connect.render(state, previous);
terminal.render(state, previous);
actions.render(state, previous);
});
await cubit.init();
}
document.addEventListener('DOMContentLoaded', init);