@vibelord/lanes 1.0.0 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/payload/lanes/README.md +92 -7
- package/payload/lanes/VERSION +1 -1
- package/payload/lanes/bin/lanes +928 -150
- package/payload/lanes/client/ghostty/common.conf +3 -0
- package/payload/lanes/client/ghostty/lanes-palette-light.conf +25 -0
- package/payload/lanes/client/ghostty/lanes-palette.conf +25 -0
- package/payload/lanes/client/ghostty/omarchy.conf +3 -2
- package/payload/lanes/client/lanes-launch +62 -5
- package/payload/lanes/client/lanes-window +17 -1
- package/payload/lanes/container/env.sh +8 -0
- package/payload/lanes/container/hooks/claude-settings.json +10 -0
- package/payload/lanes/container/hooks/codex-hooks.json +22 -0
- package/payload/lanes/container/lane +326 -25
- package/payload/lanes/container/lane-signal +173 -0
- package/payload/lanes/docs/ARCHITECTURE.md +158 -6
- package/payload/lanes/docs/DESIGN.md +250 -0
- package/payload/lanes/docs/INSTALL.md +39 -0
- package/payload/lanes/docs/RUNBOOK.md +507 -24
- package/payload/lanes/docs/SHARING.md +1 -1
- package/payload/lanes/holders/exec.sh +2 -5
- package/payload/lanes/holders/ssh.sh +18 -0
- package/payload/lanes/holders.lock +3 -0
- package/payload/lanes/install/enable-phone.sh +15 -6
- package/payload/lanes/install/enable-web.sh +19 -6
- package/payload/lanes/install/host-install.sh +10 -1
- package/payload/lanes/install/services.py +30 -3
- package/payload/lanes/install.sh +108 -3
- package/payload/lanes/libexec/agent_state.py +246 -0
- package/payload/lanes/libexec/connection.py +178 -0
- package/payload/lanes/libexec/cursor_sync.py +147 -6
- package/payload/lanes/libexec/device_sync.py +134 -0
- package/payload/lanes/libexec/economy_poll.py +685 -0
- package/payload/lanes/libexec/guard.py +27 -1
- package/payload/lanes/libexec/lanes_lib.py +436 -36
- package/payload/lanes/libexec/notify.py +317 -0
- package/payload/lanes/libexec/registrations.py +148 -0
- package/payload/lanes/libexec/session_actions.py +53 -22
- package/payload/lanes/libexec/sidebar.py +1190 -186
- package/payload/lanes/libexec/statusd.py +352 -90
- package/payload/lanes/libexec/tokens.py +310 -0
- package/payload/lanes/libexec/tokens.sh +11 -0
- package/payload/lanes/libexec/view.sh +22 -11
- package/payload/lanes/systemd/lanes-economy.service +20 -0
- package/payload/lanes/systemd/lanes-notify.service +19 -0
- package/payload/lanes/systemd/lanes-sync.service +3 -0
- package/payload/lanes/web/README.md +96 -2
- package/payload/lanes/web/browser.css +22 -0
- package/payload/lanes/web/browser.js +73 -0
- package/payload/lanes/web/index.html +2 -1
- package/payload/lanes/web/mobile.css +108 -221
- package/payload/lanes/web/mobile.js +105 -55
- package/payload/lanes/workspaces.example/README.md +9 -3
- package/payload/lanes/zellij/config.kdl +128 -19
- package/payload/manifest.json +7 -3
- package/payload/lanes/libexec/legacy_sessions.py +0 -130
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# BEGIN lanes tokens (generated by libexec/tokens.py --ghostty-light; do not edit by hand)
|
|
2
|
+
# Optional: the cockpit palette regardless of your everyday Ghostty theme (install.sh --client --palette).
|
|
3
|
+
# Slots 1-4 are the four tones, so 16-colour mode here equals truecolor mode. No font, padding or cursor keys.
|
|
4
|
+
background = #f3f5f2
|
|
5
|
+
foreground = #17241b
|
|
6
|
+
cursor-color = #2f6b45
|
|
7
|
+
selection-background = #d5ded6
|
|
8
|
+
selection-foreground = #17241b
|
|
9
|
+
palette = 0=#3b4a42
|
|
10
|
+
palette = 1=#b7322a
|
|
11
|
+
palette = 2=#1b7142
|
|
12
|
+
palette = 3=#8a5a00
|
|
13
|
+
palette = 4=#2456a8
|
|
14
|
+
palette = 5=#7a3fb8
|
|
15
|
+
palette = 6=#0f7b7b
|
|
16
|
+
palette = 7=#56616d
|
|
17
|
+
palette = 8=#5f6d66
|
|
18
|
+
palette = 9=#c2332a
|
|
19
|
+
palette = 10=#1b7a38
|
|
20
|
+
palette = 11=#8f5a00
|
|
21
|
+
palette = 12=#2860c4
|
|
22
|
+
palette = 13=#8a4dc8
|
|
23
|
+
palette = 14=#0f7272
|
|
24
|
+
palette = 15=#17241b
|
|
25
|
+
# END lanes tokens
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# BEGIN lanes tokens (generated by libexec/tokens.py --ghostty-dark; do not edit by hand)
|
|
2
|
+
# Optional: the cockpit palette regardless of your everyday Ghostty theme (install.sh --client --palette).
|
|
3
|
+
# Slots 1-4 are the four tones, so 16-colour mode here equals truecolor mode. No font, padding or cursor keys.
|
|
4
|
+
background = #101318
|
|
5
|
+
foreground = #edf1f5
|
|
6
|
+
cursor-color = #b4e3bf
|
|
7
|
+
selection-background = #2c333d
|
|
8
|
+
selection-foreground = #edf1f5
|
|
9
|
+
palette = 0=#7d8896
|
|
10
|
+
palette = 1=#f28b7d
|
|
11
|
+
palette = 2=#6fd39a
|
|
12
|
+
palette = 3=#e5b84a
|
|
13
|
+
palette = 4=#7fb3f2
|
|
14
|
+
palette = 5=#c9a6ff
|
|
15
|
+
palette = 6=#6fd6d0
|
|
16
|
+
palette = 7=#9ca7b5
|
|
17
|
+
palette = 8=#8a95a3
|
|
18
|
+
palette = 9=#ff9d96
|
|
19
|
+
palette = 10=#8fe0a3
|
|
20
|
+
palette = 11=#f7cf7f
|
|
21
|
+
palette = 12=#a5cbff
|
|
22
|
+
palette = 13=#dcc6ff
|
|
23
|
+
palette = 14=#9ae6e0
|
|
24
|
+
palette = 15=#edf1f5
|
|
25
|
+
# END lanes tokens
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# Omarchy / Hyprland specifics for the lanes window (Ghostty GTK, dedicated instance).
|
|
2
|
-
# Look & feel is inherited from ~/.config/ghostty/config (Omarchy's default: theme include,
|
|
3
|
-
# Font, shift+enter as CSI-u, control+insert /
|
|
2
|
+
# Look & feel is inherited from ~/.config/ghostty/config (Omarchy's default: theme include, the Omarchy default font —
|
|
3
|
+
# a Nerd Font is not required by lanes, every glyph is plain Unicode; shift+enter as CSI-u, control+insert /
|
|
4
|
+
# shift+insert copy & paste), so Omarchy theme switches follow.
|
|
4
5
|
# Do NOT include the theme file here as well: Ghostty reports a second include of the same file as
|
|
5
6
|
# "config-file …: cycle detected" and opens its configuration-errors window.
|
|
6
7
|
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
# lanes-launch [device] attach this terminal to the device's view session (default: see below)
|
|
4
4
|
# lanes-launch <workspace> same, and bring that workspace's tab to the front (e.g. `coder-launch worlds`)
|
|
5
5
|
# lanes-launch <device> --viewports N a wide view session: N (2-3) viewports side by side per workspace
|
|
6
|
+
# lanes-launch <device> [--ascii] [--theme dark|light|auto] [--glyphs M] [--color C] the device's look (lanes attach)
|
|
6
7
|
# lanes-launch lane <lane-id> attach straight to ONE lane, no multiplexer in between
|
|
7
8
|
# lanes-launch ls list lanes with live status
|
|
8
9
|
# lanes-launch --helper <device> [--service | --while-pid PID] run only the upload helper
|
|
@@ -41,11 +42,25 @@ case "${1:-}" in
|
|
|
41
42
|
lane)
|
|
42
43
|
[ -n "${2:-}" ] || { echo 'usage: lanes-launch lane <lane-id>' >&2; exit 64; }
|
|
43
44
|
mode=open target=$2 ;;
|
|
44
|
-
-h|--help) sed -n '2,
|
|
45
|
+
-h|--help) sed -n '2,12p' "$0"; exit 0 ;;
|
|
45
46
|
*) mode=attach target=${1:-$(default_device)} ;;
|
|
46
47
|
esac
|
|
47
|
-
if [ "$mode" = attach ] && [
|
|
48
|
-
|
|
48
|
+
if [ "$mode" = attach ] && [ $# -gt 1 ]; then
|
|
49
|
+
# Options after the device go to `lanes attach`. They are spliced into the remote command line, so only this closed
|
|
50
|
+
# vocabulary passes: a bad value refuses before any connection, an unknown option is said out loud and dropped.
|
|
51
|
+
shift
|
|
52
|
+
while [ $# -gt 0 ]; do
|
|
53
|
+
case "$1" in
|
|
54
|
+
--viewports) case "${2:-}" in 1|2|3) extra="$extra --viewports $2"; shift ;; *) echo '--viewports takes 1, 2 or 3' >&2; exit 64 ;; esac ;;
|
|
55
|
+
--ascii) extra="$extra --ascii" ;;
|
|
56
|
+
--theme) case "${2:-}" in dark|light|auto) extra="$extra --theme $2"; shift ;; *) echo '--theme takes dark, light or auto' >&2; exit 64 ;; esac ;;
|
|
57
|
+
--glyphs) case "${2:-}" in unicode|ascii|emoji) extra="$extra --glyphs $2"; shift ;; *) echo '--glyphs takes unicode, ascii or emoji' >&2; exit 64 ;; esac ;;
|
|
58
|
+
--color) case "${2:-}" in truecolor|16|none) extra="$extra --color $2"; shift ;; *) echo '--color takes truecolor, 16 or none' >&2; exit 64 ;; esac ;;
|
|
59
|
+
*) echo "lanes-launch: ignoring unknown option $1" >&2 ;;
|
|
60
|
+
esac
|
|
61
|
+
shift
|
|
62
|
+
done
|
|
63
|
+
extra=${extra# }
|
|
49
64
|
fi
|
|
50
65
|
case "$target" in ''|-*|*[!A-Za-z0-9_.-]*) echo "invalid name: $target" >&2; exit 64 ;; esac
|
|
51
66
|
client="$(hostname -s 2>/dev/null || echo client)-$$"
|
|
@@ -67,6 +82,8 @@ log() {
|
|
|
67
82
|
# Run ssh as an async child so HUP/TERM (window closed, logout) end it immediately; a foreground child
|
|
68
83
|
# would defer the trap until ssh returned. Keep the terminal on fd 9: dash gives async jobs /dev/null.
|
|
69
84
|
child=
|
|
85
|
+
dog=
|
|
86
|
+
dog_ready=
|
|
70
87
|
reader=
|
|
71
88
|
holding=
|
|
72
89
|
now=
|
|
@@ -76,6 +93,7 @@ stop() {
|
|
|
76
93
|
trap - HUP TERM INT
|
|
77
94
|
exec 2>/dev/null # bash reports every child it sees die of a signal: noise in a terminal that stays open
|
|
78
95
|
if [ -n "$child" ]; then kill -TERM "$child" 2>/dev/null || :; fi
|
|
96
|
+
cancel_watchdog
|
|
79
97
|
if [ -n "$reader" ]; then kill -TERM "$reader" 2>/dev/null || :; fi
|
|
80
98
|
end_helper
|
|
81
99
|
log 'stopped: window closed, or a signal'
|
|
@@ -89,17 +107,56 @@ end_helper() {
|
|
|
89
107
|
# INT too: in a plain terminal Ctrl-C is the way out, now that nothing else ends the client.
|
|
90
108
|
trap stop HUP TERM INT
|
|
91
109
|
# With $limit set, a watchdog ends a child that the server leaves hanging (macOS has no timeout(1)).
|
|
110
|
+
cancel_watchdog() {
|
|
111
|
+
if [ -n "$dog" ]; then
|
|
112
|
+
# A very fast probe may finish before the forked shell has installed
|
|
113
|
+
# its trap. Wait for that handshake before sending cancellation.
|
|
114
|
+
while [ ! -s "$dog_ready" ] && kill -0 "$dog" 2>/dev/null; do sleep .01; done
|
|
115
|
+
kill -TERM "$dog" 2>/dev/null || :
|
|
116
|
+
wait "$dog" 2>/dev/null || :
|
|
117
|
+
dog=
|
|
118
|
+
fi
|
|
119
|
+
if [ -n "$dog_ready" ]; then rm -f "$dog_ready"; dog_ready=; fi
|
|
120
|
+
}
|
|
92
121
|
run() {
|
|
93
122
|
exec 9<&0
|
|
94
123
|
"$@" <&9 9<&- &
|
|
95
124
|
child=$!
|
|
96
125
|
exec 9<&-
|
|
97
126
|
dog=
|
|
98
|
-
|
|
127
|
+
dog_ready=
|
|
128
|
+
if [ -n "$limit" ]; then
|
|
129
|
+
if ! dog_ready=$(mktemp "${TMPDIR:-/tmp}/lanes-watchdog.XXXXXXXX"); then
|
|
130
|
+
kill -TERM "$child" 2>/dev/null || :
|
|
131
|
+
wait "$child" 2>/dev/null || :
|
|
132
|
+
child=; rc=1; return "$rc"
|
|
133
|
+
fi
|
|
134
|
+
(
|
|
135
|
+
# A foreground sleep can defer the shell's TERM handler, and
|
|
136
|
+
# killing only that shell leaves its sleep behind. An async
|
|
137
|
+
# timer lets wait handle cancellation and reap the owned child.
|
|
138
|
+
timer=; cancelled=
|
|
139
|
+
# The timer has no cleanup. KILL also covers its fork-to-exec
|
|
140
|
+
# window, before sleep has acquired its default signal handlers.
|
|
141
|
+
trap 'cancelled=1; [ -z "$timer" ] || kill -KILL "$timer" 2>/dev/null || :' HUP TERM INT
|
|
142
|
+
sleep "$limit" &
|
|
143
|
+
timer=$!
|
|
144
|
+
printf 'ready\n' > "$dog_ready"
|
|
145
|
+
# TERM can arrive between spawning the timer and assigning $!.
|
|
146
|
+
if [ -n "$cancelled" ]; then kill -KILL "$timer" 2>/dev/null || :; fi
|
|
147
|
+
wait "$timer" 2>/dev/null || :
|
|
148
|
+
if [ -n "$cancelled" ]; then
|
|
149
|
+
wait "$timer" 2>/dev/null || :
|
|
150
|
+
else
|
|
151
|
+
kill -TERM "$child" 2>/dev/null || :
|
|
152
|
+
fi
|
|
153
|
+
) >/dev/null 2>&1 &
|
|
154
|
+
dog=$!
|
|
155
|
+
fi
|
|
99
156
|
rc=0
|
|
100
157
|
wait "$child" || rc=$?
|
|
101
158
|
child=
|
|
102
|
-
|
|
159
|
+
cancel_watchdog
|
|
103
160
|
return "$rc"
|
|
104
161
|
}
|
|
105
162
|
# hold <seconds> [jitter]: wait, but Enter continues at once. No raw mode, so there is no terminal state to restore
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
#!/bin/sh
|
|
2
|
-
# Open the lanes window in its own terminal instance. usage: lanes-window [suffix]
|
|
2
|
+
# Open the lanes window in its own terminal instance. usage: lanes-window [--ascii] [--light|--dark] [suffix]
|
|
3
3
|
# suffix: an independent view session "<device>-<suffix>" (second monitor / other half of an ultrawide).
|
|
4
4
|
# w2 / w3: a WIDE view session with 2 / 3 viewports side by side per workspace (for a full-width window).
|
|
5
|
+
# --ascii / --light / --dark: the cockpit's look for this device (`lanes attach … --ascii|--theme`), remembered on
|
|
6
|
+
# the server in devices/<device>.json and picked up by a running sidebar and its status screens.
|
|
5
7
|
# macOS: Ghostty. Linux: Ghostty when installed, otherwise Foot with the same window class.
|
|
6
8
|
set -u
|
|
7
9
|
config=${XDG_CONFIG_HOME:-$HOME/.config}/lanes
|
|
@@ -9,11 +11,25 @@ launch=$HOME/.local/bin/lanes-launch
|
|
|
9
11
|
class=org.omarchy.lanes
|
|
10
12
|
device=
|
|
11
13
|
file=$config/ghostty.conf
|
|
14
|
+
look=
|
|
15
|
+
while [ $# -gt 0 ]; do
|
|
16
|
+
case "$1" in
|
|
17
|
+
--ascii) look="$look --ascii"; shift ;;
|
|
18
|
+
--light|--dark) look="$look --theme ${1#--}"; shift ;;
|
|
19
|
+
*) break ;;
|
|
20
|
+
esac
|
|
21
|
+
done
|
|
12
22
|
if [ -n "${1:-}" ]; then
|
|
13
23
|
case "$1" in *[!A-Za-z0-9_.-]*) echo 'invalid suffix' >&2; exit 64 ;; esac
|
|
14
24
|
device="$(head -n 1 "$config/device")-$1"
|
|
15
25
|
case "$1" in w2) device="$device --viewports 2" ;; w3) device="$device --viewports 3" ;; esac
|
|
16
26
|
file=$config/ghostty.$1.conf
|
|
27
|
+
elif [ -n "$look" ]; then
|
|
28
|
+
device=$(head -n 1 "$config/device")
|
|
29
|
+
file=$config/ghostty.look.conf
|
|
30
|
+
fi
|
|
31
|
+
if [ -n "$device" ]; then
|
|
32
|
+
device="$device$look"
|
|
17
33
|
sed "s|^command = .*|command = $launch $device|" "$config/ghostty.conf" > "$file"
|
|
18
34
|
fi
|
|
19
35
|
case "$(uname -s)" in
|
|
@@ -8,6 +8,14 @@
|
|
|
8
8
|
# * tmux is NOT affected: it honours TMUX_TMPDIR only, so lane sockets stay in /tmp/tmux-<uid>.
|
|
9
9
|
# * Nothing is created here: no directory (or a read-only one) = no change. Opt out per shell or for good with
|
|
10
10
|
# LANES_TMP_OPTOUT=1 (any value, even empty, counts). LANES_TMP_DIR names another directory.
|
|
11
|
+
#
|
|
12
|
+
# Lane identity is NOT set here (this file is shared by every shell in the container). A tmux lane's shell gets it
|
|
13
|
+
# ONLY from the `-e` flags of its create recipe (`lanes_lib.canonical_recipe`, `tmux new -e …`): LANE_ID
|
|
14
|
+
# (<ws>--<slug>), LANE_WS (<ws>), THROTTLE_TRACE and ANTHROPIC_CUSTOM_HEADERS ('x-throttle-trace: lanes/<ws>/<id>';
|
|
15
|
+
# trace charset [A-Za-z0-9._-/], at most 256). Never export them from a profile or a client: the client that starts
|
|
16
|
+
# a shared board socket's tmux server copies its env into the server's GLOBAL env, which every later session there
|
|
17
|
+
# without its own -e inherits (DEF-1478) — so `lane open` strips them. A running lane gains its identity only when it
|
|
18
|
+
# is next created by a recipe carrying -e; a legacy recipe without -e gives none (`lanes cursor-sync --identity`).
|
|
11
19
|
if [ -z "${LANES_TMP_OPTOUT+set}" ] && [ -d "${LANES_TMP_DIR:-/home/coder/.tmp}" ] \
|
|
12
20
|
&& [ -w "${LANES_TMP_DIR:-/home/coder/.tmp}" ] && [ -x "${LANES_TMP_DIR:-/home/coder/.tmp}" ]; then
|
|
13
21
|
TMPDIR=${LANES_TMP_DIR:-/home/coder/.tmp}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
{"hooks": {
|
|
2
|
+
"UserPromptSubmit": [{"hooks": [{"type": "command", "command": "~/.lz/bin/lane-signal working --event UserPromptSubmit --harness claude", "timeout": 2}]}],
|
|
3
|
+
"PostToolUse": [{"hooks": [{"type": "command", "command": "~/.lz/bin/lane-signal working --event PostToolUse --harness claude", "timeout": 2}]}],
|
|
4
|
+
"Stop": [{"hooks": [{"type": "command", "command": "~/.lz/bin/lane-signal needs_input --event Stop --harness claude", "timeout": 2}]}],
|
|
5
|
+
"Notification": [{"matcher": "permission_prompt", "hooks": [{"type": "command", "command": "~/.lz/bin/lane-signal needs_approval --event Notification --harness claude", "timeout": 2}]},
|
|
6
|
+
{"matcher": "idle_prompt", "hooks": [{"type": "command", "command": "~/.lz/bin/lane-signal needs_input --event Notification --harness claude", "timeout": 2}]}],
|
|
7
|
+
"PermissionRequest": [{"hooks": [{"type": "command", "command": "~/.lz/bin/lane-signal needs_approval --event PermissionRequest --harness claude", "timeout": 2}]}],
|
|
8
|
+
"SessionEnd": [{"hooks": [{"type": "command", "command": "~/.lz/bin/lane-signal shell --event SessionEnd --harness claude", "timeout": 2}]}],
|
|
9
|
+
"StopFailure": [{"hooks": [{"type": "command", "command": "~/.lz/bin/lane-signal errored --event StopFailure --harness claude", "timeout": 2}]}]
|
|
10
|
+
}}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"SessionStart": [
|
|
4
|
+
{"matcher": null, "hooks": [{"type": "command", "command": "~/.lz/bin/lane-signal idle --event SessionStart --harness codex", "timeout": 2}]}
|
|
5
|
+
],
|
|
6
|
+
"UserPromptSubmit": [
|
|
7
|
+
{"matcher": null, "hooks": [{"type": "command", "command": "~/.lz/bin/lane-signal working --event UserPromptSubmit --harness codex", "timeout": 2}]}
|
|
8
|
+
],
|
|
9
|
+
"PostToolUse": [
|
|
10
|
+
{"matcher": null, "hooks": [{"type": "command", "command": "~/.lz/bin/lane-signal working --event PostToolUse --harness codex", "timeout": 2}]}
|
|
11
|
+
],
|
|
12
|
+
"PermissionRequest": [
|
|
13
|
+
{"matcher": null, "hooks": [{"type": "command", "command": "~/.lz/bin/lane-signal needs_approval --event PermissionRequest --harness codex", "timeout": 2}]}
|
|
14
|
+
],
|
|
15
|
+
"Stop": [
|
|
16
|
+
{"matcher": null, "hooks": [{"type": "command", "command": "~/.lz/bin/lane-signal done --event Stop --harness codex", "timeout": 2}]}
|
|
17
|
+
],
|
|
18
|
+
"SessionEnd": [
|
|
19
|
+
{"matcher": null, "hooks": [{"type": "command", "command": "~/.lz/bin/lane-signal shell --event SessionEnd --harness codex", "timeout": 2}]}
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
}
|