@ours.network/install 0.17.0-nightly.9 → 0.18.0-nightly.1

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 CHANGED
@@ -1,339 +1,61 @@
1
1
  # @ours.network/install — `ours-install`
2
2
 
3
- The **unified ours.network stack installer**. ONE guided ~3-minute flow that installs the WHOLE
4
- stack for someone who already has Claude Code, Codex, and/or Hermes, safely offers optional
5
- voice-message transcription, then hands back a single copy-paste prompt for remaining setup.
6
-
7
- ## Install
8
-
9
- **Recommended — a persistent, versioned, integrity-checked command on your PATH:**
10
-
11
- ```sh
12
- npm i -g @ours.network/install && ours-install
13
- ```
14
-
15
- Re-run (or update / add a skipped piece) any time with just `ours-install`.
16
-
17
- **One-off, no global install:**
3
+ The guided installer for the ours shared daemon, MCP adapter, harness plugins,
4
+ ours-fleet, and optional connectors.
18
5
 
19
6
  ```sh
20
- npx @ours.network/install
21
- ```
22
-
23
- **Fallback for machines without npm** (least secure — pipes a script straight into your shell):
24
-
25
- ```sh
26
- curl -fsSL https://raw.githubusercontent.com/adapt-toolkit/ours-mcp/main/packages/installer/install.sh | bash
27
- ```
28
-
29
- The `curl … | bash` bootstrap simply gets Node.js/npm sorted, then does the `npm i -g
30
- @ours.network/install` and runs `ours-install` for you. `ours-install` is the single front door;
31
- `ours-codex-install` is kept as a **thin alias** that hands off to it (use
32
- `ours-codex-install --codex-only` for the legacy Codex-only path).
33
-
34
- The installer is a small **self-contained** Node package (Node built-ins only — no runtime
35
- dependency on the things it installs): an ASCII banner, tasteful colour (degrades under `NO_COLOR`
36
- / no-tty), and plain-language **what + why** for every step.
37
-
38
- ## The flow (what the user sees)
39
-
40
- 1. **Pre-flight** — a short checklist, not a wall of logs: platform (Linux / macOS / WSL; native
41
- Windows prints a WSL pointer and exits), Node.js, and **harness detection with alias-safety**.
42
- Before ever calling `claude` / `codex`, it confirms each resolves to a **real executable** that
43
- answers `--version` promptly. A shell alias / hanging wrapper is **never called** (that would
44
- hang the run) — it's reported plainly with a fix, and a manual-install path is always offered.
45
- If neither harness exists it says so and exits.
46
- 2. **Five consent gates**, each paced with a clean `✓ … No problems.` line + an explicit
47
- **Continue?** — never a start-twice-then-ask, never a silent failure:
48
- - **1/5 the shared ours daemon** — its own step, before every consumer, and it owns its own
49
- configuration: the **broker** (end-to-end encrypted; the broker never sees message content —
50
- almost everyone just presses Enter) and the **listen port**, which is now an explicit
51
- question rather than one you only hear about when `3050` is busy. The default is still
52
- `3050` (the next free port when that is taken), so **Enter and every non-interactive run
53
- land exactly where they always did**. The answer is validated (a real port, not reserved by
54
- another component, not already in use, not already claimed by another daemon in this run),
55
- persisted to `~/.ours/config.json`, and it is the endpoint everything below is wired to.
56
- Then: write config → optional voice setup → install/start ONCE → boot service. On a re-run
57
- it reuses the running config (no re-ask) and only updates when you say yes. Complete voice
58
- setup is kept without prompting. Missing/incomplete setup is offered before the first start
59
- or pending update restart, then delegated to the canonical `ours-mcp voice-setup` provider
60
- selector and hidden API-key prompt. Accepted setup owns the one restart/readiness
61
- transaction; declining or already-ready setup preserves the normal core lifecycle. The
62
- secret is written atomically to mode-`0600` config; a failed daemon reload rolls back.
63
- - **2/5 harness plugins** — the installer **drives the plugin CLIs itself**
64
- (`claude plugin marketplace add …` + `claude plugin install ours@ours.network`;
65
- `codex plugin marketplace add …` + `codex plugin add ours@ours-codex-marketplace`). Choosing
66
- Codex also installs the `ours-codex` live launcher in the same step. Any failure / alias
67
- prints the exact manual commands and continues — it **never dead-ends**.
68
- - **3/5 ours-fleet** — makes your harnesses persistent, always-online agent teams that survive
69
- a reboot; runs `ours-fleet init`. Default **Yes**.
70
- - **4/5 Telegram connector** — install-only (no bot tokens here), then a question asked
71
- **independently of every other consumer**: use the shared daemon from step 1, or run against
72
- its **own dedicated daemon**? Default (and Enter, and non-interactive) is the shared one.
73
- A dedicated daemon is provisioned with its own port, its own state directory
74
- (`~/.ours-tg`) and its own boot unit (`ours-tg.service`), and the connector is wired to
75
- that endpoint. Then optionally installed as a boot service itself.
76
- - **5/5 Rooms (ours-cowork)** — durable mission rooms. Default **No**. Configures its own
77
- surface (the deployment's broker, its state directory `~/.ours-cowork`, and its loopback
78
- **console port** — default `3052`, validated the same way), then asks the same
79
- shared-vs-dedicated daemon question, defaulting to shared. A dedicated Rooms daemon gets
80
- `~/.ours-rooms` and `ours-rooms.service`. Then `ours-cowork install-service`. See
81
- **Daemon topology** for when the question is asked at all.
82
- 3. **Summary + hand-off** — a recap (skipped/failed rows call out the fix), then a **literal
83
- copy-paste prompt** (human identity + fleet + Telegram + Rooms) with the steps for any
84
- skipped/failed component dropped out. Copied to the clipboard where supported.
85
-
86
- The human identity is created idempotently after the daemon becomes reachable. Because
87
- `curl … | bash` gives the script its input over the pipe, every prompt is read from the
88
- controlling terminal (`/dev/tty`), so the flow still works piped.
89
-
90
- ## Nightly-only profile flow
91
-
92
- The `nightly` channel uses a topology-first flow. This is a hard release boundary: `latest`
93
- keeps the five-step behavior, prompts, package tags, files, service names, and uninstall output
94
- described above and never reads the profile registry.
95
-
96
- Nightly first discovers only configured local candidates: the registry, the historical default
97
- config, known `~/.ours-tg` / `~/.ours-rooms` configs, Telegram and Rooms daemon blocks, known ours
98
- systemd/launchd definitions, and an exact manual entry. It never scans a port range. Hosts are
99
- limited to `localhost` / `127.0.0.1` (stored as `127.0.0.1`); remote names, URLs, credentials,
100
- `0.0.0.0`, IPv6 loopback, and HTTPS are rejected. A candidate is deduplicated only when both its
101
- endpoint and canonical state directory agree. Port, state, service, or config collisions stop the
102
- plan before mutation.
103
-
104
- The durable registry is `~/.ours/installer-profiles.json` (schema v1, atomic mode `0600`):
105
-
106
- ```json
107
- {
108
- "version": 1,
109
- "profiles": {
110
- "default": {
111
- "label": "Default ours daemon",
112
- "host": "127.0.0.1",
113
- "port": 3050,
114
- "configPath": "/home/me/.ours/config.json",
115
- "stateDir": "/home/me/.ours",
116
- "serviceName": "",
117
- "ownership": { "config": true, "service": true, "state": true }
118
- }
119
- },
120
- "harnessAssociations": { "codex": "default" }
121
- }
7
+ npm install --global @ours.network/install
8
+ ours-install
122
9
  ```
