// Settings page — workspace, members, orchestration policy, security, integrations, API keys
// ---------- Form atoms ----------
function Switch({ checked, onChange }) {
return (
);
}
function Seg({ value, onChange, options }) {
return (
{options.map(o => (
))}
);
}
function Field({ label, hint, children, stack, badge }) {
return (
{label}{badge}
{hint &&
{hint}
}
{children}
);
}
function SectionHead({ title, sub, action }) {
return (
);
}
function Panel({ icon, title, right, children, danger }) {
return (
{icon && }
{title}
{right && {right}}
{children}
);
}
// ---------- Sections ----------
function GeneralSection({ s, set }) {
return (
);
}
const MEMBERS = [
{ name: 'Kavi Rao', email: 'kavi.r@zenix.team', role: 'owner', seen: 'online', color: 'linear-gradient(135deg, oklch(0.65 0.15 30), oklch(0.6 0.18 350))' },
{ name: 'Dana Whitfield', email: 'dana.w@zenix.team', role: 'admin', seen: '12m ago', color: 'oklch(0.6 0.16 280)' },
{ name: 'Marco Liu', email: 'marco.l@zenix.team', role: 'operator', seen: '1h ago', color: 'oklch(0.62 0.15 200)' },
{ name: 'Priya Nair', email: 'priya.n@zenix.team', role: 'operator', seen: '3h ago', color: 'oklch(0.62 0.15 150)' },
{ name: 'Sam Okonkwo', email: 'sam.o@zenix.team', role: 'viewer', seen: 'yesterday', color: 'oklch(0.65 0.16 60)' },
];
function MembersSection() {
return (
Invite member}
/>
{MEMBERS.length} active · 1 pending}>
{MEMBERS.map(m => (
{m.name.split(' ').map(w => w[0]).join('')}
{m.seen === 'online'
? online
: m.seen}
{m.role === 'owner'
?
owner
: (
)}
))}
?
jordan.k@zenix.team
invited by Kavi · expires in 6 days
operator
);
}
function OrchestrationSection({ s, set }) {
return (
set('autonomy', v)} options={[
{ value: 'supervised', label: 'Supervised' },
{ value: 'semi', label: 'Semi-auto' },
{ value: 'auto', label: 'Autonomous' },
]} />
set('maxAgents', +e.target.value)} />
set('queueDepth', +e.target.value)} />
set('gateDb', v)} />
set('gateProd', v)} />
set('gateHigh', v)} />
set('autoRetry', v)} />
set('maxRetries', +e.target.value)} disabled={!s.autoRetry} style={{ opacity: s.autoRetry ? 1 : 0.5 }} />
set('autoRollback', v)} />
set('quietFrom', e.target.value)} />
→
set('quietTo', e.target.value)} />
);
}
function SecuritySection({ s, set }) {
return (
set('mfa', v)} />
connected}
hint="Okta · login.zenix.team — last metadata sync 2 days ago."
>
set('scim', v)} />
);
}
const INTEGRATIONS = [
{ id: 'slack', name: 'Slack', icon: '#', bg: 'oklch(0.6 0.14 320)', desc: 'Posts approvals, halts, and completions to #orchestra-ops.', status: 'connected' },
{ id: 'github', name: 'GitHub', icon: 'GH', bg: 'oklch(0.4 0.02 260)', desc: 'Opens PRs for code-changing tasks · zenix-team org.', status: 'connected' },
{ id: 'registry', name: 'Module registry', icon: '◷', bg: 'oklch(0.58 0.19 280)', desc: 'registry.orchestra.prod — signed package source.', status: 'connected' },
{ id: 'datadog', name: 'Datadog', icon: 'DD', bg: 'oklch(0.55 0.18 300)', desc: 'Stream agent metrics and traces to your dashboards.', status: 'available' },
{ id: 'pagerduty', name: 'PagerDuty', icon: 'PD', bg: 'oklch(0.6 0.16 150)', desc: 'Page the on-call when a task halts in production.', status: 'available' },
];
function IntegrationsSection() {
return (
Sync now}
/>
3 connected}>
{INTEGRATIONS.map(it => (
{it.icon}
{it.status === 'connected'
?
connected
:
}
))}
);
}
const KEYS = [
{ id: 'k1', label: 'CI pipeline', prefix: 'orc_live_7f3a', scope: 'write', last: '4m ago', created: 'Mar 2, 2026' },
{ id: 'k2', label: 'Grafana exporter', prefix: 'orc_live_b21c', scope: 'read', last: '1h ago', created: 'Jan 18, 2026' },
{ id: 'k3', label: 'Local dev — kavi', prefix: 'orc_test_9d04', scope: 'write', last: '3d ago', created: 'Feb 27, 2026' },
];
function ApiSection() {
return (
Create key}
/>
{KEYS.length} keys}>
{KEYS.map(k => (
{k.prefix}····································
{k.label}
created {k.created}
last used {k.last}
{k.scope === 'write' ? 'read · write' : 'read only'}
))}
A key's full secret is shown only once at creation. Rotate keys every 90 days.
);
}
// ---------- Page shell ----------
function ConnectionsSection() {
const [conns, setConns] = React.useState([]);
const [prompts, setPrompts] = React.useState({});
const [busy, setBusy] = React.useState(null);
const load = React.useCallback(() => {
window.ORC_API.getConnections().then(setConns).catch(() => {});
}, []);
React.useEffect(() => {
load();
const es = window.ORC_API.stream(ev => {
if (ev.type !== 'auth') return;
setPrompts(p => ({
...p,
[ev.backend]: {
url: ev.url ?? p[ev.backend]?.url,
code: ev.code ?? p[ev.backend]?.code,
message: ev.message ?? p[ev.backend]?.message,
status: ev.status ?? p[ev.backend]?.status,
},
}));
if (ev.status === 'connected') load();
});
const t = setInterval(load, 5000);
return () => { es.close(); clearInterval(t); };
}, [load]);
async function connect(id) {
setBusy(id);
try {
const r = await window.ORC_API.loginConnection(id);
setPrompts(p => ({ ...p, [id]: { url: r.url, code: r.code, message: r.message, status: r.mode === 'manual' ? 'manual' : 'pending' } }));
} catch (e) {
setPrompts(p => ({ ...p, [id]: { status: 'error', message: e.message } }));
} finally {
setBusy(null);
}
}
const meta = {
codex: { name: 'Codex CLI', sub: 'OpenAI · ChatGPT subscription · device login' },
gemini: { name: 'Gemini CLI', sub: 'Google AI Pro/Ultra · copy ~/.gemini cache' },
};
const stateColor = (st) => st === 'connected' ? 'var(--c-status-done)' : st === 'expired' ? 'var(--c-status-review)' : 'var(--c-text-3)';
return (
{conns.map(c => {
const m = meta[c.id] || { name: c.id, sub: c.kind };
const pr = prompts[c.id];
return (
{c.auth.state}}>
{pr && (
{pr.url &&
}
{pr.code &&
2. Enter code: {pr.code}
}
{pr.message &&
{pr.message}
}
{pr.status &&
status: {pr.status}
}
)}
);
})}
{conns.length === 0 &&
no backends
}
);
}
const SETTINGS_NAV = [
{ id: 'general', name: 'General', icon: 'settings' },
{ id: 'members', name: 'Members', icon: 'users', count: '6' },
{ id: 'connections', name: 'Connections', icon: 'cpu' },
{ id: 'orchestration', name: 'Orchestration', icon: 'cpu' },
{ id: 'security', name: 'Security', icon: 'shield' },
{ id: 'integrations', name: 'Integrations', icon: 'link', count: '3' },
{ id: 'api', name: 'API keys', icon: 'key' },
];
function SettingsPage() {
const [section, setSection] = React.useState('general');
const [s, setS] = React.useState({
wsName: 'Zenix Team',
wsSlug: 'zenix-team',
env: 'prod',
region: 'us-east-2',
autonomy: 'semi',
maxAgents: 6,
queueDepth: 12,
gateDb: true,
gateProd: true,
gateHigh: false,
autoRetry: true,
maxRetries: 3,
autoRollback: true,
quietFrom: '23:00',
quietTo: '06:00',
mfa: true,
scim: false,
sessionTimeout: '12h',
ipAllow: '10.0.0.0/8\n52.18.44.12/32',
});
const set = (k, v) => setS(prev => ({ ...prev, [k]: v }));
return (
Settings
Workspace · zenix-team · 6 members · plan Scale
{section === 'general' &&
}
{section === 'members' &&
}
{section === 'connections' &&
}
{section === 'orchestration' &&
}
{section === 'security' &&
}
{section === 'integrations' &&
}
{section === 'api' &&
}
);
}
window.SettingsPage = SettingsPage;
// Shared form atoms reused by the agent editor
Object.assign(window, { Switch, Seg, Field, SectionHead, Panel });