This repository has no description
5.3 kB
119 lines
1{ inputs, ... }:
2{
3 perSystem =
4 {
5 pkgs,
6 self',
7 system,
8 ...
9 }:
10 let
11 server = "https://omni.lassul.us";
12 omp = inputs.llm-agents.packages.${system}.omp;
13
14 # omnigent's `pi-native` harness runs the agent's own TUI in a tmux pane,
15 # attaches your TTY to it, and mirrors the session (transcript, tool
16 # calls, terminal, files) to the server — the shape covibe always had,
17 # now with omnigent's per-session ACLs behind it. It resolves the CLI by
18 # the literal name `pi` (onboarding/harness_install.py: the readiness gate
19 # is `resolve_cli_binary("pi")`, which ignores OMNIGENT_PI_PATH and the
20 # config override), so omp has to reach it under that name. Version-gated
21 # flags are satisfied by omp reporting 17.x >= pi's declared 0.79.0 floor.
22 piShim = pkgs.writeShellApplication {
23 name = "pi";
24 text = ''
25 OMP_BIN=${omp}/bin/omp
26 ''
27 + builtins.readFile ./pi-shim.sh;
28 };
29 in
30 {
31 # Launch omp as a session on the omni.lassul.us dashboard: omp's own TUI
32 # runs here, in a tmux pane owned by the local runner, while the server
33 # holds the transcript, the diff view and the per-session ACLs — so a
34 # session can be shared read-only or read-write with another pocket-id
35 # account, and a teammate's message from the browser executes in this
36 # TUI.
37 #
38 # Auth is the pocket-id login, but *only a browser* has to see pocket-id —
39 # the machine running the agent only needs to reach the server. omnigent's
40 # cli-ticket flow (POST /auth/cli-login, then poll /auth/cli-poll for five
41 # minutes) prints a URL that any device with your passkey can complete, so
42 # this works over ssh on a box with no display: run `covibe login`, open
43 # the printed link on a laptop or phone, done. The session JWT is then
44 # cached in ~/.omnigent/auth_tokens.json for the server's configured TTL.
45 # Other arguments go through to omp, e.g. `covibe --continue`;
46 # `covibe --resume` (no value) opens omnigent's session picker.
47 #
48 # Note the TUI path runs on a spec omnigent materialises itself
49 # (`pi-native-ui`), so the only terminal it declares is omp's own pane;
50 # extra shells from the dashboard are a property of the *browser-started*
51 # omp agent, whose spec lives in configs/omnigent.nix.
52 packages.covibe =
53 (pkgs.writeShellApplication {
54 name = "covibe";
55 runtimeInputs = [
56 self'.packages.omnigent
57 piShim
58 # The host daemon we spawn inherits this PATH, and sessions started
59 # from the *browser* use the server's catalog entry, whose command is
60 # a bare `omp acp` resolved here — without omp on PATH those turns
61 # die with "inner executor error: [Errno 2] No such file or
62 # directory". The `pi` shim alone is not enough: it only covers the
63 # pi-native TUI path.
64 omp
65 pkgs.tmux
66 pkgs.jq
67 pkgs.git
68 ];
69 text = ''
70 server=${server}
71 tokens="$HOME/.omnigent/auth_tokens.json"
72
73 # `webbrowser.open` walks a candidate list that includes terminal
74 # browsers; on a headless box one of those can seize the tty we are
75 # about to hand to omp's TUI. With no display, make the "open" a
76 # no-op — the URL is printed either way.
77 if [ -z "''${DISPLAY:-}" ] && [ -z "''${WAYLAND_DISPLAY:-}" ]; then
78 export BROWSER=''${BROWSER:-echo}
79 fi
80
81 login() {
82 echo "covibe: authenticating against $server" >&2
83 echo "covibe: open the link below on any device that has your pocket-id passkey" >&2
84 omnigent login "$server"
85 }
86
87 case "''${1:-}" in
88 login)
89 login
90 exit 0
91 ;;
92 host)
93 # Register this machine so sessions can also be STARTED from the
94 # browser (New Chat → this host → the `omp` agent). That path runs
95 # `omp acp` from the runner's PATH, which only has omp when the
96 # daemon was spawned with it — as it is here. Runs in the
97 # foreground; ^C stops hosting.
98 shift
99 exec omnigent host "$server" "$@"
100 ;;
101 esac
102
103 # The cached session is a JWT with a server-side TTL
104 # (OMNIGENT_OIDC_SESSION_TTL_HOURS), so "a token exists" is not the
105 # same as "we are logged in": a stale entry makes omnigent fail with
106 # `Pi session creation failed (401): Authentication required`
107 # instead of re-authenticating. Check the recorded expiry (with a
108 # minute of slack, via jq's own clock so this needs no coreutils).
109 if ! jq -e --arg s "$server" \
110 '((.[$s].expires_at // 0) > (now + 60)) and ((.[$s].token // "") != "")' \
111 "$tokens" >/dev/null 2>&1; then
112 login
113 fi
114 exec omnigent pi --server "$server" "$@"
115 '';
116 }).overrideAttrs
117 { passthru.usage = builtins.readFile ./usage.kdl; };
118 };
119}