@vibelord/lanes 1.0.4 → 1.1.0
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/README.md +52 -15
- package/bin/lanes-install.mjs +8 -2
- package/lib/plan.mjs +34 -1
- package/package.json +2 -2
- package/payload/lanes/README.md +39 -18
- package/payload/lanes/VERSION +1 -1
- package/payload/lanes/bin/lanes +994 -49
- package/payload/lanes/client/lanes-launch +160 -14
- package/payload/lanes/client/ssh.stanza.in +11 -2
- package/payload/lanes/docs/ARCHITECTURE.md +86 -2
- package/payload/lanes/docs/INSTALL.md +296 -43
- package/payload/lanes/docs/RUNBOOK.md +453 -27
- package/payload/lanes/docs/USERGUIDE.md +449 -76
- package/payload/lanes/install/enable-phone.sh +8 -20
- package/payload/lanes/install/enable-web.sh +2 -2
- package/payload/lanes/install/host-install.sh +6 -0
- package/payload/lanes/install.sh +888 -52
- package/payload/lanes/libexec/enrol.py +695 -0
- package/payload/lanes/libexec/enrold.py +1264 -0
- package/payload/lanes/libexec/keys.py +644 -0
- package/payload/lanes/libexec/lanes_lib.py +14 -6
- package/payload/lanes/libexec/members.py +297 -0
- package/payload/lanes/libexec/notify.py +146 -21
- package/payload/lanes/libexec/qr.py +217 -0
- package/payload/lanes/libexec/site_edit.py +237 -0
- package/payload/lanes/libexec/web_tokens.py +315 -0
- package/payload/lanes/site/README.md +13 -1
- package/payload/lanes/site/beyondwork/site.conf +3 -0
- package/payload/lanes/site/site.example.conf +5 -0
- package/payload/lanes/sudo/one-time-root.sh +1 -1
- package/payload/lanes/systemd/lanes-enrold.service +22 -0
- package/payload/lanes/systemd/lanes-notify.service +4 -4
- package/payload/lanes/systemd/lanes-web.service +4 -0
- package/payload/lanes/web/Caddyfile +10 -0
- package/payload/lanes/web/README.md +8 -2
- package/payload/lanes/workspaces.example/README.md +6 -1
- package/payload/manifest.json +7 -4
package/README.md
CHANGED
|
@@ -4,35 +4,53 @@ npm distribution of [Lanes](../../lanes/README.md): shared agent terminals over
|
|
|
4
4
|
registry sync, uploads and a phone interface. The package carries the committed `lanes/` tree and runs its own
|
|
5
5
|
`install.sh`; every option and exit code of that installer applies unchanged.
|
|
6
6
|
|
|
7
|
-
The package is **
|
|
7
|
+
The package is **public**: installing needs no npm account or token. The only site facts it carries are the bundled
|
|
8
|
+
profiles' public SSH host keys and tailnet names and addresses (reachable only inside that tailnet); access to a server
|
|
9
|
+
is still its own key authorization or invite.
|
|
8
10
|
|
|
9
11
|
How to use Lanes once installed (desktop, phone and browser views, keys, uploads, operations): the
|
|
10
12
|
[Lanes user guide](../../lanes/docs/USERGUIDE.md).
|
|
11
13
|
|
|
12
|
-
##
|
|
14
|
+
## Join a site
|
|
13
15
|
|
|
14
16
|
Requirements: Node.js 18+ (for npm only), OpenSSH, and a route to the server (Tailscale for the Beyond Work profile).
|
|
15
17
|
|
|
18
|
+
A member of the site sends you an invite with a one-time code (`lanes invite` on the server). Join the tailnet with
|
|
19
|
+
the Tailscale link in it, on this machine and on your phone; then:
|
|
20
|
+
|
|
16
21
|
```sh
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
lanes-
|
|
22
|
+
npx @vibelord/lanes --invite LN-xxxx-xxxxxxxxxxxx # the prefix picks the bundled site (LN = beyondwork)
|
|
23
|
+
lanes-launch # your desktop view (lanes-window: in its own window)
|
|
24
|
+
lanes-launch web phone # your phone's view: QR code of the URL, then a login token
|
|
25
|
+
lanes-launch web laptop # this laptop's browser view
|
|
20
26
|
```
|
|
21
27
|
|
|
22
|
-
|
|
23
|
-
|
|
28
|
+
The wrapper prints `site beyondwork, inferred from the invite code's prefix LN` and passes the code to the installer,
|
|
29
|
+
which redeems it at the server's enrolment service: `invite: redeemed — this machine's key is now authorized for
|
|
30
|
+
<login> (device <device>)`. An unknown prefix is refused with the bundled list; `--site` given explicitly wins. The
|
|
31
|
+
code is never printed, stored, or put on a command line. `LANES_INVITE` works like `--invite`.
|
|
32
|
+
|
|
33
|
+
**Without a code:**
|
|
24
34
|
|
|
25
35
|
```sh
|
|
26
|
-
|
|
36
|
+
npx @vibelord/lanes --site beyondwork # install or update the client
|
|
37
|
+
lanes-window # open the desktop view
|
|
27
38
|
```
|
|
28
39
|
|
|
29
|
-
|
|
40
|
+
When the server does not know this machine's key, the installer offers, on a terminal, to ask the site's members for
|
|
41
|
+
access; a member approves it on their phone (`/enrol/pending`) or with `lanes enrol approve <id>` on the server, while
|
|
42
|
+
the installer waits up to 10 minutes (`npx @vibelord/lanes --resume` waits again). Other sites pass their own profile:
|
|
43
|
+
`npx @vibelord/lanes --site /path/to/site.conf`.
|
|
30
44
|
|
|
31
|
-
|
|
32
|
-
the server (send the printed **public** key to the admin, then run `npx @vibelord/lanes --check`) · `4` installed,
|
|
33
|
-
but the server is unreachable (Tailscale?) · `1` error.
|
|
45
|
+
Never put the organization's publish token on an installing machine; it needs no npm credential.
|
|
34
46
|
|
|
35
|
-
|
|
47
|
+
Exit status is the installer's: `0` ready · `3` installed, but this machine's SSH key is not yet authorized on
|
|
48
|
+
the server (a refused code, an unanswered request, or a server without the enrolment service: send the printed
|
|
49
|
+
**public** key to the admin, then run `npx @vibelord/lanes --check`; a device name or key the server already has: run
|
|
50
|
+
the printed command again with `--device NAME` or a key of its own — the invite is still valid) · `4` installed, but
|
|
51
|
+
this machine is not in the
|
|
52
|
+
tailnet (the installer names the Tailscale install command, or opens `tailscale login` and waits up to 120 s) or the
|
|
53
|
+
server is unreachable · `1` error.
|
|
36
54
|
|
|
37
55
|
## Server
|
|
38
56
|
|
|
@@ -43,6 +61,14 @@ npx @vibelord/lanes --server --check
|
|
|
43
61
|
npx @vibelord/lanes --server
|
|
44
62
|
```
|
|
45
63
|
|
|
64
|
+
The server install also enables the enrolment service, `lanes-enrold`, that redeems invites and takes access
|
|
65
|
+
requests. Once its operator check passes, pin request mode with `lanes enrol mode request` (`invite` for invites
|
|
66
|
+
only), so no restart changes it. Members then invite from the server, in their own terminal, with `lanes invite <name>
|
|
67
|
+
--email <address>`, decide requests with `lanes enrol approve <id>`, and remove a machine — key, browsers and phones,
|
|
68
|
+
view — with `lanes offboard <device> --yes`; see the [install guide](../../lanes/docs/INSTALL.md#invite-a-user).
|
|
69
|
+
Until the server runs this release, `lanes-launch web` from an updated client opens the view without a QR code and
|
|
70
|
+
says the server is older.
|
|
71
|
+
|
|
46
72
|
## What the wrapper does
|
|
47
73
|
|
|
48
74
|
`lanes-install` copies the bundled tree to `~/.local/share/lanes/pkg/<version>/` (`$LANES_PKG_ROOT` overrides the
|
|
@@ -53,11 +79,15 @@ server still runs from are kept.
|
|
|
53
79
|
| Option | Effect |
|
|
54
80
|
| --- | --- |
|
|
55
81
|
| `--site NAME` | a bundled profile (`beyondwork`); a value with `/` or `.conf` is your own file |
|
|
82
|
+
| `--invite CODE` | passed to the installer; without `--site`, the code's prefix picks the bundled profile (`LN` → `beyondwork`) |
|
|
56
83
|
| `--stage-only` | stage the tree and print its path, run nothing |
|
|
57
84
|
| `--version` | print the package version |
|
|
58
85
|
| `--wrapper-help` | wrapper help only |
|
|
59
86
|
|
|
60
|
-
Update: re-run `npx @vibelord/lanes@latest`
|
|
87
|
+
Update: `lanes-launch update` on the client, or re-run `npx @vibelord/lanes@latest` — the installer repeats the
|
|
88
|
+
options of the last install (remembered in `~/.config/lanes/install-options`; a flag given again overrides one), and
|
|
89
|
+
`lanes-launch update` also gives each remembered option to it again as its flag. Remove the client:
|
|
90
|
+
`npx @vibelord/lanes --uninstall`.
|
|
61
91
|
|
|
62
92
|
## Maintainers
|
|
63
93
|
|
|
@@ -70,4 +100,11 @@ scripts/publish.sh --dry-run # then without --dry-run
|
|
|
70
100
|
```
|
|
71
101
|
|
|
72
102
|
`publish.sh` reads the publish token from the main checkout's `.env.local` (`NPMJS2` by default; override with
|
|
73
|
-
`LANES_NPM_ENV_FILE` / `LANES_NPM_TOKEN_KEY`) and hands it to npm through the environment only.
|
|
103
|
+
`LANES_NPM_ENV_FILE` / `LANES_NPM_TOKEN_KEY`) and hands it to npm through the environment only. It publishes with
|
|
104
|
+
`--access public`, as `publishConfig.access` says; `npm test` pins both, and fails when a bundled site profile carries
|
|
105
|
+
more than its public host key, tailnet name and tailnet address, any shipped file looks like a key or token, or a
|
|
106
|
+
shipped file names an e-mail address outside the example domains.
|
|
107
|
+
|
|
108
|
+
Access is a setting of the whole package, not of one version: the first public publish makes every version already
|
|
109
|
+
on the registry publicly installable too, including 1.0.0–1.0.4, which were published for `@vibelord` members only.
|
|
110
|
+
Deprecating or unpublishing an earlier version is a separate, deliberate `npm deprecate` / `npm unpublish` step.
|
package/bin/lanes-install.mjs
CHANGED
|
@@ -20,7 +20,7 @@ import os from 'node:os';
|
|
|
20
20
|
import path from 'node:path';
|
|
21
21
|
import { fileURLToPath } from 'node:url';
|
|
22
22
|
|
|
23
|
-
import { installArgs, platformError, prunable, splitArgs } from '../lib/plan.mjs';
|
|
23
|
+
import { inferredSite, installArgs, platformError, prunable, splitArgs } from '../lib/plan.mjs';
|
|
24
24
|
|
|
25
25
|
const packageDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
26
26
|
const payload = path.join(packageDir, 'payload');
|
|
@@ -105,6 +105,7 @@ function prune(root, target) {
|
|
|
105
105
|
function usage(manifest) {
|
|
106
106
|
process.stdout.write(`@vibelord/lanes ${manifest.version} — installs Lanes from this package
|
|
107
107
|
|
|
108
|
+
npx @vibelord/lanes --invite LN-… join a site with an invite code (the code's prefix picks the site)
|
|
108
109
|
npx @vibelord/lanes --site beyondwork install or update the client (macOS / Linux)
|
|
109
110
|
npx @vibelord/lanes --check verify SSH path, key and server; changes nothing
|
|
110
111
|
npx @vibelord/lanes --server install or update the Linux workspace server
|
|
@@ -116,6 +117,7 @@ Wrapper options
|
|
|
116
117
|
--wrapper-help this text
|
|
117
118
|
|
|
118
119
|
Bundled site profiles: ${manifest.sites.join(', ') || 'none'} (or --site /path/to/site.conf)
|
|
120
|
+
Invite prefixes: ${Object.entries(manifest.site_codes || {}).map(([p, site]) => `${p} → ${site}`).join(', ') || 'none'} (an explicit --site wins)
|
|
119
121
|
Every other option goes to install.sh; its own help follows.
|
|
120
122
|
|
|
121
123
|
`);
|
|
@@ -144,11 +146,15 @@ function main() {
|
|
|
144
146
|
if (help) usage(manifest);
|
|
145
147
|
if (own.has('--wrapper-help')) return 0;
|
|
146
148
|
let args;
|
|
149
|
+
let inferred;
|
|
147
150
|
try {
|
|
148
|
-
|
|
151
|
+
const codes = manifest.site_codes || {};
|
|
152
|
+
args = installArgs(passthrough, staged, manifest.sites, codes, process.env);
|
|
153
|
+
inferred = inferredSite(passthrough, codes, process.env);
|
|
149
154
|
} catch (error) {
|
|
150
155
|
fail(error.message);
|
|
151
156
|
}
|
|
157
|
+
if (inferred) process.stdout.write(`site ${inferred.site}, inferred from the invite code's prefix ${inferred.prefix}\n`);
|
|
152
158
|
// Run through the staged path (not the npx cache), so install.sh resolves its checkout to the stable tree.
|
|
153
159
|
const result = spawnSync('sh', [path.join(staged, 'install.sh'), ...args], { stdio: 'inherit', cwd: staged });
|
|
154
160
|
if (result.error) fail(`could not run install.sh: ${result.error.message}`);
|
package/lib/plan.mjs
CHANGED
|
@@ -28,8 +28,10 @@ export function splitArgs(argv) {
|
|
|
28
28
|
// Rewrite install.sh arguments for a staged tree:
|
|
29
29
|
// * `--site NAME` names a profile bundled under lanes/site/NAME/ (a value with a `/` or ending in `.conf` stays a
|
|
30
30
|
// path the user owns);
|
|
31
|
+
// * without `--site`, an invite code (`--invite CODE`, or LANES_INVITE in `env`) picks the bundled profile its prefix
|
|
32
|
+
// names (`LN-…` -> the site whose site.conf says LANES_SITE_CODE=LN); the code itself passes through unchanged;
|
|
31
33
|
// * `--server` must be install.sh's FIRST argument (it dispatches on $1 only).
|
|
32
|
-
export function installArgs(passthrough, stagedDir, sites) {
|
|
34
|
+
export function installArgs(passthrough, stagedDir, sites, siteCodes = {}, env = {}) {
|
|
33
35
|
const out = [];
|
|
34
36
|
let server = false;
|
|
35
37
|
for (let i = 0; i < passthrough.length; i += 1) {
|
|
@@ -45,9 +47,40 @@ export function installArgs(passthrough, stagedDir, sites) {
|
|
|
45
47
|
}
|
|
46
48
|
out.push(arg);
|
|
47
49
|
}
|
|
50
|
+
const inferred = inferredSite(passthrough, siteCodes, env);
|
|
51
|
+
if (inferred) out.unshift('--site', resolveSite(inferred.site, stagedDir, sites));
|
|
48
52
|
return server ? ['--server', ...out] : out;
|
|
49
53
|
}
|
|
50
54
|
|
|
55
|
+
// The invite code of this run: the last `--invite CODE`, else LANES_INVITE; null when there is none.
|
|
56
|
+
export function inviteCode(passthrough, env = {}) {
|
|
57
|
+
let code = null;
|
|
58
|
+
for (let i = 0; i < passthrough.length; i += 1) {
|
|
59
|
+
if (passthrough[i] !== '--invite') continue;
|
|
60
|
+
if (i + 1 >= passthrough.length) throw new Error('--invite CODE: the code is missing');
|
|
61
|
+
code = passthrough[i + 1];
|
|
62
|
+
i += 1;
|
|
63
|
+
}
|
|
64
|
+
if (code === null && env.LANES_INVITE) code = env.LANES_INVITE;
|
|
65
|
+
return code === null ? null : code.replace(/\s+/g, '');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// {site, prefix} when an invite code picks the profile (no explicit --site), else null. Messages name the prefix at
|
|
69
|
+
// most, never the code: it is a secret until redeemed.
|
|
70
|
+
export function inferredSite(passthrough, siteCodes, env = {}) {
|
|
71
|
+
if (passthrough.includes('--site') || passthrough.includes('--server')) return null;
|
|
72
|
+
const code = inviteCode(passthrough, env);
|
|
73
|
+
if (code === null) return null;
|
|
74
|
+
const match = /^([A-Za-z]{1,8})-[A-Za-z0-9-]+$/.exec(code);
|
|
75
|
+
if (!match) throw new Error('not an invite code (letters, digits and dashes, like LN-xxxx-xxxxxxxxxxxx)');
|
|
76
|
+
const prefix = match[1].toUpperCase();
|
|
77
|
+
if (!Object.hasOwn(siteCodes, prefix)) {
|
|
78
|
+
const known = Object.entries(siteCodes).map(([p, site]) => `${p} → ${site}`).join(', ') || 'none';
|
|
79
|
+
throw new Error(`unknown invite prefix '${prefix}' (bundled: ${known}); or pass --site /path/to/site.conf`);
|
|
80
|
+
}
|
|
81
|
+
return { site: siteCodes[prefix], prefix };
|
|
82
|
+
}
|
|
83
|
+
|
|
51
84
|
export function resolveSite(value, stagedDir, sites) {
|
|
52
85
|
if (value.includes('/') || value.endsWith('.conf')) return value;
|
|
53
86
|
if (!sites.includes(value)) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vibelord/lanes",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Installer for Lanes: shared agent terminals over SSH with Zellij views, registry sync, uploads and a phone interface.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"test": "node --test"
|
|
28
28
|
},
|
|
29
29
|
"publishConfig": {
|
|
30
|
-
"access": "
|
|
30
|
+
"access": "public"
|
|
31
31
|
},
|
|
32
32
|
"repository": {
|
|
33
33
|
"type": "git",
|
package/payload/lanes/README.md
CHANGED
|
@@ -14,15 +14,16 @@ when someone closes it explicitly.
|
|
|
14
14
|
|
|
15
15
|
**Who uses it.** Operators and teammates who run several Claude Code or Codex agents at once use Lanes daily to see
|
|
16
16
|
which lane needs them, switch to it, answer, and move on, from a desk or from a phone. An administrator installs the
|
|
17
|
-
server once on the workspace host
|
|
18
|
-
Mac or Linux machine
|
|
17
|
+
server once on the workspace host. Any member invites a new person with `lanes invite`, and that person joins from
|
|
18
|
+
their own Mac or Linux machine with one command, `npx @vibelord/lanes --invite <code>`, which installs the client and
|
|
19
|
+
authorizes the machine.
|
|
19
20
|
|
|
20
21
|
**Three surfaces, one set of lanes:**
|
|
21
22
|
|
|
22
23
|
| Surface | How you reach it | Typical use |
|
|
23
24
|
| --- | --- | --- |
|
|
24
25
|
| Desktop terminal | `lanes-window` (own Ghostty window) or `lanes-launch` in any terminal, over SSH | Daily work: workspace tabs, the sidebar, one to three lanes side by side |
|
|
25
|
-
| Browser |
|
|
26
|
+
| Browser | `lanes-launch web phone` or `lanes-launch web laptop` from a client: a private URL over Tailscale with a login token (on a phone, Add to Home Screen) | Checking and answering agents away from the desk |
|
|
26
27
|
| Editor | Cursor / VS Code terminals that run `lane open <lane-id>` inside the container | Opening the same lane next to the code |
|
|
27
28
|
|
|
28
29
|
**How it relates to the rest of the repository.**
|
|
@@ -46,15 +47,21 @@ Phone / tablet / laptop ── HTTPS ────┘ Zellij views
|
|
|
46
47
|
|
|
47
48
|
## Quick start
|
|
48
49
|
|
|
49
|
-
**
|
|
50
|
+
**Join a site with an invite** (public npm package, no npm login; Node.js 18+ for npm only). Join the tailnet with
|
|
51
|
+
the Tailscale link in the invite, then:
|
|
50
52
|
|
|
51
53
|
```sh
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
lanes-
|
|
54
|
+
npx @vibelord/lanes --invite LN-xxxx-xxxxxxxxxxxx # installs the client and authorizes this machine
|
|
55
|
+
lanes-launch # your desktop view (lanes-window: in its own window)
|
|
56
|
+
lanes-launch web phone # your phone's view: QR code of the URL, then a login token
|
|
57
|
+
lanes-launch web laptop # this laptop's browser view
|
|
55
58
|
```
|
|
56
59
|
|
|
57
|
-
The
|
|
60
|
+
The code's prefix picks the site. Without a code, `npx @vibelord/lanes --site beyondwork` installs the client and, in
|
|
61
|
+
your own terminal, offers to ask the site's members for access; a member approves it on their phone or with
|
|
62
|
+
`lanes enrol approve <id>`. Later, `lanes-launch update` updates the client with the same choices. See the user
|
|
63
|
+
guide's [Join a site](docs/USERGUIDE.md#2-join-a-site). The package runs the same
|
|
64
|
+
installer with the same options and exit codes; see [`npm/lanes`](../npm/lanes/README.md).
|
|
58
65
|
|
|
59
66
|
**From a checkout:** clone the repository once on each machine that needs it:
|
|
60
67
|
|
|
@@ -77,9 +84,13 @@ Beyond Work's existing deployment has an explicit profile:
|
|
|
77
84
|
```
|
|
78
85
|
|
|
79
86
|
The client works on macOS and Linux. It needs OpenSSH and a route to the server (Tailscale for the supplied
|
|
80
|
-
profile
|
|
81
|
-
|
|
82
|
-
|
|
87
|
+
profile: the installer checks it first, names the install command when it is missing and opens `tailscale login`
|
|
88
|
+
when this machine is logged out). Ghostty is recommended for a separate window; `lanes-launch` works in an existing
|
|
89
|
+
terminal.
|
|
90
|
+
The installer uses an existing SSH key or creates a dedicated one. An invite code or an approved access request
|
|
91
|
+
authorizes it through the server's enrolment service; otherwise an administrator authorizes its **public** key on the
|
|
92
|
+
server (`lanes keys add`). Re-run `./install.sh --check` after authorization. Your site and every other install choice
|
|
93
|
+
are remembered.
|
|
83
94
|
|
|
84
95
|
When an ended view was rebuilt on the server, the client probes that state and reconnects
|
|
85
96
|
automatically. The probe's watchdog cancels and reaps its timer when the probe completes;
|
|
@@ -94,14 +105,16 @@ client installer after updating their checkout.
|
|
|
94
105
|
./install.sh --server
|
|
95
106
|
```
|
|
96
107
|
|
|
97
|
-
Then configure the workspace catalog and install the container helper
|
|
98
|
-
[installation guide](docs/INSTALL.md) for prerequisites, a complete
|
|
108
|
+
Then configure the workspace catalog and install the container helper — for one user's container, `lanes onboard`
|
|
109
|
+
does both and attaches its agents. Follow the [installation guide](docs/INSTALL.md) for prerequisites, a complete
|
|
110
|
+
example, invites, phone access, upgrades and removal.
|
|
99
111
|
The installer starts an empty catalog safely; it does not assume anyone's containers, projects or login name.
|
|
100
112
|
|
|
101
113
|
## Daily use
|
|
102
114
|
|
|
103
115
|
| Action | Command or control |
|
|
104
116
|
| --- | --- |
|
|
117
|
+
| Join a site from a new machine | `npx @vibelord/lanes --invite <code>` with the code from a member's invite: installs the client, authorizes this machine, then `lanes-launch`, `lanes-launch web phone`, `lanes-launch web laptop`. Without a code the installer asks the site's members for access ([guide](docs/USERGUIDE.md#2-join-a-site)) |
|
|
105
118
|
| Open desktop view | `lanes-window` or the Lanes app; `lanes-launch` inside any terminal |
|
|
106
119
|
| Independent second view | `lanes-window 2` |
|
|
107
120
|
| Two or three panes per workspace | `lanes-window w2` / `lanes-window w3` |
|
|
@@ -118,6 +131,8 @@ The installer starts an empty catalog safely; it does not assume anyone's contai
|
|
|
118
131
|
| Register an existing session | **+** beside an unregistered session, or select it and press `a`; choose a workspace (`lanes register <lane-id> [workspace]` on the server) |
|
|
119
132
|
| Declare a session in the repository | `P` in the sidebar, then `y`: writes the session's binding into the workspace's one repository `terminals.json`, so it becomes a repository lane `<workspace>--<slug>` |
|
|
120
133
|
| Rename a lane | `R` in the sidebar (Enter saves, Esc cancels), or `lanes rename <lane-id> <name>` on the server; the lane id never changes |
|
|
134
|
+
| Add a workspace (board) | `lanes workspaces add <id> --container <name> --repo /home/coder/<repo> [--title T]` on the server prints the catalog and manifest diff; add `--write` to apply, then `lanes sync` ([install](docs/INSTALL.md#2-configure-workspaces-and-editor-sync)) |
|
|
135
|
+
| Onboard a user's container | `lanes onboard <login> --container <name> --workspace <id> --repo /home/coder/<repo>` on the server prints every step (board, contract, Claude and Codex hooks, Throttle attachment, canonical recipes, sync, doctor); add `--write` to apply them and keep a receipt ([install](docs/INSTALL.md#onboard-a-users-container)) |
|
|
121
136
|
| See which agents need you | The sidebar sorts **Needs you** (◆ approval, ✗ error, ◇ waiting for a prompt), **Working** (▸) and **Done** (✓, until you open the lane) above the rest; the header counts them in the same glyphs (`◆1 ◇2 ▸6 ✓1`, zeros left out; `N lanes` when no lane runs an agent) |
|
|
122
137
|
| Read a sidebar row | `▌` cursor · agent glyph · `›` the lane this pane shows (`²`/`³`: shown in pane 2/3) · the name (`·` per other viewer, `…` when clipped) · an **age column** on every row (agent turn, else last output: `45s`, ` 3m`, ` 2h`, ` 4d`; amber in **Needs you**) · stream glyph · one tail cell by priority (`?` not observed right now > `⚠` starving: producing output but no request for over 10 min, or a 429 > `↓` downgraded > `$` mostly paid — the economy cells need the opt-in economy poller; a dim `⚠`/`↓` is a frozen last-known figure, not a live alarm) · `×` close. `⋮ N more` ends a list that continues below |
|
|
123
138
|
| End an unregistered session and its jobs | **×** beside the session, or select it and press `x`; confirm with `y` (any other key cancels) |
|
|
@@ -126,9 +141,12 @@ The installer starts an empty catalog safely; it does not assume anyone's contai
|
|
|
126
141
|
| Other workspaces that need you | The row above the footer rule: ` ◆1 Worlds · ◇2 Code` (blank and absent when none; panes of 18 rows or more) |
|
|
127
142
|
| What the fleet spends | With the opt-in economy poller ([RUNBOOK](docs/RUNBOOK.md#economy-projection-opt-in)) the same row is the fleet economy strip instead: ` $8.5k/16k 53% · F2 S1 · spill 76% · free 3/19` — overflow spend against the daily cap (amber at the alert level), new upstream 429s, this workspace's lanes by model tier (`F` fable, `O` opus, `S` sonnet, `H` haiku, `G` gpt), the pool's spill fraction, free accounts and the next 5 h reset, whole parts dropped from the end to fit; a leading `?` means the figures are frozen — the poller could not reach the daemon, or it stopped (no rewrite for over 3 min) |
|
|
128
143
|
| Upload | Click the one-row upload strip above the footer (` ↑ uploads · click, then drop (U)`) or press `U`, then drop anywhere on the window; or use `~/Lanes Uploads/<workspace>/`, or `lanes-upload <files>` |
|
|
129
|
-
| Phone
|
|
130
|
-
| Laptop or tablet browser view | `lanes web <device> [--desktop\|--mobile] [--viewports N] [--token \| --print-only]`
|
|
144
|
+
| Phone view from a client | `lanes-launch web phone` in your own terminal: prepares `<your device>-phone` (phone layout), makes Tailscale Serve forward private HTTPS 8443 when the mapping is missing, prints the private URL with a QR code of it and a login token; scan the code or open the URL in Safari. `sh install/enable-phone.sh --host lanes` is the older one-shot for the device `phone` ([guide](docs/USERGUIDE.md#phone-safari-home-screen)) |
|
|
145
|
+
| Laptop or tablet browser view | `lanes-launch web laptop` (`<your device>-web`, desktop layout) or `lanes-launch web <device> [--desktop\|--mobile]` from a client; on the server `lanes web <device> [--desktop\|--mobile] [--viewports N] [--token \| --print-only]` (prepares the view, enables the loopback web services, adds the Tailscale Serve `https:8443` mapping when missing, prints the private URL) ([guide](docs/INSTALL.md#laptop-and-tablet-browser)) |
|
|
146
|
+
| Web login tokens | `lanes token list` (each token with the device it was created for), `lanes token revoke <device>`, `lanes token revoke-all --yes` ([runbook](docs/RUNBOOK.md#login-tokens)) |
|
|
147
|
+
| Update the client | `lanes-launch update` (the newest client from npm with your remembered install options; flags you add win). A client older than the server says so in one dim line when it attaches |
|
|
131
148
|
| Switch workspace or lane in a laptop browser | The **Lanes** chip (top right) opens the workspace and lane pickers |
|
|
149
|
+
| Invite a teammate, approve a machine, remove one | On the server, in your own terminal: `lanes invite <name> --email <addr>` prints a five-line welcome kit with a one-time code (shown once, on a terminal only); `lanes enrol list` / `lanes enrol approve <id>` decide machines that asked without a code; `lanes enrol mode request` pins request mode once the Serve header check passed; `lanes offboard <device> --yes` revokes a machine's key, signs its browsers and phones out and ends its view ([guide](docs/USERGUIDE.md#invite-a-user-administrator), [install](docs/INSTALL.md#offboard-a-machine)) |
|
|
132
150
|
| Detach | Close the window, or Ctrl–Alt–O then `d`; the agents keep running |
|
|
133
151
|
|
|
134
152
|
Unregistered sessions also appear when they use shared or previously unlisted tmux servers. Reopening a
|
|
@@ -217,8 +235,10 @@ See [workspace configuration](workspaces.example/README.md) and the [architectur
|
|
|
217
235
|
|
|
218
236
|
## Update and verify
|
|
219
237
|
|
|
220
|
-
After `git pull`, clients re-run `./install.sh
|
|
221
|
-
|
|
238
|
+
After `git pull`, clients re-run `./install.sh` (it repeats the options remembered in
|
|
239
|
+
`~/.config/lanes/install-options`); servers re-run `./install.sh --server` from their installed checkout. npm
|
|
240
|
+
client installations update with `lanes-launch update` (`npx @vibelord/lanes@latest` with those options given again
|
|
241
|
+
as flags); a server updates with `npx @vibelord/lanes@latest --server` on the host. After a server
|
|
222
242
|
update, restart `lanes-statusd.service` (and `lanes-web`/`lanes-mobile` when their units or the pinned Zellij
|
|
223
243
|
changed); existing views keep their layout until `lanes rebuild <device>`. `lanes hello` prints the server's
|
|
224
244
|
version ([user guide](docs/USERGUIDE.md#update)). The server installer resolves the checkout's actual path,
|
|
@@ -238,7 +258,8 @@ For recovery, service updates and troubleshooting, see the [runbook](docs/RUNBOO
|
|
|
238
258
|
|
|
239
259
|
The host's site catalog (`LANES_WORKSPACES/catalog.json`, normally
|
|
240
260
|
`~/.config/lanes/workspaces/catalog.json`) is the workspace authority. Add its
|
|
241
|
-
manifest there
|
|
261
|
+
manifest there (`lanes workspaces add <id> --container <name> --repo <path>
|
|
262
|
+
--write` writes a one-repository board) and run `lanes sync`. The existing sync timer refreshes it every
|
|
242
263
|
2 minutes; the ensure timer also reconciles known device sessions every minute.
|
|
243
264
|
New desktop and phone tabs appear without rebuilding sessions or restarting
|
|
244
265
|
agents. Changing a catalog title renames the existing tab using its workspace ID.
|
package/payload/lanes/VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
1.0
|
|
1
|
+
1.1.0
|