123
10
 
124
- It stores topology and explicit installer ownership only—never tokens, token sources, status,
125
- PIDs, versions, or running state. `OURS_INSTALL_PROFILES` is a test/operator override. Telegram
126
- and Rooms remain authoritative in their own configs; their associations are reverse-indexed from
127
- those real daemon blocks rather than copied into the registry.
128
-
129
- After selecting or creating one profile, Nightly selects Claude Code, Codex/`ours-codex`, Hermes,
130
- Telegram, and Rooms for that exact daemon, shows one review, and confirms once. A harness already
131
- associated elsewhere requires explicit reassignment. The global `@ours.network/mcp@nightly`
132
- package is installed/updated at most once; only the selected installer-owned daemon is restarted.
133
- Snapshotted connector/registry config bytes roll back if validation or service apply fails. Npm changes and
134
- new identity state are intentionally not destroyed during rollback.
135
-
136
- `OURS_ASSUME_YES=1` selects or creates only the historical `3050` / `~/.ours` default, preserves
137
- existing harness assignments, assigns otherwise-unassociated detected harnesses to it, keeps the
138
- current harness/fleet defaults, and skips Telegram and Rooms.
139
-
140
- ## Daemon topology
141
-
142
- A clean deployment installs, configures and starts **one shared** ours daemon first, then wires
143
- every client to that same daemon. That is the default and the backward-compatible answer.
144
-
145
- **Optional isolation.** The Telegram connector and Rooms may each instead be given their own
146
- daemon, chosen independently of one another. Isolation is only real when all three of these are
147
- separate, which is what the installer provisions:
148
-
149
- | | shared | dedicated (Telegram) | dedicated (Rooms) |
150
- |---|---|---|---|
151
- | listen port | your step-1 answer | its own, validated against every other daemon in the run | likewise |
152
- | state directory | `~/.ours` | `~/.ours-tg` — the daemon's API token lives here | `~/.ours-rooms` |
153
- | boot unit | `ours.service` | `ours-tg.service` (via `OURS_SERVICE_NAME`) | `ours-rooms.service` |
154
- | config file | `~/.ours/config.json` | `~/.ours-tg/config.json` | `~/.ours-rooms/config.json` |
11
+ The installer treats the operator CLI and the MCP adapter as separate packages:
155
12
 
