key and API address fall back to the person's secrets file; a non-git folder becomes a local_path project
This commit is contained in:
@@ -211,3 +211,116 @@ Say which you want and I will do only that.
|
||||
- session: [[packs/paperclip-harness/80-sessions/nixos-13-outage-postmortem-nixos-rebuild-switch-broke-static|NixOS .13 outage postmortem: nixos-rebuild switch broke static network via NetworkManager race]]
|
||||
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-35-2026-11-12-03-14-45|Rich discovery: .35 2026-11-12 03:14:45]]
|
||||
- context: [[packs/paperclip-harness/20-context/nix|.35]]
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Update (2026-11-12 05:27:33)
|
||||
|
||||
## Rich discovery update (2026-11-12 05:27:33)
|
||||
|
||||
## Is everything working — measured just now
|
||||
|
||||
| | State | Evidence |
|
||||
|---|---|---|
|
||||
| **Pi** | 1.5.0 on all three, same bytes | `.13`, `.27`, `.51` all resolve to `/nix/store/4yxlfj0…-pi-coding-agent-1.5.0` |
|
||||
| **Paperclip** | running, `2026.916.1` | service active, `GET /api/health` 200, web UI 200 at `http://127.0.0.1:3100/` |
|
||||
| **Agent runs** | working again | 3 runs failed before the shim, then succeeded (69 s, then 24 s) |
|
||||
| **omniroute** | working | `/v1/models` returns **486** models, a direct completion returns 200 |
|
||||
| **headroom** | tool yes, provider no | `/v1/compress` 200 with `messages`; `/v1/chat/completions` returns **502** for both `auto/cheap` and `deepseek/deepseek-v4-flash`; `/stats` shows 153 requests, 2 compressed |
|
||||
| **TUIOS** | 0.12.1 | both sessions' 8 panes verified by process: each runs `bin/harness -screen …` |
|
||||
|
||||
One thing worth knowing about headroom: my earlier `compress` call returned 400 because I sent `text`; it wants `messages`. Tool path is fine. Provider path is still broken for the same reason as before — its LiteLLM has no entry for the router's model ids.
|
||||
|
||||
## CLI versus web — they are the same thing
|
||||
|
||||
There is no separate web backend. Port 3100 serves the HTML at `/` and the JSON at `/api/...` from one process and one Postgres database. So a cost problem on the web is an **API and aggregation** problem, not a web problem — and you can see the same fault in my terminal panes, which is how I found it.
|
||||
|
||||
Two separate faults, both measured:
|
||||
|
||||
**1. `costCents` is a whole number, so small tasks show zero.**
|
||||
|
||||
```
|
||||
AUD-173 costCents 0 inputTokens 13023 outputTokens 2253
|
||||
usageJson.costUsd = 0.001934786 → 0.19 cents → displayed as 0
|
||||
AUD-163 costCents 3 303609 input tokens → 2.9 cents → displayed as 3
|
||||
```
|
||||
|
||||
Any task that costs less than half a cent reads as free. On the web that looks like "costs aren't working"; the exact figure is in each run's `usageJson`.
|
||||
|
||||
**2. One task's summary is stale and I cannot explain it away.**
|
||||
|
||||
```
|
||||
AUD-172 costCents 0 inputTokens 0 outputTokens 0 runCount 4 runtimeMs 81645
|
||||
its successful run's usageJson: 34217 input, 7187 output, $0.014134
|
||||
```
|
||||
|
||||
`runtimeMs` proves the summary did aggregate all four runs. But tokens and cost are zero, while the run record holds both. So the per-task summary was computed before that run's usage was written and never recomputed. AUD-173 and AUD-163 summarise correctly, so it is not universal.
|
||||
|
||||
This is a Paperclip-side defect, in the endpoint the web uses. `docs/NEXT.md` records it with these numbers. I am not going to patch Paperclip's internals without you asking me to.
|
||||
|
||||
## `office --help`
|
||||
|
||||
```
|
||||
office — open a folder as a project, and say what else can be run
|
||||
|
||||
THE RULE
|
||||
The folder and the session live on the same machine. …
|
||||
WHEN IT DOES / WHAT IT WRITES
|
||||
COMMANDS office, board, board --replace, board --reset, project-reset,
|
||||
merge, review, triage — one line each
|
||||
MACHINES nixos-desktop .13 192.168.20.13
|
||||
sam-4screen-desktop .27 192.168.20.27
|
||||
sam-thinkpad .51 192.168.20.51
|
||||
EVERY MACHINE, FROM THE TERMINAL a worked example for .13, .27 and .51
|
||||
ON ANOTHER MACHINE where the copy lives
|
||||
AFTER A REBOOT run board once per project
|
||||
KEYS INSIDE THE PANES ( ) tab, tab panel, r, R, ?
|
||||
MORE the four docs to read next
|
||||
```
|
||||
|
||||
## While writing it I found `--host` could never have worked
|
||||
|
||||
It ran `office` on the other machine at *this* machine's path, so it needed the repository to exist there at the same location. `.27` and `.51` have no repository at all. It also checked the folder existed **locally** before deciding the folder was remote, so it always answered "no folder at …".
|
||||
|
||||
Both fixed. `--host` now copies `bin/` to `~/.local/share/paperclip-harness/bin/` on the other machine, leaves a short `~/.local/bin/office` there, and the folder is checked on the machine that owns it. Verified against both machines:
|
||||
|
||||
```
|
||||
$ office /tmp/office-remote-test --new office-remote-test --host sam-4screen-desktop --dry
|
||||
on sam-4screen-desktop: exec "$HOME/.local/share/paperclip-harness/bin/office" …
|
||||
would create a Paperclip project 'office-remote-test' for /tmp/office-remote-test
|
||||
dry run: nothing was written
|
||||
switch to it: tuios attach --host sam-4screen-desktop office-remote-test
|
||||
```
|
||||
|
||||
`.27` and `.51` now have the commands and a working `--help`. One gap remains: **`~/.local/bin` is not on `PATH` on either machine**, so bare `office` does not resolve there — the full path does:
|
||||
|
||||
```sh
|
||||
~/.local/share/paperclip-harness/bin/office <folder>
|
||||
```
|
||||
|
||||
Fixing that is one line in each machine's home-manager config (`home.sessionPath = [ "$HOME/.local/bin" ]`), and one rebuild. Say the word and I will add it to both and hand you the two commands.
|
||||
|
||||
Committed and pushed: `paperclip-harness` → `3043e9d`.
|
||||
|
||||
## Source paths observed
|
||||
|
||||
- `HOME/.local/share/paperclip-harness/bin/office`
|
||||
- `tmp/office-remote-test/.harness/harness.toml`
|
||||
- `.local/bin/office`
|
||||
- `.local/share/paperclip-harness/bin`
|
||||
- `.local/share/paperclip-harness/bin/office`
|
||||
- `nix/store/4yxlfj0`
|
||||
- `v1/chat/completions`
|
||||
- `HOME/.local/bin`
|
||||
|
||||
## Evidence
|
||||
|
||||
- Confidence: 84%
|
||||
- Reason: Update component/repository context with detailed discovered structure and configuration
|
||||
|
||||
## Related learned memories <!-- same-turn-2026-11-12 05:27:33 -->
|
||||
|
||||
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-nix-2026-11-12-05-27-33|Rich discovery: nix 2026-11-12 05:27:33]]
|
||||
- context: [[packs/paperclip-harness/20-context/nix|nix]]
|
||||
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|nix repository structure and configuration patterns]]
|
||||
|
||||
@@ -910,3 +910,34 @@ Open issues: pi_local runs report costCents 0 / outputTokens 0 / usageJson null
|
||||
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Fixed four harness defects, verified clean tests, pushed 6c6bacc]]
|
||||
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-session-2026-11-12-04-50-42|Rich discovery: --session 2026-11-12 04:50:42]]
|
||||
- context: [[packs/paperclip-harness/20-context/omni|--session]]
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Update (2026-11-12 06:13:12)
|
||||
|
||||
Multi-host NixOS dev setup used to drive `office` CLI work across three machines.
|
||||
|
||||
Hosts and roles:
|
||||
- 192.168.20.13 (host alias `nixos-desktop`): primary desktop; had ~/.local/bin on home-manager PATH all along (so `office` works there). Source of folders when driving other hosts: `office <folder> --project "<Name>" --host nixos-desktop`.
|
||||
- 192.168.20.27 (sam-4screen-desktop): 4-screen desktop; Android SDK lives outside Nix at ~/Android/Sdk (nix-ld block in configuration.nix), with cmdline-tools/latest/bin and platform-tools on sessionPath. Also has ~/bin on PATH.
|
||||
- 192.168.20.51 (sam-thinkpad): laptop; minimal sessionPath.
|
||||
|
||||
Config layout: /etc/nixos/home/sam/home.nix per host (home-manager). Common sessionVariables: QT_QPA_PLATFORM=wayland, QT_WAYLAND_DISABLE_WINDOWDECORATION=1, GOOSE_DISABLE_KEYRING=1, NIXOS_OZONE_WL=1 (Electron/Obsidian), FALCODE_NOTIFY_SCRIPT=/bin/true. Both desktops use gtk Adwaita-dark via gnome-themes-extra.
|
||||
|
||||
office CLI: installed at ~/.local/share/paperclip-harness/bin/office with wrapper at ~/.local/bin/office. Supports `--host <machine>` to copy commands to another host, and `--new` / `--project` flags. Repo commit 3043e9d fixed `office --help` and `--host`.
|
||||
|
||||
Operational note: home-manager-only changes are safe to `nixos-rebuild switch` (only home-manager-sam.service restarts); avoid changes that move nixpkgs system packages or touch networking unless intended.
|
||||
|
||||
## Evidence
|
||||
|
||||
- Confidence: 80%
|
||||
- Reason: Durable component/fleet overview: host roles, config layout, shared env vars, CLI install paths, and safe-rebuild convention.
|
||||
|
||||
## Related learned memories <!-- same-turn-2026-11-12 06:13:12 -->
|
||||
|
||||
- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]]
|
||||
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|NixOS host PATH diffs and prebuilt switch closures]]
|
||||
- runbook: [[packs/paperclip-harness/70-runbooks/tmp-deployment-operations|Rebuild .27 and .51 after adding ~/.local/bin to home-manager PATH]]
|
||||
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Added ~/.local/bin to PATH on .27/.51 and committed; .13 untouched]]
|
||||
- session: [[packs/paperclip-harness/80-sessions/nixos-fleet-path-fix-rollout-27-and-51-need-home-rebuild-13-|NixOS fleet PATH fix rollout: .27 and .51 need home rebuild, .13 already correct]]
|
||||
|
||||
@@ -1260,3 +1260,35 @@ Next suggested work: cost capture.
|
||||
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Fixed four harness defects, verified clean tests, pushed 6c6bacc]]
|
||||
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-session-2026-11-12-04-50-42|Rich discovery: --session 2026-11-12 04:50:42]]
|
||||
- context: [[packs/paperclip-harness/20-context/omni|--session]]
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Update (2026-11-12 06:13:12)
|
||||
|
||||
Deliverable: put `~/.local/bin` on home-manager PATH on the two NixOS hosts missing it so the paperclip-harness `office` wrapper becomes reachable.
|
||||
|
||||
Changes: one line in /etc/nixos/home/sam/home.nix on each of 192.168.20.27 (sam-4screen-desktop) and 192.168.20.51 (sam-thinkpad): prepend `${config.home.homeDirectory}/.local/bin` to `home.sessionPath`. 192.168.20.13 (nixos-desktop) required no change.
|
||||
|
||||
Artifacts / commits:
|
||||
- .27: master c2d10d6 "home: put ~/.local/bin on PATH" (pushed f56f053..c2d10d6)
|
||||
- .51: main ea2cb86 "home: put ~/.local/bin on PATH" (pushed 40ccd7e..ea2cb86)
|
||||
- paperclip-harness: 3043e9d (office --help / --host fix)
|
||||
- Backups: home/sam/home.nix.bak-path on both hosts
|
||||
|
||||
Validation: both builds succeeded; closures produced for sam-4screen-desktop and sam-thinkpad. `switch` not yet confirmed by the user; post-switch checks are `grep -c '.local/bin'` on PATH and `office --help` in a new shell.
|
||||
|
||||
Follow-up: machines can then drive each other via `office <folder> --project|--new ... --host <name>`; --host copies commands, so a host that never saw the repo still works.
|
||||
|
||||
## Evidence
|
||||
|
||||
- Confidence: 90%
|
||||
- Reason: Concrete completed edits, commit hashes, build validation, and pending verification steps.
|
||||
|
||||
## Related learned memories <!-- same-turn-2026-11-12 06:13:12 -->
|
||||
|
||||
- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]]
|
||||
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|NixOS host PATH diffs and prebuilt switch closures]]
|
||||
- runbook: [[packs/paperclip-harness/70-runbooks/tmp-deployment-operations|Rebuild .27 and .51 after adding ~/.local/bin to home-manager PATH]]
|
||||
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Added ~/.local/bin to PATH on .27/.51 and committed; .13 untouched]]
|
||||
- session: [[packs/paperclip-harness/80-sessions/nixos-fleet-path-fix-rollout-27-and-51-need-home-rebuild-13-|NixOS fleet PATH fix rollout: .27 and .51 need home rebuild, .13 already correct]]
|
||||
|
||||
@@ -5399,3 +5399,213 @@ The one thing I would do next, if you agree, is the cost capture: it is the only
|
||||
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Fixed four harness defects, verified clean tests, pushed 6c6bacc]]
|
||||
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-session-2026-11-12-04-50-42|Rich discovery: --session 2026-11-12 04:50:42]]
|
||||
- context: [[packs/paperclip-harness/20-context/omni|--session]]
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Update (2026-11-12 05:27:33)
|
||||
|
||||
## Structural/configuration observations (2026-11-12 05:27:33)
|
||||
|
||||
## Is everything working — measured just now
|
||||
|
||||
| | State | Evidence |
|
||||
|---|---|---|
|
||||
| **Pi** | 1.5.0 on all three, same bytes | `.13`, `.27`, `.51` all resolve to `/nix/store/4yxlfj0…-pi-coding-agent-1.5.0` |
|
||||
| **Paperclip** | running, `2026.916.1` | service active, `GET /api/health` 200, web UI 200 at `http://127.0.0.1:3100/` |
|
||||
| **Agent runs** | working again | 3 runs failed before the shim, then succeeded (69 s, then 24 s) |
|
||||
| **omniroute** | working | `/v1/models` returns **486** models, a direct completion returns 200 |
|
||||
| **headroom** | tool yes, provider no | `/v1/compress` 200 with `messages`; `/v1/chat/completions` returns **502** for both `auto/cheap` and `deepseek/deepseek-v4-flash`; `/stats` shows 153 requests, 2 compressed |
|
||||
| **TUIOS** | 0.12.1 | both sessions' 8 panes verified by process: each runs `bin/harness -screen …` |
|
||||
|
||||
One thing worth knowing about headroom: my earlier `compress` call returned 400 because I sent `text`; it wants `messages`. Tool path is fine. Provider path is still broken for the same reason as before — its LiteLLM has no entry for the router's model ids.
|
||||
|
||||
## CLI versus web — they are the same thing
|
||||
|
||||
There is no separate web backend. Port 3100 serves the HTML at `/` and the JSON at `/api/...` from one process and one Postgres database. So a cost problem on the web is an **API and aggregation** problem, not a web problem — and you can see the same fault in my terminal panes, which is how I found it.
|
||||
|
||||
Two separate faults, both measured:
|
||||
|
||||
**1. `costCents` is a whole number, so small tasks show zero.**
|
||||
|
||||
```
|
||||
AUD-173 costCents 0 inputTokens 13023 outputTokens 2253
|
||||
usageJson.costUsd = 0.001934786 → 0.19 cents → displayed as 0
|
||||
AUD-163 costCents 3 303609 input tokens → 2.9 cents → displayed as 3
|
||||
```
|
||||
|
||||
Any task that costs less than half a cent reads as free. On the web that looks like "costs aren't working"; the exact figure is in each run's `usageJson`.
|
||||
|
||||
**2. One task's summary is stale and I cannot explain it away.**
|
||||
|
||||
```
|
||||
AUD-172 costCents 0 inputTokens 0 outputTokens 0 runCount 4 runtimeMs 81645
|
||||
its successful run's usageJson: 34217 input, 7187 output, $0.014134
|
||||
```
|
||||
|
||||
`runtimeMs` proves the summary did aggregate all four runs. But tokens and cost are zero, while the run record holds both. So the per-task summary was computed before that run's usage was written and never recomputed. AUD-173 and AUD-163 summarise correctly, so it is not universal.
|
||||
|
||||
This is a Paperclip-side defect, in the endpoint the web uses. `docs/NEXT.md` records it with these numbers. I am not going to patch Paperclip's internals without you asking me to.
|
||||
|
||||
## `office --help`
|
||||
|
||||
```
|
||||
office — open a folder as a project, and say what else can be run
|
||||
|
||||
THE RULE
|
||||
The folder and the session live on the same machine. …
|
||||
WHEN IT DOES / WHAT IT WRITES
|
||||
COMMANDS office, board, board --replace, board --reset, project-reset,
|
||||
merge, review, triage — one line each
|
||||
MACHINES nixos-desktop .13 192.168.20.13
|
||||
sam-4screen-desktop .27 192.168.20.27
|
||||
sam-thinkpad .51 192.168.20.51
|
||||
EVERY MACHINE, FROM THE TERMINAL a worked example for .13, .27 and .51
|
||||
ON ANOTHER MACHINE where the copy lives
|
||||
AFTER A REBOOT run board once per project
|
||||
KEYS INSIDE THE PANES ( ) tab, tab panel, r, R, ?
|
||||
MORE the four docs to read next
|
||||
```
|
||||
|
||||
## While writing it I found `--host` could never have worked
|
||||
|
||||
It ran `office` on the other machine at *this* machine's path, so it needed the repository to exist there at the same location. `.27` and `.51` have no repository at all. It also checked the folder existed **locally** before deciding the folder was remote, so it always answered "no folder at …".
|
||||
|
||||
Both fixed. `--host` now copies `bin/` to `~/.local/share/paperclip-harness/bin/` on the other machine, leaves a short `~/.local/bin/office` there, and the folder is checked on the machine that owns it. Verified against both machines:
|
||||
|
||||
```
|
||||
$ office /tmp/office-remote-test --new office-remote-test --host sam-4screen-desktop --dry
|
||||
on sam-4screen-desktop: exec "$HOME/.local/share/paperclip-harness/bin/office" …
|
||||
would create a Paperclip project 'office-remote-test' for /tmp/office-remote-test
|
||||
dry run: nothing was written
|
||||
switch to it: tuios attach --host sam-4screen-desktop office-remote-test
|
||||
```
|
||||
|
||||
`.27` and `.51` now have the commands and a working `--help`. One gap remains: **`~/.local/bin` is not on `PATH` on either machine**, so bare `office` does not resolve there — the full path does:
|
||||
|
||||
```sh
|
||||
~/.local/share/paperclip-harness/bin/office <folder>
|
||||
```
|
||||
|
||||
Fixing that is one line in each machine's home-manager config (`home.sessionPath = [ "$HOME/.local/bin" ]`), and one rebuild. Say the word and I will add it to both and hand you the two commands.
|
||||
|
||||
Committed and pushed: `paperclip-harness` → `3043e9d`.
|
||||
|
||||
## Source paths observed
|
||||
|
||||
- `HOME/.local/share/paperclip-harness/bin/office`
|
||||
- `tmp/office-remote-test/.harness/harness.toml`
|
||||
- `.local/bin/office`
|
||||
- `.local/share/paperclip-harness/bin`
|
||||
- `.local/share/paperclip-harness/bin/office`
|
||||
- `nix/store/4yxlfj0`
|
||||
- `v1/chat/completions`
|
||||
- `HOME/.local/bin`
|
||||
|
||||
## Evidence
|
||||
|
||||
- Confidence: 82%
|
||||
- Reason: Captured factual repo structure/configuration patterns from rich discovery
|
||||
|
||||
## Related learned memories <!-- same-turn-2026-11-12 05:27:33 -->
|
||||
|
||||
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-nix-2026-11-12-05-27-33|Rich discovery: nix 2026-11-12 05:27:33]]
|
||||
- context: [[packs/paperclip-harness/20-context/nix|nix]]
|
||||
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|nix repository structure and configuration patterns]]
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Update (2026-11-12 06:13:12)
|
||||
|
||||
Before fix, only .13 had `~/.local/bin` in home.sessionPath; .27 and .51 did not, which is why `office` failed there despite the wrapper existing.
|
||||
|
||||
Post-patch sessionPath:
|
||||
- .27: ${home}/.local/bin, ${home}/.local/share/npm-global/bin, ${home}/bin, Android SDK cmdline-tools/latest/bin, Android SDK platform-tools
|
||||
- .51: ${home}/.local/bin, ${home}/.local/share/npm-global/bin
|
||||
- .13 (unchanged): /home/sam/.cargo/bin, /home/sam/.local/bin, $HOME/.local/share/npm-global/bin
|
||||
|
||||
Build outputs already present so switch is fast:
|
||||
- .27: /nix/store/3g5j39n0kxdfzv7l311vckczmsz2ca17-nixos-system-sam-4screen-desktop-26.11.20260729.0954f7e
|
||||
- .51: /nix/store/hl6mby792nq37d5cm87nk567jck86d9w-nixos-system-sam-thinkpad-26.11.20260926.e158d9e
|
||||
|
||||
Caveat: existing interactive shells keep the old PATH; verification must be in a new shell.
|
||||
|
||||
## Detailed observations from completed turn
|
||||
|
||||
## Rebuild these two machines only
|
||||
|
||||
**`.27` (sam-4screen-desktop):**
|
||||
|
||||
```sh
|
||||
sudo nixos-rebuild switch --flake /etc/nixos#sam-4screen-desktop
|
||||
```
|
||||
|
||||
**`.51` (sam-thinkpad):**
|
||||
|
||||
```sh
|
||||
sudo nixos-rebuild switch --flake /etc/nixos#sam-thinkpad
|
||||
```
|
||||
|
||||
**`.13` needs nothing.** It already has `/home/sam/.local/bin` on `PATH` — that is why `office` works there — and I changed nothing else on it.
|
||||
|
||||
## What I changed, and why `switch` is safe this time
|
||||
|
||||
One line in each machine's `home/sam/home.nix`:
|
||||
|
||||
```nix
|
||||
home.sessionPath = [
|
||||
"${config.home.homeDirectory}/.local/bin"
|
||||
...
|
||||
];
|
||||
```
|
||||
|
||||
That is a home-manager setting, not a nixpkgs change. No system package moves, so the only unit that restarts is `home-manager-sam.service`. Unlike the last time, nothing on the network side is touched.
|
||||
|
||||
Both systems are **already built**, so each `switch` is seconds, not a download:
|
||||
|
||||
```
|
||||
.27 /nix/store/3g5j39n0kxdfzv7l311vckczmsz2ca17-nixos-system-sam-4screen-desktop-26.11.20260729.0954f7e
|
||||
.51 /nix/store/hl6mby792nq37d5cm87nk567jck86d9w-nixos-system-sam-thinkpad-26.11.20260926.e158d9e
|
||||
```
|
||||
|
||||
## What to check afterwards
|
||||
|
||||
On each of `.27` and `.51`, in a **new** shell (the old one keeps the old `PATH`):
|
||||
|
||||
```sh
|
||||
echo $PATH | tr ':' '\n' | grep -c '.local/bin' # expect 1
|
||||
office --help # should print the help
|
||||
```
|
||||
|
||||
`office` was already copied to both machines as `~/.local/share/paperclip-harness/bin/office`, with a wrapper at `~/.local/bin/office`. The wrapper was there but unreachable; this rebuild is what puts it on `PATH`.
|
||||
|
||||
From then on, every machine can drive every other one:
|
||||
|
||||
```sh
|
||||
office <folder> --project "<Name>" --host nixos-desktop # folder on .13
|
||||
office <folder> --new "<Name>" --host sam-thinkpad # folder on .51
|
||||
```
|
||||
|
||||
`--host` copies the commands to the named machine each time, so a machine that has never seen this repository still works.
|
||||
|
||||
## Committed and pushed
|
||||
|
||||
- `.27` → `c2d10d6` on `nixos-4screen`
|
||||
- `.51` → `ea2cb86` on `nixos-thinkpad`
|
||||
- `paperclip-harness` → `3043e9d` (the `office --help` and `--host` fix)
|
||||
|
||||
Backups kept as `home/sam/home.nix.bak-path` on both, if you want to see the exact diff before switching.
|
||||
|
||||
## Evidence
|
||||
|
||||
- Confidence: 85%
|
||||
- Reason: Factual per-host config deltas, exact store paths, and a practical PATH caveat.; enriched with final-answer detail because persistence should be reusable
|
||||
|
||||
## Related learned memories <!-- same-turn-2026-11-12 06:13:12 -->
|
||||
|
||||
- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]]
|
||||
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|NixOS host PATH diffs and prebuilt switch closures]]
|
||||
- runbook: [[packs/paperclip-harness/70-runbooks/tmp-deployment-operations|Rebuild .27 and .51 after adding ~/.local/bin to home-manager PATH]]
|
||||
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Added ~/.local/bin to PATH on .27/.51 and committed; .13 untouched]]
|
||||
- session: [[packs/paperclip-harness/80-sessions/nixos-fleet-path-fix-rollout-27-and-51-need-home-rebuild-13-|NixOS fleet PATH fix rollout: .27 and .51 need home rebuild, .13 already correct]]
|
||||
|
||||
@@ -1640,3 +1640,43 @@ Rollback: restore the `.bak-*` files and `git checkout` the previous commit per
|
||||
- session: [[packs/paperclip-harness/80-sessions/pi-1-5-0-alignment-across-three-nixos-hosts-via-pinned-nixpk|Pi 1.5.0 alignment across three NixOS hosts via pinned nixpkgs-pi input]]
|
||||
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-nixos-2026-11-12-03-29-14|Rich discovery: NixOS 2026-11-12 03:29:14]]
|
||||
- context: [[packs/paperclip-harness/20-context/home|NixOS]]
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Update (2026-11-12 06:13:12)
|
||||
|
||||
Trigger: `office` is not found on a NixOS box because `~/.local/bin` is missing from home-manager's sessionPath.
|
||||
|
||||
Prerequisites: SSH access to the host; edits already made to /etc/nixos/home/sam/home.nix; a backup at home/sam/home.nix.bak-path.
|
||||
|
||||
Which machines: only 192.168.20.27 (sam-4screen-desktop) and 192.168.20.51 (sam-thinkpad) need the rebuild. 192.168.20.13 (nixos-desktop) already had ~/.local/bin on PATH and was not changed.
|
||||
|
||||
Steps:
|
||||
1. On .27: `sudo nixos-rebuild switch --flake /etc/nixos#sam-4screen-desktop`
|
||||
2. On .51: `sudo nixos-rebuild switch --flake /etc/nixos#sam-thinkpad`
|
||||
Both are pre-built (no download); typically seconds.
|
||||
3. Verify in a NEW shell (old shell keeps stale PATH):
|
||||
`echo $PATH | tr ':' '\n' | grep -c '.local/bin'` -> expect 1
|
||||
`office --help` -> should print help
|
||||
|
||||
Expected system closures: .27 -> ...-nixos-system-sam-4screen-desktop-26.11.20260729.0954f7e; .51 -> ...-nixos-system-sam-thinkpad-26.11.20260926.e158d9e.
|
||||
|
||||
Scope/safety: home-manager-only change (home.sessionPath), so only home-manager-sam.service restarts. No system package moves and no network changes.
|
||||
|
||||
Rollback: restore home/sam/home.nix.bak-path over home/sam/home.nix and re-run the same nixos-rebuild switch command on that host (or git revert the commit: .27 c2d10d6, .51 ea2cb86).
|
||||
|
||||
Troubleshooting: if `office` still isn't found, confirm wrapper exists at ~/.local/bin/office pointing at ~/.local/share/paperclip-harness/bin/office, then open a fresh login shell.
|
||||
|
||||
## Evidence
|
||||
|
||||
- Confidence: 90%
|
||||
- Reason: Repeatable, evidence-backed multi-host procedures with exact commands, verification, expected outputs, and rollback.
|
||||
|
||||
## Related learned memories <!-- same-turn-2026-11-12 06:13:12 -->
|
||||
|
||||
- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]]
|
||||
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|NixOS host PATH diffs and prebuilt switch closures]]
|
||||
- runbook: [[packs/paperclip-harness/70-runbooks/tmp-deployment-operations|Rebuild .27 and .51 after adding ~/.local/bin to home-manager PATH]]
|
||||
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Added ~/.local/bin to PATH on .27/.51 and committed; .13 untouched]]
|
||||
- session: [[packs/paperclip-harness/80-sessions/nixos-fleet-path-fix-rollout-27-and-51-need-home-rebuild-13-|NixOS fleet PATH fix rollout: .27 and .51 need home rebuild, .13 already correct]]
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
type: session
|
||||
id: session.paperclip-harness.nixos-fleet-path-fix-rollout-27-and-51-need-home-rebuild-13-
|
||||
title: NixOS fleet PATH fix rollout: .27 and .51 need home rebuild, .13 already correct
|
||||
status: active
|
||||
source_of_truth: false
|
||||
freshness: current
|
||||
last_reviewed: 2026-11-12
|
||||
tags:
|
||||
- pack/paperclip-harness
|
||||
- agent-memory/session
|
||||
- nixos
|
||||
- home-manager
|
||||
- paperclip-harness
|
||||
- office-cli
|
||||
- flux-fleet
|
||||
- path
|
||||
- rebuild
|
||||
---
|
||||
|
||||
# NixOS fleet PATH fix rollout: .27 and .51 need home rebuild, .13 already correct
|
||||
|
||||
Goal: make the `office` CLI (from paperclip-harness) reachable on all three NixOS machines by getting `~/.local/bin` onto home-manager PATH.
|
||||
|
||||
Findings per machine (config at /etc/nixos/home/sam/home.nix on each):
|
||||
- 192.168.20.13 (nixos-desktop): already had `/home/sam/.local/bin` in `home.sessionPath` (line ~4, alongside /home/sam/.cargo/bin and npm-global bin). Why `office` already works there. NOT changed.
|
||||
- 192.168.20.27 (sam-4screen-desktop): sessionPath originally only npm-global bin + $HOME/bin + Android SDK paths; missing `~/.local/bin`. PATCHED.
|
||||
- 192.168.20.51 (sam-thinkpad): sessionPath originally only npm-global bin; missing `~/.local/bin`. PATCHED.
|
||||
|
||||
Patch applied (one line, prefixed first in home.sessionPath using `${config.home.homeDirectory}/.local/bin`):
|
||||
- .27 resulting list: .local/bin, .local/share/npm-global/bin, ~/bin, Android SDK cmdline-tools + platform-tools (with comment that Android SDK is NOT Nix-managed, see nix-ld block in configuration.nix).
|
||||
- .51 resulting list: .local/bin, .local/share/npm-global/bin.
|
||||
|
||||
Shared home.sessionVariables on both: QT_QPA_PLATFORM=wayland, QT_WAYLAND_DISABLE_WINDOWDECORATION=1, GOOSE_DISABLE_KEYRING=1, NIXOS_OZONE_WL=1, FALCODE_NOTIFY_SCRIPT=/bin/true. .27 additionally sets ANDROID_HOME / ANDROID_SDK_ROOT to ~/Android/Sdk. Both use gtk with Adwaita-dark (gnome-themes-extra).
|
||||
|
||||
Build results (already built, no download needed at switch time):
|
||||
- .27 -> /nix/store/3g5j39n0kxdfzv7l311vckczmsz2ca17-nixos-system-sam-4screen-desktop-26.11.20260729.0954f7e
|
||||
- .51 -> /nix/store/hl6mby792nq37d5cm87nk567jck86d9w-nixos-system-sam-thinkpad-26.11.20260926.e158d9e
|
||||
|
||||
Commits pushed:
|
||||
- .27 branch master -> c2d10d6 "home: put ~/.local/bin on PATH" (f56f053..c2d10d6)
|
||||
- .51 branch main -> ea2cb86 "home: put ~/.local/bin on PATH" (40ccd7e..ea2cb86)
|
||||
- paperclip-harness -> 3043e9d (office --help and --host fix)
|
||||
|
||||
Safety note: change is home-manager-only, so only home-manager-sam.service restarts; no nixpkgs/system package movement, network config untouched (unlike a previous incident). Backups left at home/sam/home.nix.bak-path on both .27 and .51.
|
||||
|
||||
`office` binary already copied to .27 and .51 at ~/.local/share/paperclip-harness/bin/office with wrapper ~/.local/bin/office that was unreachable until rebuild.
|
||||
|
||||
Cross-machine usage: `office <folder> --project "<Name>" --host nixos-desktop` (folder lives on .13) or `office <folder> --new "<Name>" --host sam-thinkpad`; `--host` copies commands to the named machine so a machine that has never seen the repo still works.
|
||||
|
||||
## Evidence
|
||||
|
||||
- Confidence: 85%
|
||||
- Reason: Rich sanitized snapshot of a multi-machine config discovery, patch, build, and commit rollout with exact hostnames, paths, store outputs, and commit hashes.
|
||||
|
||||
## Related
|
||||
|
||||
- [[packs/paperclip-harness/00-system/pi-agent/memory-manifest|Memory Manifest]]
|
||||
|
||||
## Related learned memories <!-- same-turn-2026-11-12 06:13:12 -->
|
||||
|
||||
- context: [[packs/paperclip-harness/20-context/paperclip-harness|paperclip-harness]]
|
||||
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|NixOS host PATH diffs and prebuilt switch closures]]
|
||||
- runbook: [[packs/paperclip-harness/70-runbooks/tmp-deployment-operations|Rebuild .27 and .51 after adding ~/.local/bin to home-manager PATH]]
|
||||
- action: [[packs/paperclip-harness/40-actions/2026-11-10-wrote-docs-ui-layout-md-and-three-live-layout-mockups|Added ~/.local/bin to PATH on .27/.51 and committed; .13 untouched]]
|
||||
- session: [[packs/paperclip-harness/80-sessions/nixos-fleet-path-fix-rollout-27-and-51-need-home-rebuild-13-|NixOS fleet PATH fix rollout: .27 and .51 need home rebuild, .13 already correct]]
|
||||
@@ -0,0 +1,132 @@
|
||||
---
|
||||
type: session
|
||||
id: session.paperclip-harness.rich-discovery-nix-2026-11-12-05-27-33
|
||||
title: Rich discovery: nix 2026-11-12 05:27:33
|
||||
status: active
|
||||
source_of_truth: false
|
||||
freshness: current
|
||||
last_reviewed: 2026-11-12
|
||||
tags:
|
||||
- pack/paperclip-harness
|
||||
- agent-memory/session
|
||||
- autolearn
|
||||
- rich-discovery
|
||||
- repo/nix
|
||||
---
|
||||
|
||||
# Rich discovery: nix 2026-11-12 05:27:33
|
||||
|
||||
Sanitized rich discovery snapshot captured automatically from the completed turn.
|
||||
|
||||
|
||||
## Source paths observed
|
||||
|
||||
- `HOME/.local/share/paperclip-harness/bin/office`
|
||||
- `tmp/office-remote-test/.harness/harness.toml`
|
||||
- `.local/bin/office`
|
||||
- `.local/share/paperclip-harness/bin`
|
||||
- `.local/share/paperclip-harness/bin/office`
|
||||
- `nix/store/4yxlfj0`
|
||||
- `v1/chat/completions`
|
||||
- `HOME/.local/bin`
|
||||
|
||||
## Final answer snapshot
|
||||
|
||||
## Is everything working — measured just now
|
||||
|
||||
| | State | Evidence |
|
||||
|---|---|---|
|
||||
| **Pi** | 1.5.0 on all three, same bytes | `.13`, `.27`, `.51` all resolve to `/nix/store/4yxlfj0…-pi-coding-agent-1.5.0` |
|
||||
| **Paperclip** | running, `2026.916.1` | service active, `GET /api/health` 200, web UI 200 at `http://127.0.0.1:3100/` |
|
||||
| **Agent runs** | working again | 3 runs failed before the shim, then succeeded (69 s, then 24 s) |
|
||||
| **omniroute** | working | `/v1/models` returns **486** models, a direct completion returns 200 |
|
||||
| **headroom** | tool yes, provider no | `/v1/compress` 200 with `messages`; `/v1/chat/completions` returns **502** for both `auto/cheap` and `deepseek/deepseek-v4-flash`; `/stats` shows 153 requests, 2 compressed |
|
||||
| **TUIOS** | 0.12.1 | both sessions' 8 panes verified by process: each runs `bin/harness -screen …` |
|
||||
|
||||
One thing worth knowing about headroom: my earlier `compress` call returned 400 because I sent `text`; it wants `messages`. Tool path is fine. Provider path is still broken for the same reason as before — its LiteLLM has no entry for the router's model ids.
|
||||
|
||||
## CLI versus web — they are the same thing
|
||||
|
||||
There is no separate web backend. Port 3100 serves the HTML at `/` and the JSON at `/api/...` from one process and one Postgres database. So a cost problem on the web is an **API and aggregation** problem, not a web problem — and you can see the same fault in my terminal panes, which is how I found it.
|
||||
|
||||
Two separate faults, both measured:
|
||||
|
||||
**1. `costCents` is a whole number, so small tasks show zero.**
|
||||
|
||||
```
|
||||
AUD-173 costCents 0 inputTokens 13023 outputTokens 2253
|
||||
usageJson.costUsd = 0.001934786 → 0.19 cents → displayed as 0
|
||||
AUD-163 costCents 3 303609 input tokens → 2.9 cents → displayed as 3
|
||||
```
|
||||
|
||||
Any task that costs less than half a cent reads as free. On the web that looks like "costs aren't working"; the exact figure is in each run's `usageJson`.
|
||||
|
||||
**2. One task's summary is stale and I cannot explain it away.**
|
||||
|
||||
```
|
||||
AUD-172 costCents 0 inputTokens 0 outputTokens 0 runCount 4 runtimeMs 81645
|
||||
its successful run's usageJson: 34217 input, 7187 output, $0.014134
|
||||
```
|
||||
|
||||
`runtimeMs` proves the summary did aggregate all four runs. But tokens and cost are zero, while the run record holds both. So the per-task summary was computed before that run's usage was written and never recomputed. AUD-173 and AUD-163 summarise correctly, so it is not universal.
|
||||
|
||||
This is a Paperclip-side defect, in the endpoint the web uses. `docs/NEXT.md` records it with these numbers. I am not going to patch Paperclip's internals without you asking me to.
|
||||
|
||||
## `office --help`
|
||||
|
||||
```
|
||||
office — open a folder as a project, and say what else can be run
|
||||
|
||||
THE RULE
|
||||
The folder and the session live on the same machine. …
|
||||
WHEN IT DOES / WHAT IT WRITES
|
||||
COMMANDS office, board, board --replace, board --reset, project-reset,
|
||||
merge, review, triage — one line each
|
||||
MACHINES nixos-desktop .13 192.168.20.13
|
||||
sam-4screen-desktop .27 192.168.20.27
|
||||
sam-thinkpad .51 192.168.20.51
|
||||
EVERY MACHINE, FROM THE TERMINAL a worked example for .13, .27 and .51
|
||||
ON ANOTHER MACHINE where the copy lives
|
||||
AFTER A REBOOT run board once per project
|
||||
KEYS INSIDE THE PANES ( ) tab, tab panel, r, R, ?
|
||||
MORE the four docs to read next
|
||||
```
|
||||
|
||||
## While writing it I found `--host` could never have worked
|
||||
|
||||
It ran `office` on the other machine at *this* machine's path, so it needed the repository to exist there at the same location. `.27` and `.51` have no repository at all. It also checked the folder existed **locally** before deciding the folder was remote, so it always answered "no folder at …".
|
||||
|
||||
Both fixed. `--host` now copies `bin/` to `~/.local/share/paperclip-harness/bin/` on the other machine, leaves a short `~/.local/bin/office` there, and the folder is checked on the machine that owns it. Verified against both machines:
|
||||
|
||||
```
|
||||
$ office /tmp/office-remote-test --new office-remote-test --host sam-4screen-desktop --dry
|
||||
on sam-4screen-desktop: exec "$HOME/.local/share/paperclip-harness/bin/office" …
|
||||
would create a Paperclip project 'office-remote-test' for /tmp/office-remote-test
|
||||
dry run: nothing was written
|
||||
switch to it: tuios attach --host sam-4screen-desktop office-remote-test
|
||||
```
|
||||
|
||||
`.27` and `.51` now have the commands and a working `--help`. One gap remains: **`~/.local/bin` is not on `PATH` on either machine**, so bare `office` does not resolve there — the full path does:
|
||||
|
||||
```sh
|
||||
~/.local/share/paperclip-harness/bin/office <folder>
|
||||
```
|
||||
|
||||
Fixing that is one line in each machine's home-manager config (`home.sessionPath = [ "$HOME/.local/bin" ]`), and one rebuild. Say the word and I will add it to both and hand you the two commands.
|
||||
|
||||
Committed and pushed: `paperclip-harness` → `3043e9d`.
|
||||
|
||||
## Evidence
|
||||
|
||||
- Confidence: 86%
|
||||
- Reason: Preserve full sanitized discovery detail from a rich investigation turn
|
||||
|
||||
## Related
|
||||
|
||||
- [[packs/paperclip-harness/00-system/pi-agent/memory-manifest|Memory Manifest]]
|
||||
|
||||
## Related learned memories <!-- same-turn-2026-11-12 05:27:33 -->
|
||||
|
||||
- session: [[packs/paperclip-harness/80-sessions/rich-discovery-nix-2026-11-12-05-27-33|Rich discovery: nix 2026-11-12 05:27:33]]
|
||||
- context: [[packs/paperclip-harness/20-context/nix|nix]]
|
||||
- observation: [[packs/paperclip-harness/60-observations/tmp-repository-structure-and-configuration-patterns|nix repository structure and configuration patterns]]
|
||||
BIN
bin/harness
BIN
bin/harness
Binary file not shown.
@@ -28,9 +28,63 @@ for _p in (
|
||||
if _p not in os.environ.get("PATH", ""):
|
||||
os.environ["PATH"] = _p + ":" + os.environ.get("PATH", "")
|
||||
|
||||
API = os.environ.get("PAPERCLIP_API_URL", "http://127.0.0.1:3100").rstrip("/")
|
||||
COMPANY = os.environ.get("PAPERCLIP_COMPANY_ID", "d4921013-e5da-4d88-8016-57d5746e2d03")
|
||||
KEY = os.environ.get("PAPERCLIP_BOARD_KEY", "")
|
||||
|
||||
def _from_secrets(name):
|
||||
"""Read one variable out of the person's own secrets file.
|
||||
|
||||
A terminal started by a desktop session does not inherit systemd's
|
||||
environment.d, so the key can be missing from the shell on a machine where
|
||||
the file is right there. The file is the person's; this only reads it.
|
||||
"""
|
||||
path = os.path.join(_HOME, ".config", "environment.d")
|
||||
try:
|
||||
names = sorted(os.listdir(path))
|
||||
except OSError:
|
||||
return ""
|
||||
for fn in names:
|
||||
if not fn.endswith(".conf"):
|
||||
continue
|
||||
try:
|
||||
with open(os.path.join(path, fn)) as fh:
|
||||
for line in fh:
|
||||
line = line.strip()
|
||||
if line.startswith("#") or "=" not in line:
|
||||
continue
|
||||
k, _, v = line.partition("=")
|
||||
if k.strip() == name:
|
||||
return v.strip().strip('"').strip("'")
|
||||
except OSError:
|
||||
continue
|
||||
return ""
|
||||
|
||||
|
||||
def _var(name, default=""):
|
||||
return os.environ.get(name) or _from_secrets(name) or default
|
||||
|
||||
|
||||
def _resolve_api():
|
||||
"""Where Paperclip is. Its own address if given, else this machine, else .13.
|
||||
|
||||
Paperclip runs on one machine in this house. A session on another machine
|
||||
has to ask that one, so the fallback is the machine that hosts it rather
|
||||
than a loopback address that answers nothing there.
|
||||
"""
|
||||
told = _var("PAPERCLIP_API_URL")
|
||||
if told:
|
||||
return told.rstrip("/")
|
||||
here = "http://127.0.0.1:3100"
|
||||
try:
|
||||
with urllib.request.urlopen(here + "/api/health", timeout=2) as f:
|
||||
if f.status == 200:
|
||||
return here
|
||||
except Exception:
|
||||
pass
|
||||
return "http://192.168.20.13:3100"
|
||||
|
||||
|
||||
API = _resolve_api()
|
||||
COMPANY = _var("PAPERCLIP_COMPANY_ID", "d4921013-e5da-4d88-8016-57d5746e2d03")
|
||||
KEY = _var("PAPERCLIP_BOARD_KEY")
|
||||
|
||||
BLOCKING_SEVERITIES = {"critical", "high"}
|
||||
|
||||
|
||||
22
bin/office
22
bin/office
@@ -74,19 +74,33 @@ def create_project(name, folder, dry):
|
||||
Adopting a folder that Paperclip has never heard of needed two steps before:
|
||||
make the project in the browser, then run this command. One command is enough.
|
||||
"""
|
||||
# A folder that is a git repository gets a branch per task in its own
|
||||
# worktree. A folder that is not cannot: asking for a worktree would create
|
||||
# a project whose every run fails. Those keep the folder in place instead.
|
||||
is_repo = subprocess.run(["git", "-C", folder, "rev-parse", "--is-inside-work-tree"],
|
||||
capture_output=True, text=True, timeout=30).stdout.strip() == "true"
|
||||
body = {
|
||||
"name": name,
|
||||
"description": "Adopted from %s by bin/office." % folder,
|
||||
"status": "backlog",
|
||||
"workspace": {"name": name, "sourceType": "git_repo", "cwd": folder, "isPrimary": True},
|
||||
"executionWorkspacePolicy": {
|
||||
"workspace": {
|
||||
"name": name,
|
||||
"sourceType": "git_repo" if is_repo else "local_path",
|
||||
"cwd": folder,
|
||||
"isPrimary": True,
|
||||
},
|
||||
}
|
||||
if is_repo:
|
||||
body["executionWorkspacePolicy"] = {
|
||||
"enabled": True,
|
||||
"defaultMode": "isolated_workspace",
|
||||
"allowIssueOverride": True,
|
||||
"workspaceStrategy": {"type": "git_worktree", "baseRef": "main",
|
||||
"branchTemplate": "{{issue.identifier}}-{{slug}}"},
|
||||
},
|
||||
}
|
||||
}
|
||||
else:
|
||||
h.say(" not a git repository: the project keeps the folder in place,")
|
||||
h.say(" so a task changes these files directly. No branch, no worktree.")
|
||||
if dry:
|
||||
h.say("would create Paperclip project %r for %s" % (name, folder))
|
||||
return "(dry run)"
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
@@ -29,21 +30,79 @@ type Client struct {
|
||||
//
|
||||
// The token is read from PAPERCLIP_BOARD_KEY (the person's key) and only falls
|
||||
// back to PAPERCLIP_API_KEY for read-only use. The token is never logged.
|
||||
// fromSecrets reads one variable out of the person's own secrets file, for the
|
||||
// same reason as the shell fallback: a terminal started by a desktop session
|
||||
// does not inherit systemd's environment.d. The file is the person's; this
|
||||
// only reads it, and the value is never logged.
|
||||
func fromSecrets(name string) string {
|
||||
home, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
dir := filepath.Join(home, ".config", "environment.d")
|
||||
entries, err := os.ReadDir(dir)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
for _, e := range entries {
|
||||
if e.IsDir() || !strings.HasSuffix(e.Name(), ".conf") {
|
||||
continue
|
||||
}
|
||||
data, err := os.ReadFile(filepath.Join(dir, e.Name()))
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
for _, line := range strings.Split(string(data), "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if line == "" || strings.HasPrefix(line, "#") {
|
||||
continue
|
||||
}
|
||||
k, v, ok := strings.Cut(line, "=")
|
||||
if ok && strings.TrimSpace(k) == name {
|
||||
return strings.Trim(strings.TrimSpace(v), `"'`)
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func varOr(name, fallback string) string {
|
||||
if v := os.Getenv(name); v != "" {
|
||||
return v
|
||||
}
|
||||
if v := fromSecrets(name); v != "" {
|
||||
return v
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
// reachable says whether something answers at this address right now.
|
||||
func reachable(url string) bool {
|
||||
c := &http.Client{Timeout: 2 * time.Second}
|
||||
resp, err := c.Get(url)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
resp.Body.Close()
|
||||
return resp.StatusCode == 200
|
||||
}
|
||||
|
||||
func New() (*Client, error) {
|
||||
base := os.Getenv("PAPERCLIP_API_URL")
|
||||
base := varOr("PAPERCLIP_API_URL", "")
|
||||
if base == "" {
|
||||
base = "http://127.0.0.1:3100"
|
||||
if !reachable(base + "/api/health") {
|
||||
// Paperclip lives on one machine; a session on another has to ask it.
|
||||
base = "http://192.168.20.13:3100"
|
||||
}
|
||||
}
|
||||
company := os.Getenv("PAPERCLIP_COMPANY_ID")
|
||||
if company == "" {
|
||||
company = "d4921013-e5da-4d88-8016-57d5746e2d03"
|
||||
}
|
||||
token := os.Getenv("PAPERCLIP_BOARD_KEY")
|
||||
company := varOr("PAPERCLIP_COMPANY_ID", "d4921013-e5da-4d88-8016-57d5746e2d03")
|
||||
token := varOr("PAPERCLIP_BOARD_KEY", "")
|
||||
if token == "" {
|
||||
token = os.Getenv("PAPERCLIP_API_KEY")
|
||||
token = varOr("PAPERCLIP_API_KEY", "")
|
||||
}
|
||||
if token == "" {
|
||||
return nil, errors.New("no key in the environment: set PAPERCLIP_BOARD_KEY")
|
||||
return nil, errors.New("no key found: set PAPERCLIP_BOARD_KEY, or put it in ~/.config/environment.d/*.conf")
|
||||
}
|
||||
return &Client{
|
||||
BaseURL: strings.TrimRight(base, "/"),
|
||||
|
||||
Reference in New Issue
Block a user