@timqi/pier 0.1.0 → 0.1.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/README.md +58 -125
- package/dist/agent/config.js +6 -15
- package/dist/agent/credentials.js +11 -23
- package/dist/agent/events.js +42 -64
- package/dist/agent/listing.js +39 -92
- package/dist/agent/pi.js +103 -252
- package/dist/boards/boards.js +19 -29
- package/dist/channels/attach.js +14 -42
- package/dist/channels/chains.js +33 -37
- package/dist/channels/chunk.js +8 -28
- package/dist/channels/commands.js +3 -14
- package/dist/channels/config.js +33 -52
- package/dist/channels/control.js +4 -13
- package/dist/channels/conversations.js +8 -25
- package/dist/channels/dedup.js +8 -17
- package/dist/channels/gatekeeper.js +13 -23
- package/dist/channels/lark-api.js +23 -63
- package/dist/channels/lark-outbound.js +12 -44
- package/dist/channels/lark-panel.js +7 -23
- package/dist/channels/lark-render.js +18 -62
- package/dist/channels/lark.js +52 -141
- package/dist/channels/lines.js +13 -15
- package/dist/channels/panel.js +16 -36
- package/dist/channels/receipts.js +29 -52
- package/dist/channels/routes.js +3 -9
- package/dist/channels/runtime.js +12 -23
- package/dist/channels/slack-api.js +34 -86
- package/dist/channels/slack-directory.js +7 -23
- package/dist/channels/slack-outbound.js +12 -56
- package/dist/channels/slack-panel.js +4 -13
- package/dist/channels/slack-render.js +23 -91
- package/dist/channels/slack-tool.js +48 -171
- package/dist/channels/slack.js +73 -239
- package/dist/channels/telegram-api.js +8 -20
- package/dist/channels/telegram-panel.js +5 -21
- package/dist/channels/telegram-render.js +13 -40
- package/dist/channels/telegram.js +54 -146
- package/dist/channels/types.js +5 -16
- package/dist/cli.js +17 -41
- package/dist/config-sync.js +87 -4
- package/dist/core/hub.js +7 -20
- package/dist/core/identity.js +20 -59
- package/dist/core/inbound-file.js +15 -49
- package/dist/core/inbox.js +12 -34
- package/dist/core/queue.js +3 -5
- package/dist/core/reply.js +41 -142
- package/dist/core/router.js +209 -264
- package/dist/core/types.js +4 -0
- package/dist/db.js +88 -272
- package/dist/drain.js +57 -50
- package/dist/extensions/index.js +3 -11
- package/dist/extensions/web/anthropic.js +3 -9
- package/dist/extensions/web/artifacts.js +2 -5
- package/dist/extensions/web/content.js +4 -12
- package/dist/extensions/web/http.js +2 -6
- package/dist/extensions/web/language.js +8 -18
- package/dist/extensions/web/openai.js +1 -1
- package/dist/extensions/web/provider.js +5 -18
- package/dist/extensions/web/tools.js +19 -63
- package/dist/lock.js +98 -0
- package/dist/log.js +9 -26
- package/dist/main.js +84 -183
- package/dist/paths.js +10 -26
- package/dist/secrets.js +18 -45
- package/dist/service.js +31 -73
- package/dist/settings.js +19 -63
- package/dist/tasks/agent.js +24 -45
- package/dist/tasks/callbacks.js +8 -16
- package/dist/tasks/command.js +2 -6
- package/dist/tasks/definitions.js +39 -62
- package/dist/tasks/execution.js +41 -39
- package/dist/tasks/groups.js +8 -11
- package/dist/tasks/messages.js +88 -155
- package/dist/tasks/outbox.js +33 -54
- package/dist/tasks/routes.js +4 -7
- package/dist/tasks/runs.js +4 -9
- package/dist/tasks/service.js +29 -42
- package/dist/tasks/store.js +36 -27
- package/dist/tasks/tool.js +59 -60
- package/dist/tools-task.js +20 -60
- package/dist/tools.js +98 -325
- package/dist/update.js +20 -43
- package/dist/web/auth.js +118 -179
- package/dist/web/config-sync.js +2 -2
- package/dist/web/config.js +3 -7
- package/dist/web/explorer.js +10 -21
- package/dist/web/fs.js +20 -42
- package/dist/web/instance.js +35 -82
- package/dist/web/providers.js +5 -11
- package/dist/web/public/assets/{activity-Bl3vZukb.js → activity-DOr8dWeX.js} +1 -1
- package/dist/web/public/assets/activity-DOr8dWeX.js.br +0 -0
- package/dist/web/public/assets/activity-DOr8dWeX.js.gz +0 -0
- package/dist/web/public/assets/{boards-DYuf4Mlj.js → boards-CneyR23E.js} +1 -1
- package/dist/web/public/assets/boards-CneyR23E.js.br +0 -0
- package/dist/web/public/assets/boards-CneyR23E.js.gz +0 -0
- package/dist/web/public/assets/explorer-D0srXT1c.js +4 -0
- package/dist/web/public/assets/explorer-D0srXT1c.js.br +0 -0
- package/dist/web/public/assets/explorer-D0srXT1c.js.gz +0 -0
- package/dist/web/public/assets/index-DbFu15NN.js +85 -0
- package/dist/web/public/assets/index-DbFu15NN.js.br +0 -0
- package/dist/web/public/assets/index-DbFu15NN.js.gz +0 -0
- package/dist/web/public/assets/index-eqQLVS8Q.css +2 -0
- package/dist/web/public/assets/index-eqQLVS8Q.css.br +0 -0
- package/dist/web/public/assets/index-eqQLVS8Q.css.gz +0 -0
- package/dist/web/public/assets/{runs-BLJu7EXN.js → runs-Cv0A-e08.js} +1 -1
- package/dist/web/public/assets/runs-Cv0A-e08.js.br +0 -0
- package/dist/web/public/assets/runs-Cv0A-e08.js.gz +0 -0
- package/dist/web/public/assets/{settings-BrdVh-Zi.js → settings-VmCjhGBd.js} +1 -1
- package/dist/web/public/assets/settings-VmCjhGBd.js.br +0 -0
- package/dist/web/public/assets/settings-VmCjhGBd.js.gz +0 -0
- package/dist/web/public/assets/{task-runs-CeQS1rxa.js → task-runs-CqpTV644.js} +1 -1
- package/dist/web/public/assets/task-runs-CqpTV644.js.br +0 -0
- package/dist/web/public/assets/task-runs-CqpTV644.js.gz +0 -0
- package/dist/web/public/assets/tasks-BffPVgXg.js +4 -0
- package/dist/web/public/assets/tasks-BffPVgXg.js.br +0 -0
- package/dist/web/public/assets/tasks-BffPVgXg.js.gz +0 -0
- package/dist/web/public/index.html +30 -16
- package/dist/web/public/index.html.br +0 -0
- package/dist/web/public/index.html.gz +0 -0
- package/dist/web/public/sw.js +14 -2
- package/dist/web/public/sw.js.br +0 -0
- package/dist/web/public/sw.js.gz +0 -0
- package/dist/web/push.js +55 -77
- package/dist/web/route.js +3 -7
- package/dist/web/server.js +109 -190
- package/dist/web/session-state.js +13 -53
- package/dist/web/types.js +2 -4
- package/dist/web/webpush.js +10 -25
- package/docs/deploy.md +115 -330
- package/package.json +1 -1
- package/skills/pier-boards/SKILL.md +81 -160
- package/skills/pier-help/SKILL.md +23 -20
- package/skills/pier-slack/SKILL.md +2 -2
- package/skills/pier-tasks/SKILL.md +23 -15
- package/dist/config-sync-fetch.js +0 -84
- package/dist/limits.js +0 -14
- package/dist/web/public/assets/activity-Bl3vZukb.js.br +0 -0
- package/dist/web/public/assets/activity-Bl3vZukb.js.gz +0 -0
- package/dist/web/public/assets/boards-DYuf4Mlj.js.br +0 -0
- package/dist/web/public/assets/boards-DYuf4Mlj.js.gz +0 -0
- package/dist/web/public/assets/explorer-qJH_9nTE.js +0 -4
- package/dist/web/public/assets/explorer-qJH_9nTE.js.br +0 -0
- package/dist/web/public/assets/explorer-qJH_9nTE.js.gz +0 -0
- package/dist/web/public/assets/index-Dqdb-Eqt.js +0 -85
- package/dist/web/public/assets/index-Dqdb-Eqt.js.br +0 -0
- package/dist/web/public/assets/index-Dqdb-Eqt.js.gz +0 -0
- package/dist/web/public/assets/index-DzmMzvi_.css +0 -2
- package/dist/web/public/assets/index-DzmMzvi_.css.br +0 -0
- package/dist/web/public/assets/index-DzmMzvi_.css.gz +0 -0
- package/dist/web/public/assets/runs-BLJu7EXN.js.br +0 -0
- package/dist/web/public/assets/runs-BLJu7EXN.js.gz +0 -0
- package/dist/web/public/assets/settings-BrdVh-Zi.js.br +0 -0
- package/dist/web/public/assets/settings-BrdVh-Zi.js.gz +0 -0
- package/dist/web/public/assets/task-runs-CeQS1rxa.js.br +0 -0
- package/dist/web/public/assets/task-runs-CeQS1rxa.js.gz +0 -0
- package/dist/web/public/assets/tasks-bcb3fYdK.js +0 -4
- package/dist/web/public/assets/tasks-bcb3fYdK.js.br +0 -0
- package/dist/web/public/assets/tasks-bcb3fYdK.js.gz +0 -0
package/docs/deploy.md
CHANGED
|
@@ -1,15 +1,7 @@
|
|
|
1
1
|
# Running Pier as a service (Linux, systemd)
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
adds is the thing a terminal cannot: scheduled tasks fire and IM channels stay
|
|
6
|
-
connected while nobody is logged in.
|
|
7
|
-
|
|
8
|
-
That is also why this document is Linux-only. A laptop closes its lid; the
|
|
9
|
-
machine you want always-on is a server, and on that machine systemd is already
|
|
10
|
-
there.
|
|
11
|
-
|
|
12
|
-
## The short version
|
|
3
|
+
`pier serve` in a terminal is a complete installation; systemd keeps tasks
|
|
4
|
+
firing and channels connected while nobody is logged in. Linux only.
|
|
13
5
|
|
|
14
6
|
```sh
|
|
15
7
|
npm install -g @timqi/pier
|
|
@@ -17,225 +9,97 @@ pier service install
|
|
|
17
9
|
journalctl --user -u pier -e # the password, printed once
|
|
18
10
|
```
|
|
19
11
|
|
|
20
|
-
That writes the
|
|
21
|
-
|
|
22
|
-
|
|
12
|
+
That writes the units, enables linger, and starts the service. `systemctl
|
|
13
|
+
--user cat pier` shows the units with their per-line comments; this page is
|
|
14
|
+
what they do not say.
|
|
23
15
|
|
|
24
16
|
## Prerequisites
|
|
25
17
|
|
|
26
18
|
- Node 24 or newer (`node:sqlite` is used unflagged).
|
|
27
|
-
- A user-writable global npm prefix
|
|
28
|
-
|
|
29
|
-
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
(`git clone` + `npm ci && npm run build`) is the *develop* path; point the
|
|
34
|
-
unit's `ExecStart` at its `dist/main.js` if you run one as the service, and
|
|
35
|
-
update it with the "From a checkout" steps under Updating.
|
|
19
|
+
- A user-writable global npm prefix: the updater runs as you.
|
|
20
|
+
- `sqlite3` CLI: optional, for the off-machine backup and password steps below.
|
|
21
|
+
- Pier installed globally. A checkout (`git clone` + `npm ci && npm run build`)
|
|
22
|
+
is the *develop* path; point the unit's `ExecStart` at its `dist/main.js` if
|
|
23
|
+
you run one as the service, and update it with the checkout steps under
|
|
24
|
+
Updating.
|
|
36
25
|
|
|
37
26
|
## The unit
|
|
38
27
|
|
|
39
|
-
A **user** unit
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
`~/.config/systemd/user/pier.service` — what `pier service install` generates,
|
|
45
|
-
with your absolute Node and package paths filled in:
|
|
46
|
-
|
|
47
|
-
```ini
|
|
48
|
-
[Unit]
|
|
49
|
-
Description=Pier — agent workspace
|
|
50
|
-
Documentation=https://github.com/timqi/pier
|
|
51
|
-
After=network-online.target
|
|
52
|
-
Wants=network-online.target
|
|
53
|
-
|
|
54
|
-
[Service]
|
|
55
|
-
Type=simple
|
|
56
|
-
WorkingDirectory=%h
|
|
57
|
-
# Absolute paths on purpose: systemd starts with a minimal PATH, so a node
|
|
58
|
-
# installed by nvm/fnm/asdf is not on it — the installer fills in the node
|
|
59
|
-
# that installed Pier and the globally installed entry point.
|
|
60
|
-
ExecStart="/absolute/path/to/node" "/absolute/npm/prefix/lib/node_modules/@timqi/pier/dist/main.js"
|
|
61
|
-
# Inherited by every command a turn runs: on systemd's bare PATH an agent asked
|
|
62
|
-
# to run npm or node would be told they do not exist. The installer records the
|
|
63
|
-
# PATH of the shell that ran it — that shell is your login one — with the node
|
|
64
|
-
# above first and the standard directories as a floor. Installed a new tool
|
|
65
|
-
# since? Re-run install --force, or add your own drop-in.
|
|
66
|
-
Environment="PATH=/absolute/path/to/node/bin:/your/shell/PATH:/usr/local/bin:/usr/bin:/bin"
|
|
67
|
-
# Loopback by default. Put a reverse proxy in front before widening this —
|
|
68
|
-
# whoever reaches this port can drive an agent that runs a shell.
|
|
69
|
-
Environment="HOST=127.0.0.1"
|
|
70
|
-
Environment="PORT=3141"
|
|
71
|
-
Restart=always
|
|
72
|
-
RestartSec=2
|
|
73
|
-
StandardOutput=journal
|
|
74
|
-
StandardError=journal
|
|
75
|
-
SyslogIdentifier=pier
|
|
76
|
-
|
|
77
|
-
[Install]
|
|
78
|
-
WantedBy=default.target
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
`--pier-home` adds a quoted `PIER_HOME` environment line. Paths with spaces and
|
|
82
|
-
literal systemd `%` specifiers are escaped. `pier service install` also writes an
|
|
83
|
-
updater unit containing the exact npm executable currently on `PATH`. Re-run with
|
|
84
|
-
`--force` after changing service settings or the Node/npm installation; it
|
|
85
|
-
rewrites both units and restarts the running service. The limits drop-in remains
|
|
86
|
-
operator-owned and is never overwritten.
|
|
87
|
-
|
|
88
|
-
Enable it, and tell logind to keep your user manager alive after you log out —
|
|
89
|
-
without lingering, every scheduled task stops when your SSH session ends:
|
|
90
|
-
|
|
91
|
-
```sh
|
|
92
|
-
loginctl enable-linger "$USER"
|
|
93
|
-
systemctl --user daemon-reload
|
|
94
|
-
systemctl --user enable --now pier
|
|
95
|
-
```
|
|
28
|
+
A **user** unit. `~/.config/systemd/user/pier.service` records the absolute
|
|
29
|
+
node and entry point, the installing shell's `PATH`, and a loopback bind;
|
|
30
|
+
`--pier-home` adds `PIER_HOME`. `pier service install --force` rewrites both
|
|
31
|
+
units and restarts (after a new tool, or a moved Node); the limits drop-in is
|
|
32
|
+
never overwritten. Linger is enabled by the installer.
|
|
96
33
|
|
|
97
34
|
## Memory limits
|
|
98
35
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
leaking. A cgroup limit turns "the box OOMs and sshd dies with it" into "the
|
|
102
|
-
heaviest process inside this unit is killed".
|
|
103
|
-
|
|
104
|
-
User units can do this without root as long as the memory controller is
|
|
105
|
-
delegated to your user manager, which it is by default on current systemd:
|
|
36
|
+
Requires the memory controller delegated to your user manager (default on
|
|
37
|
+
current systemd):
|
|
106
38
|
|
|
107
39
|
```sh
|
|
108
40
|
systemctl show "user@$(id -u).service" -p DelegateControllers
|
|
109
41
|
# DelegateControllers=cpu memory pids ← memory listed means these work
|
|
110
42
|
```
|
|
111
43
|
|
|
112
|
-
`~/.config/systemd/user/pier.service.d/limits.conf`
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
as a share of the machine, not as "how much should Pier need". Percentages are
|
|
119
|
-
relative to installed physical memory, which also keeps this file portable:
|
|
120
|
-
|
|
121
|
-
```ini
|
|
122
|
-
[Service]
|
|
123
|
-
# Soft ceiling: past this the kernel reclaims hard and lets the unit crawl
|
|
124
|
-
# instead of killing anything. This is the one that should bite first.
|
|
125
|
-
MemoryHigh=60%
|
|
126
|
-
# Hard ceiling: the kernel OOM-kills *inside this cgroup*. The number exists to
|
|
127
|
-
# protect everything outside it — the OS, sshd, your other services — so what
|
|
128
|
-
# it should leave behind is a few GB for them, not a small share for Pier.
|
|
129
|
-
MemoryMax=75%
|
|
130
|
-
# Swapping an agent is worse than failing it — the machine stops responding
|
|
131
|
-
# long before the limit is reached.
|
|
132
|
-
MemorySwapMax=0
|
|
133
|
-
# A runaway command an agent ran can fork as well as allocate.
|
|
134
|
-
TasksMax=512
|
|
135
|
-
# The unit's own processes are the preferred victims if the *machine* still
|
|
136
|
-
# runs out, e.g. before these limits are tuned. Works with no cgroup limit at
|
|
137
|
-
# all, which makes it the cheapest half of this file.
|
|
138
|
-
OOMScoreAdjust=200
|
|
139
|
-
# A child being OOM-killed must not take the service with it: the turn that
|
|
140
|
-
# ran it fails, Pier keeps serving. (Delegated units default to this; set
|
|
141
|
-
# explicitly because the default depends on system configuration.)
|
|
142
|
-
OOMPolicy=continue
|
|
143
|
-
```
|
|
144
|
-
|
|
145
|
-
On a dedicated 4–8 GB VPS those percentages land around 2.5–6 GB, which is
|
|
146
|
-
roughly what one agent doing ordinary work needs. On a big shared box, prefer
|
|
147
|
-
absolute values sized to what you are willing to lose to a runaway build —
|
|
148
|
-
`MemoryHigh=8G` / `MemoryMax=12G` on 32 GB, say — because 75% of a large
|
|
149
|
-
machine is no longer a meaningful ceiling.
|
|
150
|
-
|
|
151
|
-
Pick the numbers by measuring, not by guessing — Pier idles in the hundreds of
|
|
152
|
-
MB, and what varies is what the agent runs:
|
|
44
|
+
The installer writes `~/.config/systemd/user/pier.service.d/limits.conf` once
|
|
45
|
+
(`MemoryHigh=60%`, `MemoryMax=75%`, no swap, `TasksMax=512`, `OOMPolicy=continue`,
|
|
46
|
+
each line commented). The limit covers the **whole unit** — `node`, Pi
|
|
47
|
+
subagents, every command a turn ran, their page cache. Dedicated 4–8 GB VPS:
|
|
48
|
+
the defaults land around 2.5–6 GB. Big shared box: absolute values
|
|
49
|
+
(`MemoryHigh=8G` / `MemoryMax=12G` on 32 GB). Pier idles in the hundreds of MB:
|
|
153
50
|
|
|
154
51
|
```sh
|
|
155
52
|
systemctl --user show pier -p MemoryCurrent -p MemoryPeak
|
|
156
53
|
systemd-cgtop --depth=3 "user.slice/user-$(id -u).slice"
|
|
157
54
|
```
|
|
158
55
|
|
|
159
|
-
A limit that fires during ordinary work is worse than none: the turn dies with
|
|
160
|
-
a signal and the cause is a kernel message nobody reads. Set it to protect the
|
|
161
|
-
machine, then raise it the first time it kills something legitimate.
|
|
162
|
-
|
|
163
56
|
## Logs
|
|
164
57
|
|
|
165
|
-
|
|
166
|
-
rotation, no log configuration. Under the unit above that *is* the log:
|
|
167
|
-
journald stamps the time, keeps the history and rotates it, which is why
|
|
168
|
-
nothing in Pier reimplements any of that.
|
|
58
|
+
stdout and stderr only; journald stamps, keeps and rotates.
|
|
169
59
|
|
|
170
60
|
```sh
|
|
171
61
|
journalctl --user -u pier -f # follow
|
|
172
62
|
journalctl --user -u pier -p warning # only what went wrong
|
|
173
63
|
journalctl --user -u pier --since -1h | grep 'tasks:' # one area
|
|
64
|
+
journalctl --user -u pier | grep 'client:' # browser-side errors
|
|
174
65
|
```
|
|
175
66
|
|
|
176
67
|
Every line is `area: message` — `core`, `agent`, `tasks`, `slack`, `telegram`,
|
|
177
68
|
`lark`, `channels`, `slack.tool`, `auth`, `boards`, `client`, `db`, `drain`,
|
|
178
|
-
`secrets`, `settings`, `credentials`, `update`, `tools`, `push`,
|
|
179
|
-
`web
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
What is logged by default: process start and shutdown, sessions opening,
|
|
185
|
-
conversation → session routing, every turn ending, every task run queued and
|
|
186
|
-
settled, callback and message delivery failures with their retries, adapters
|
|
187
|
-
starting (and failing to start or stop), dropped inbound messages, failed
|
|
188
|
-
logins, and any request that threw. A watch probe that matched nothing is the
|
|
189
|
-
one routine event kept at `debug` — it fires on every interval.
|
|
190
|
-
|
|
191
|
-
`client:` is the browser's half, posted back by the workbench (`ui/report.ts`)
|
|
192
|
-
and written into this same stream: script errors, unhandled rejections and a
|
|
193
|
-
dead SSE stream, each with the view and the user agent. It comes from signed-in
|
|
194
|
-
tabs only — the route sits behind the password like the rest of `/api`, so
|
|
195
|
-
nobody who cannot already reach Pier can write into this journal. The person sees the
|
|
196
|
-
same sentence in the chat pane, so "I clicked and nothing happened" has a line
|
|
197
|
-
on both ends:
|
|
198
|
-
|
|
199
|
-
```sh
|
|
200
|
-
journalctl --user -u pier | grep 'client:'
|
|
201
|
-
```
|
|
69
|
+
`secrets`, `settings`, `credentials`, `update`, `tools`, `push`, `web`,
|
|
70
|
+
`web.providers`, `pier`. Level: a syslog priority prefix under
|
|
71
|
+
`$JOURNAL_STREAM`, a level word in a terminal. `client:` is posted back by
|
|
72
|
+
signed-in workbench tabs (`ui/report.ts`): script errors, unhandled rejections,
|
|
73
|
+
a dead SSE stream, with view and user agent.
|
|
202
74
|
|
|
203
75
|
```sh
|
|
204
76
|
systemctl --user set-environment PIER_LOG=debug # + per-message tracing
|
|
205
77
|
systemctl --user restart pier
|
|
206
78
|
```
|
|
207
79
|
|
|
208
|
-
`PIER_LOG
|
|
209
|
-
|
|
210
|
-
message and per tool call.
|
|
80
|
+
`PIER_LOG`: `debug`, `info` (default), `warn`, `error`, `silent`. `debug` logs
|
|
81
|
+
one line per inbound message and per tool call.
|
|
211
82
|
|
|
212
83
|
## First login
|
|
213
84
|
|
|
214
|
-
|
|
215
|
-
journal is where you read it:
|
|
85
|
+
A password is generated on an empty database and printed once:
|
|
216
86
|
|
|
217
87
|
```sh
|
|
218
88
|
journalctl --user -u pier | grep -A2 'no password'
|
|
219
89
|
```
|
|
220
90
|
|
|
221
|
-
Only its scrypt hash is stored.
|
|
222
|
-
new password is generated and printed:
|
|
91
|
+
Only its scrypt hash is stored. Lost it? Drop the row and restart:
|
|
223
92
|
|
|
224
93
|
```sh
|
|
225
94
|
sqlite3 ~/.pier/db/pier.db 'DELETE FROM auth'
|
|
226
95
|
pier restart
|
|
227
96
|
```
|
|
228
97
|
|
|
229
|
-
Changing the password signs out every browser
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
out, expired, password changed, password recovered — its push subscription is
|
|
235
|
-
deleted with it, so that browser stops being notified as well.
|
|
236
|
-
|
|
237
|
-
The upgrade that introduced these rows signs everyone out once: a cookie issued
|
|
238
|
-
before it names no row. Have the password to hand.
|
|
98
|
+
- Changing or recovering the password signs out every browser.
|
|
99
|
+
- One browser: Settings → Instance → Signed-in devices.
|
|
100
|
+
- A browser session expires 7 days after its last request, and 90 days after
|
|
101
|
+
it signed in however often it is used.
|
|
102
|
+
- However a session ends, its push subscription goes with it.
|
|
239
103
|
|
|
240
104
|
## Restarting and reloading
|
|
241
105
|
|
|
@@ -245,34 +109,26 @@ pier reload # apply channel config and recycle idle sessions in place
|
|
|
245
109
|
pier tools sync # install/update the managed CLI tools
|
|
246
110
|
```
|
|
247
111
|
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
Console → Settings → Instance → **Reload** is the same thing from a browser, and
|
|
269
|
-
needs no shell on the box. It also takes the session open in the tab that asked
|
|
270
|
-
(only a turn in flight is exempt) and answers with how many were recycled and
|
|
271
|
-
how many are still mid-turn — the second number is the only reason a change can
|
|
272
|
-
still fail to show up.
|
|
273
|
-
|
|
274
|
-
An ordinary `systemctl --user restart pier` and `pier update` remain fast,
|
|
275
|
-
hard-stop paths. Let active work finish first when using either one.
|
|
112
|
+
All three signal the installed service.
|
|
113
|
+
|
|
114
|
+
- `pier restart`: refuses new messages and root Task runs, waits up to five
|
|
115
|
+
minutes, exits for `Restart=always`; aborted IM turns at the deadline are
|
|
116
|
+
recorded and posted by the next process; post-deadline cleanup has one shared
|
|
117
|
+
10-second bound.
|
|
118
|
+
- `pier reload`: reloads channel adapters, evicts idle unwatched sessions;
|
|
119
|
+
streaming or watched sessions, and sessions still holding queued messages,
|
|
120
|
+
stay until normal eviction. Console → Settings → Instance → **Reload** is
|
|
121
|
+
the same, also takes the asking tab's session (unless mid-turn or holding a
|
|
122
|
+
queue), and answers `recycled` / `busy`.
|
|
123
|
+
- `pier tools sync`: converges the tools switched on in Console → Settings into
|
|
124
|
+
`~/.pier/tools/bin` (first on every session's PATH); one sync at a time per
|
|
125
|
+
machine.
|
|
126
|
+
- `systemctl --user restart pier` and `pier update` are hard stops.
|
|
127
|
+
- One Pier per `$PIER_HOME`: a start whose directory another live Pier holds
|
|
128
|
+
logs `another Pier (pid N) owns …` and exits before opening the database. Kept
|
|
129
|
+
under `Restart=always` on purpose — the service takes the directory back by
|
|
130
|
+
itself once the other process (usually a hand-typed `pier serve`) is gone, at
|
|
131
|
+
one refused start every `RestartSec=2` until then.
|
|
276
132
|
|
|
277
133
|
## Updating
|
|
278
134
|
|
|
@@ -281,12 +137,10 @@ pier update # installs the latest release, then hard-stops/restarts Pi
|
|
|
281
137
|
pier update --check # only says whether one exists
|
|
282
138
|
```
|
|
283
139
|
|
|
284
|
-
The
|
|
285
|
-
`
|
|
286
|
-
version turns into `v0.0.1 → 0.0.2` when there is something newer. A failed
|
|
287
|
-
check is silent by design — an offline box is not a broken one.
|
|
140
|
+
The footer asks `registry.npmjs.org` at boot and at most every 30 minutes and
|
|
141
|
+
shows `v0.0.1 → 0.0.2` when newer exists; a failed check is silent.
|
|
288
142
|
|
|
289
|
-
From a checkout
|
|
143
|
+
From a checkout:
|
|
290
144
|
|
|
291
145
|
```sh
|
|
292
146
|
systemctl --user stop pier
|
|
@@ -297,54 +151,27 @@ npm ci && npm run build
|
|
|
297
151
|
systemctl --user start pier
|
|
298
152
|
```
|
|
299
153
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
`~/.pier/db/backups/pier.db.release-<version>.bak` before npm touches the
|
|
307
|
-
package — `<version>` being the Pier that is being replaced, i.e. the release to
|
|
308
|
-
reinstall if that copy is ever restored. This happens for every release,
|
|
309
|
-
including releases with no schema change.
|
|
310
|
-
|
|
311
|
-
Both of those steps run while Pier is still serving: the snapshot is taken
|
|
312
|
-
through a read-only connection, so it is consistent on a live database, and npm
|
|
313
|
-
writes into the global prefix rather than into the running process. Only the
|
|
314
|
-
stop and the start that follow them are downtime — a second or two instead of
|
|
315
|
-
the ten to twenty an install takes. A backup or install that fails therefore
|
|
316
|
-
never stops anything; the failure is in the updater's journal and the running
|
|
317
|
-
Pier keeps serving the version it already loaded. (For those seconds the live
|
|
318
|
-
process is running code whose files on disk have already been replaced: a
|
|
319
|
-
browser left open on the old page can see a lazily loaded asset 404 until it
|
|
320
|
-
reloads. On the drained paths nothing else is running by then; `pier update`
|
|
321
|
-
does not drain, which is the same reason to let active work finish first.)
|
|
322
|
-
|
|
323
|
-
### Automatic updates
|
|
324
|
-
|
|
325
|
-
Off by default. Switched on from the version panel, it checks every 15 minutes
|
|
326
|
-
and hands over only when all three hold: the switch is on, a newer release
|
|
327
|
-
exists, and the instance is idle (nothing streaming, no task run in flight).
|
|
328
|
-
systemd only — without the unit there is nothing to hand the install to.
|
|
154
|
+
`pier update` hard-stops; the Console's **Update now** and the automatic path
|
|
155
|
+
drain first (new work refused, running turns finished, the rest ledgered for
|
|
156
|
+
the next boot). Either way the updater snapshots
|
|
157
|
+
`~/.pier/db/backups/pier.db.release-<version>.bak` (`<version>` = the Pier being
|
|
158
|
+
replaced) before npm runs, every release. Only stop and start are downtime; a
|
|
159
|
+
failed backup or install stops nothing (`journalctl --user -u pier-update`).
|
|
329
160
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
Pier checks those paths at boot and before every handover, and reports it in the
|
|
335
|
-
journal and in the version panel rather than letting the next restart fail:
|
|
161
|
+
**Automatic updates**: off by default; switched on from the version panel;
|
|
162
|
+
checks every 15 minutes; hands over only when the switch is on, a newer release
|
|
163
|
+
exists, and the instance is idle (nothing streaming, no task run in flight).
|
|
164
|
+
systemd only.
|
|
336
165
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
166
|
+
**Node paths**: `fnm uninstall v24` (or nvm) deletes the Node `ExecStart`
|
|
167
|
+
names while the process survives; Pier checks at boot and before every handover
|
|
168
|
+
and reports in the journal and the version panel. Fix: `pier service install
|
|
169
|
+
--force`.
|
|
340
170
|
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
only.** Start an older Pier on a database a newer one has migrated and it
|
|
346
|
-
refuses to run rather than write tables it does not understand — the way back
|
|
347
|
-
down is either a release backup or that schema snapshot:
|
|
171
|
+
**Schema**: a newer Pier migrates on start, in one transaction before the port
|
|
172
|
+
opens, after snapshotting to `~/.pier/db/backups/pier.db.v<N>.bak` (`N` = the
|
|
173
|
+
schema it was at). Upgrades only: an older Pier refuses a newer database. Way
|
|
174
|
+
back:
|
|
348
175
|
|
|
349
176
|
```sh
|
|
350
177
|
systemctl --user stop pier
|
|
@@ -354,86 +181,44 @@ cp backups/pier.db.release-0.0.4.bak pier.db # the version in the name
|
|
|
354
181
|
# then reinstall that Pier release: npm install -g @timqi/pier@0.0.4
|
|
355
182
|
```
|
|
356
183
|
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
snapshots, counted separately so a run of releases cannot evict the copies taken
|
|
361
|
-
before a migration. Backing up twice at the same version replaces that version's
|
|
362
|
-
copy. Each is a full copy of the database, one directory below it, and therefore
|
|
363
|
-
protects against a bad upgrade, not against a lost disk — an off-machine copy is
|
|
364
|
-
still yours to take.
|
|
365
|
-
|
|
366
|
-
### Can it update itself?
|
|
367
|
-
|
|
368
|
-
Yes, with one caveat that decides the shape: **the updater must not be a child
|
|
369
|
-
of the service it restarts.** `systemctl --user restart pier` kills everything
|
|
370
|
-
in `pier.service`'s cgroup, so an update script spawned by Pier dies halfway
|
|
371
|
-
through — sometimes after unpacking and before restarting, which is the one
|
|
372
|
-
outcome worse than not updating.
|
|
373
|
-
|
|
374
|
-
So installation writes a second unit and `pier update` starts it after recording
|
|
375
|
-
the running service's effective `PIER_HOME` in a runtime drop-in. That includes
|
|
376
|
-
an operator environment override, so the updater cannot back up one database and
|
|
377
|
-
migrate another. `~/.config/systemd/user/pier-update.service`:
|
|
378
|
-
|
|
379
|
-
```ini
|
|
380
|
-
[Unit]
|
|
381
|
-
Description=Update Pier to the latest published version
|
|
382
|
-
|
|
383
|
-
[Service]
|
|
384
|
-
Type=oneshot
|
|
385
|
-
# npm's dependencies run postinstall scripts as `sh -c node …`, which needs a
|
|
386
|
-
# node on PATH — the absolute one below only answers npm's own shebang. Same
|
|
387
|
-
# recorded PATH as pier.service, and recorded rather than sourced from a login
|
|
388
|
-
# shell at run time: a dotfile must not get to decide which node npm uses.
|
|
389
|
-
Environment="PATH=/path/to/node/bin:/your/shell/PATH:/usr/local/bin:/usr/bin:/bin"
|
|
390
|
-
ExecStart=/path/to/node /path/to/pier/dist/cli.js backup
|
|
391
|
-
ExecStart=/path/to/node /recorded/path/to/npm install -g @timqi/pier@latest
|
|
392
|
-
# Last: everything above it runs with Pier still up, so the stop is the downtime.
|
|
393
|
-
ExecStart=systemctl --user stop pier.service
|
|
394
|
-
ExecStopPost=systemctl --user start pier.service
|
|
395
|
-
```
|
|
396
|
-
|
|
397
|
-
`pier update` triggers that unit with a call that survives Pier's restart because
|
|
398
|
-
the work happens in a different cgroup. Starting the unit directly is unsupported:
|
|
399
|
-
the command first records the effective database home used by the running service.
|
|
184
|
+
Copies are written under a temporary name and renamed into place; the three
|
|
185
|
+
newest of **each kind** (release, schema) are kept; a second backup at the same
|
|
186
|
+
version replaces it. They protect against a bad upgrade, not a lost disk.
|
|
400
187
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
successful stop, and — as a no-op, since nothing was stopped — after a backup or
|
|
407
|
-
install that failed.
|
|
188
|
+
**The updater unit** `pier-update.service` (backup, `npm install -g`, stop,
|
|
189
|
+
`ExecStopPost` start) runs outside `pier.service`'s cgroup, which a restart
|
|
190
|
+
kills. `pier update` records the service's effective `PIER_HOME` in a runtime
|
|
191
|
+
drop-in, then starts it; starting the unit directly is unsupported. No
|
|
192
|
+
`systemd.timer`: only Pier starts an update, so it can drain first.
|
|
408
193
|
|
|
409
194
|
## Remote access
|
|
410
195
|
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
-
|
|
415
|
-
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
196
|
+
Loopback bind; reach it over a tunnel, not a wider bind:
|
|
197
|
+
|
|
198
|
+
- `ssh -L 3141:localhost:3141 server`
|
|
199
|
+
- Tailscale, or Cloudflare Tunnel — no open port, TLS terminates outside.
|
|
200
|
+
- A reverse proxy (Caddy, nginx): terminate TLS there, preserve the external
|
|
201
|
+
`Host` (or pass `X-Forwarded-Host`), and **set** `X-Forwarded-For` to the
|
|
202
|
+
client — nginx `proxy_set_header X-Forwarded-For $remote_addr;`, Caddy by
|
|
203
|
+
default. Pier reads the rightmost hop, so appending the peer is safe too
|
|
204
|
+
(`$proxy_add_x_forwarded_for`); the unsafe case is a proxy that passes the
|
|
205
|
+
client's own header through without adding a hop of its own, which lets the
|
|
206
|
+
client name its throttle bucket.
|
|
207
|
+
Pier uses the external host for write-origin checks and counts login
|
|
208
|
+
failures per forwarded client. The cookie is `Secure` when a loopback proxy
|
|
209
|
+
reports `X-Forwarded-Proto: https` (ignored from anywhere else).
|
|
210
|
+
- `ssh -L` sends no such header, so every client through the tunnel shares one
|
|
211
|
+
throttle bucket.
|
|
422
212
|
|
|
423
213
|
## Backups
|
|
424
214
|
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
`~/.pier/boards/`.
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
Inbound chat attachments (photos, uploads from any surface) accumulate under
|
|
437
|
-
`~/.pier/inbox/<channel>/` and are never deleted by Pier — a transcript may
|
|
438
|
-
reference them indefinitely. Prune old files by hand (or a cron) when disk
|
|
439
|
-
matters; a pruned file degrades to a broken attachment link, nothing else.
|
|
215
|
+
- `~/.pier/db/pier.db` — tasks, channels, chat → session map, workbench state,
|
|
216
|
+
settings, password hash, sealed credentials and tokens. Off-machine: `sqlite3
|
|
217
|
+
... "VACUUM INTO '…'"`, not `cp` (WAL can miss the latest commits).
|
|
218
|
+
- `~/.pier/master.key` — seals the database's credentials.
|
|
219
|
+
- `~/.pier/boards/`.
|
|
220
|
+
- `~/.pier/db/backups/` — the automatic pre-update and pre-migration copies.
|
|
221
|
+
- `~/.pier/pi` — Pi's session history (unless `PI_CODING_AGENT_DIR` names
|
|
222
|
+
another directory).
|
|
223
|
+
- `~/.pier/inbox/<channel>/` — inbound chat attachments, never deleted by
|
|
224
|
+
Pier; prune by hand (a pruned file becomes a broken attachment link).
|
package/package.json
CHANGED