156
- Without a distinct service name, `ours-mcp install-service` would write the **same** unit for
157
- both and the second daemon would silently overwrite the first's port and state directory. See
158
- `packages/core/src/service-instance.ts`; a daemon with no instance name keeps exactly the
159
- historical `ours.service` / `solutions.adaptframework.ours`.
13
+ - `@ours.network/cli` owns daemon configuration, lifecycle, and boot services.
14
+ - `@ours.network/mcp` is the stdio MCP adapter spawned by agent harnesses.
160
15
 
161
- **Rooms has a third answer.** `ours-cowork` used to host its own daemon, always. It now also
162
- supports an **external** one, so Rooms answers the same shared-vs-dedicated question — plus
163
- `embedded`, cowork's own, which is what every install predating that support runs.
16
+ It configures a selected state directory, installs both packages, starts the
17
+ daemon with `ours daemon start`, installs its service with
18
+ `ours daemon install-service`, and uses `ours identity create-root` for the Human
19
+ identity. It never asks ours-mcp to boot or configure a daemon.
164
20
 
165
- Its config carries an optional `daemon` block. Absent means embedded; external is
166
- `{ mode: 'external', endpoint, stateDir }` and **requires both halves**, because cowork stores no
167
- token and its SDK reads `<stateDir>/daemon-token`. Env equivalents are `OURS_COWORK_DAEMON_MODE`
168
- / `_ENDPOINT` / `_STATE_DIR`, and the service unit carries only those — never a token.
21
+ The default is one shared daemon at `~/.ours` on port 3050. A non-default daemon
22
+ must be selected coherently with a config file or matching port and state
23
+ directory. Harnesses receive that selection through `OURS_CONFIG`; there is no
24
+ separate per-harness daemon registry.
169
25
 
