@ours.network/install 0.17.0-nightly.3 → 0.17.0-nightly.4

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
@@ -87,6 +87,56 @@ The human identity is created idempotently after the daemon becomes reachable. B
87
87
  `curl … | bash` gives the script its input over the pipe, every prompt is read from the
88
88
  controlling terminal (`/dev/tty`), so the flow still works piped.
89
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
+ }
122
+ ```
123
+
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
+
90
140
  ## Daemon topology
91
141
 
92
142
  A clean deployment installs, configures and starts **one shared** ours daemon first, then wires
@@ -244,6 +294,17 @@ directory (`~/.ours`), and the `ours-mcp` daemon. It removes **only** what the i
244
294
  created, and guards the two destructive items — the data directory and the daemon — behind
245
295
  an explicit typed `yes`.
246
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
+
247
308
  Headless (no terminal), drive it with environment variables:
248
309
 
249
310
  ```sh
@@ -258,6 +319,10 @@ OURS_UNINSTALL_DAEMON=yes \
258
319
  | `OURS_UNINSTALL` | harnesses to remove (space/comma list of `claude-code codex hermes`, or `all`) |
259
320
  | `OURS_UNINSTALL_DATA` | `yes` — remove the ours data directory (`~/.ours`) |
260
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>` |
261
326
 
262
327
  ## Notes
263
328
 
package/install.mjs CHANGED
@@ -40,6 +40,7 @@ import {
40
40
  coworkSupportsExternalDaemon, COWORK_EXTERNAL_MIN_VERSION,
41
41
  } from './lib/logic.mjs';
42
42
  import { atomicWriteConfig } from './lib/config.mjs';
43
+ import { runNightlyInstaller } from './lib/nightly-install.mjs';
43
44
 
44
45
  const NPM = process.env.OURS_NPM || 'npm';
45
46
  // Release channel: OURS_CHANNEL=nightly installs each package's PRERELEASE dist-tag —
@@ -306,6 +307,16 @@ async function main() {
306
307
  finish(ttyFd); return;
307
308
  }
308
309
 
310
+ // HARD RELEASE BOUNDARY. Nightly owns the topology-first profile flow. The
311
+ // latest/stable consumer-first implementation below remains untouched and
312
+ // never reads or writes installer-profiles.json or emits --application.
313
+ if (CHANNEL === 'nightly') {
314
+ return runNightlyInstaller({
315
+ harnesses, ttyFd, interactive, write, yes, ask, cont, dry: DRY,
316
+ npm: NPM, run, runAsync, act, actSpin, finish,
317
+ });
318
+ }
319
+
309
320
  // Daemon state up front (decides first-install vs update, and whether Step 0 runs at all).
310
321
  const versionBefore = daemonVersionLine();
311
322
  const daemonInstalled = !!versionBefore;