170
- Two things follow from cowork's boot being **fail-closed** (an unreachable endpoint, a non-ours
171
- daemon, or a mismatched state directory aborts startup, with no embedded fallback):
172
-
173
- - A build that **predates** the external mode is never handed a block. The check is on the
174
- **installed version**, not the channel: `COWORK_EXTERNAL_MIN_VERSION` in `lib/logic.mjs` is
175
- pinned to `0.4.1-nightly.20260816.4aaf940`, the first published cowork that implements it
176
- (verified against the registry *and* the tarball's contents). A channel-only gate would have
177
- accepted an earlier nightly of the same core version. `cowork@latest` (0.4.0) predates the
178
- mode, so the stable installer keeps Rooms embedded, names the build it found and the version it
179
- needed, and points at `OURS_CHANNEL=nightly`. An unreadable version keeps Rooms embedded too —
180
- it never guesses.
181
- - An install **already running embedded** is never migrated behind the user's back — headless
182
- runs leave it exactly as it is, and interactive runs ask first.
183
-
184
- Note the two different `stateDir` keys: the top-level one is cowork's own private state;
185
- `daemon.stateDir` is the **ours daemon's** state directory. Confusing them fails closed at boot.
186
-
187
- What wiring a client to a daemon takes differs per client:
188
-
189
- - **The harness plugins** need nothing extra: each is `ours-mcp proxy`, which reads the daemon's
190
- own config (`OURS_CONFIG`, else `~/.ours/config.json`). `autoStart` is off by default, so a proxy
191
- whose daemon is down reports it rather than quietly starting a second one.
192
- - **The Telegram connector keeps its own config file** and never inherits the daemon's, so the
193
- installer writes `~/.ours-telegram/config.json` (honouring `OURS_TG_CONFIG`) **before** the
194
- connector is started or installed as a service — `install-service` bakes whatever it resolves
195
- into the service unit as environment variables, and those outrank the file from then on.
196
- Three keys are written, so whichever connector generation is installed finds what it reads:
197
- - `daemonUrl` + `daemonStateDir` — for `>=0.3.3-nightly.1`, which attaches to the running daemon
198
- over `/api/v1`. **Both** are required: with neither, its SDK never reads `~/.ours/config.json`
199
- and falls back to the built-in `127.0.0.1:3050`, missing a daemon on any other port; with the
200
- endpoint alone it refuses outright (`INCOHERENT_SELECTION` — the daemon's API token belongs to
201
- a state directory, so selecting an endpoint without one would disclose that token).
202
- - `brokerUrl` — for `<=0.3.2`, which hosts its own ADAPT wrapper and meets the daemon at a broker
203
- instead. It must match the daemon's or the two can never see each other.
204
-
205
- A re-run that changes nothing rewrites nothing, and keys the installer does not own are preserved.
206
-
207
- If `ours-mcp install-service` fails (no systemd user bus, no linger, a container, WSL without
208
- systemd) it has already **stopped** the daemon it was about to supervise. The installer restarts it
209
- and says plainly that the boot service is missing — a clean deployment never ends with no daemon
210
- while the summary claims success.
211
-
212
- ## Release channel
213
-
214
- `OURS_CHANNEL=nightly` (or `OURS_INSTALL_CHANNEL`) installs each package's **own** prerelease
215
- dist-tag. The tag is not the same string everywhere, so the mapping is per package:
216
-
217
- | package | stable channel | nightly channel |
218
- |---|---|---|
219
- | `mcp`, `tg-connector`, `claude-code`, `codex`, `hermes` | `latest` | `nightly` |
220
- | `fleet` | `latest` | `nightly` |
221
- | `cowork` (Rooms) | `latest` | `nightly` |
222
-
223
- `fleet` follows the channel: it publishes its own `nightly` dist-tag, and the nightly stack needs
224
- the fleet build carrying the SDK integration. A nightly installer that quietly installed stable
225
- fleet is the same split-brain deployment the channel exists to prevent.
226
-
227
- `cowork` publishes `nightly` alongside every other service (it previously used `next`). The
228
- nightly channel must reach that line, because the external-daemon mode the Rooms step configures
229
- ships there; taking `latest` would pair a config carrying a `daemon` block with a build that
230
- predates it.
231
-
232
- A package with no mapping for the selected channel installs `@latest` rather than a guessed tag,
233
- because a 404 fails the *whole* install — which is also why the nightly installer must not be
234
- published before every package it names actually has the tag it will ask for.
235
-
236
- **With no explicit selection the installer follows its own version.** A published nightly build
237
- carries the `-nightly.N` suffix the release bump stamps, so `npm i -g @ours.network/install@nightly`
238
- builds a nightly stack, and a stable installer can never consume a nightly. This is load-bearing
239
- rather than cosmetic: across `tg-connector` 0.3.2 → 0.3.3-nightly.1 the connector stopped hosting
240
- its own ADAPT wrapper and became a client of the shared daemon, so mixing tags across that boundary
241
- pairs a connector that needs `/api/v1` with a daemon that does not serve it. `OURS_CHANNEL` still
242
- overrides in both directions.
243
-
244
- ## Non-interactive / CI / safe dry-run
26
+ ## Preview and automation
245
27
 
246
28
  ```sh
247
- OURS_ASSUME_YES=1 bash install.sh # accept every default, no prompts
248
- OURS_INSTALL_DRY_RUN=1 bash install.sh # walk the WHOLE flow, install/change NOTHING
29
+ ours-install --dry-run
30
+ OURS_ASSUME_YES=1 ours-install
31
+ ours-install --state-dir /absolute/path --port 3070
249
32
  ```
250
33
 
251
- `OURS_INSTALL_DRY_RUN=1` routes every side-effecting action through a print-only seam it shows
252
- exactly the commands it *would* run (npm installs, `ours-mcp start`, plugin adds, `ours-fleet
253
- init`, service installs) without executing them. That is the safe way to preview the flow on a
254
- machine you don't want to touch, and how the integration tests drive it.
255
-
256
- Non-interactive runs never prompt for or synthesize voice credentials. Supply a complete
257
- `OURS_STT_*` environment configuration yourself, or rerun interactively later; missing setup
258
- is reported and left unchanged.
259
-
260
- | var | meaning |
261
- |---|---|
262
- | `OURS_ASSUME_YES` | accept every default, never prompt (implies no tty needed) |
263
- | `OURS_INSTALL_DRY_RUN` | walk the flow without installing or changing anything |
264
- | `OURS_NPM` | npm binary to use (default `npm`) |
265
- | `OURS_CONFIG` | daemon config file location (default `~/.ours/config.json`) |
266
- | `OURS_STATE_DIR` | daemon state directory (default `~/.ours`) — also what the Telegram connector is told to expect |
267
- | `OURS_TG_CONFIG` | Telegram connector config file location (default `~/.ours-telegram/config.json`) |
268
- | `OURS_COWORK_CONFIG` | Rooms config file location (default `~/.ours-cowork/config.json`) |
269
- | `OURS_COWORK_DAEMON_MODE` / `_ENDPOINT` / `_STATE_DIR` | cowork's own env equivalents of its `daemon` block (read by cowork, not set by the installer) |
270
- | `OURS_CHANNEL` | `nightly` or `latest`; unset follows the installer's own version |
271
-
272
- A non-interactive run takes the shared daemon on its existing default port and installs no
273
- dedicated daemon and no Rooms — the topology is unchanged from before this flow existed. It also
274
- never converts an existing embedded Rooms install to an external daemon.
34
+ Dry-run walks the real plan without writing files, installing packages, starting
35
+ processes, or changing services. Non-interactive runs accept defaults but never
36
+ bypass selection conflicts or destructive safeguards.
275
37
 
276
38
  ## Uninstall
277
39
 
278
- The companion `uninstall.sh` reverses what the installers created — same thin-bootstrap +
279
- Node treatment (banner, colour, a clear explanation of what will be removed). Run it from a
280
- checkout:
281
-
282
- ```sh
283
- bash packages/installer/uninstall.sh
284
- ```
285
-
286
- or over the same raw-URL pattern as `install.sh` (pointing at `uninstall.sh`):
287
-
288
40
  ```sh
289
- curl -fsSL https://raw.githubusercontent.com/adapt-toolkit/ours-mcp/main/packages/installer/uninstall.sh | bash
41
+ ours-uninstall --state-dir "$HOME/.ours"
42
+ ours-uninstall --state-dir "$HOME/.ours" --purge
290
43
  ```
291
44
 
292
- It uses the **same toggle UI** to pick what to remove — per-harness plugins, the ours data
293
- directory (`~/.ours`), and the `ours-mcp` daemon. It removes **only** what the installers
294
- created, and guards the two destructive items — the data directory and the daemon — behind
295
- an explicit typed `yes`.
296
-
297
- On Nightly, uninstall is profile-aware. Removing a harness removes only its one association and
298
- managed plugin artifacts. A daemon/profile cannot be forgotten or removed while any harness,
299
- Telegram, or Rooms still depends on it. A dependent connector can be explicitly detached (stop its
300
- service and remove only its daemon target), uninstalled, or reassigned to a different retained
301
- profile; connector secrets and unrelated settings are preserved. These connector changes roll back
302
- if exact daemon removal or the registry commit fails. Only a profile with `ownership.service: true`
303
- may have its exact named service uninstalled; external profiles are forgotten as metadata only.
304
- Data removal is a separate confirmation that requires typing the exact state path and explicitly
305
- warns about identity/key loss. The global MCP package is retained while any profile/application
306
- still needs it.
307
-
308
- Headless (no terminal), drive it with environment variables:
45
+ The uninstaller delegates service and daemon removal to the `ours` CLI. Identity
46
+ state is retained by default. Purging requires the existing destructive gates and
47
+ targets only the explicit state directory.
309
48
 
310
- ```sh
311
- OURS_UNINSTALL="hermes codex" \
312
- OURS_UNINSTALL_DATA=yes \
313
- OURS_UNINSTALL_DAEMON=yes \
314
- bash uninstall.sh
315
- ```
49
+ ## Release channel
316
50
 
317
- | var | meaning |
318
- |---|---|
319
- | `OURS_UNINSTALL` | harnesses to remove (space/comma list of `claude-code codex hermes`, or `all`) |
320
- | `OURS_UNINSTALL_DATA` | `yes` — remove the ours data directory (`~/.ours`) |
321
- | `OURS_UNINSTALL_DAEMON` | `yes` — remove the `ours-mcp` daemon |
322
- | `OURS_UNINSTALL_PROFILE` | Nightly profile id to forget/remove |
323
- | `OURS_UNINSTALL_FORGET_PROFILE` | Nightly `yes` — forget external/profile metadata only |
324
- | `OURS_UNINSTALL_TELEGRAM` | Nightly dependency action: `detach`, `uninstall`, or `reassign:<profile-id>` |
325
- | `OURS_UNINSTALL_ROOMS` | Nightly dependency action: `detach`, `uninstall`, or `reassign:<profile-id>` |
51
+ `OURS_CHANNEL=nightly` (or `OURS_INSTALL_CHANNEL`) selects the packages' nightly
52
+ dist-tags. Without an override, the installer's own version selects the channel.
326
53
 
327
- ## Notes
54
+ ## Environment
328
55
 
329
- - This package is **not published to npm** (`private: true`); it ships as the hosted
330
- `install.sh` bootstrap plus the `install.mjs` Node installer (and its `lib/`), and exposes the
331
- `ours-install` bin. The pieces it installs — the daemon (`@ours.network/mcp`), the harness
332
- plugins via each marketplace, `@ours.network/fleet`, `@ours.network/tg-connector`, and
333
- `@ours.network/cowork` are the published components.
334
- - **Idempotent + safe to re-run.** A re-run adds a skipped piece, re-points the plugins, or (only
335
- when you say yes) updates a component; an already-current daemon is left untouched, its running
336
- port and complete voice setup are reused everywhere — and the Telegram connector's daemon
337
- selection, a dedicated daemon's config, and the Rooms config are each rewritten only if they
338
- actually changed. Bot tokens and fleet roles remain in the copy-paste hand-off; provider keys
339
- never enter that prompt or agent chat.
56
+ - `OURS_ASSUME_YES=1`: accept defaults without prompting.
57
+ - `OURS_INSTALL_DRY_RUN=1`: preview without mutation.
58
+ - `OURS_NPM`: npm executable.
59
+ - `OURS_CONFIG`: explicit daemon configuration file.
60
+ - `OURS_STATE_DIR`: explicit daemon state directory.
61
+ - `OURS_CHANNEL`: `latest` or `nightly`.