@ours.network/install 0.16.0 → 0.17.0-nightly.10
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 +233 -32
- package/install.mjs +472 -80
- package/lib/components.mjs +351 -0
- package/lib/detect.mjs +182 -0
- package/lib/effects.mjs +331 -0
- package/lib/extras.mjs +400 -0
- package/lib/journal.mjs +113 -0
- package/lib/logic.mjs +419 -21
- package/lib/nightly-install.mjs +739 -0
- package/lib/nightly-uninstall.mjs +396 -0
- package/lib/orchestrate-uninstall.mjs +374 -0
- package/lib/orchestrate.mjs +1035 -0
- package/lib/plan.mjs +238 -0
- package/lib/profiles.mjs +524 -0
- package/lib/rerun.mjs +119 -0
- package/lib/target.mjs +357 -0
- package/lib/uninstall.mjs +735 -0
- package/lib/usage.mjs +47 -0
- package/package.json +1 -1
- package/uninstall.mjs +36 -4
- package/uninstall.sh +13 -4
package/README.md
CHANGED
|
@@ -43,37 +43,204 @@ dependency on the things it installs): an ASCII banner, tasteful colour (degrade
|
|
|
43
43
|
answers `--version` promptly. A shell alias / hanging wrapper is **never called** (that would
|
|
44
44
|
hang the run) — it's reported plainly with a fix, and a manual-install path is always offered.
|
|
45
45
|
If neither harness exists it says so and exits.
|
|
46
|
-
2. **
|
|
47
|
-
the **broker** (end-to-end encrypted; the broker never sees message content — almost everyone
|
|
48
|
-
just presses Enter) and the **port** (probes `3050`; only asks if it's busy; never hands out
|
|
49
|
-
`3051`, reserved for the Telegram connector). Applied once, then the stack is built with it.
|
|
50
|
-
3. **Four consent gates**, each paced with a clean `✓ … No problems.` line + an explicit
|
|
46
|
+
2. **Five consent gates**, each paced with a clean `✓ … No problems.` line + an explicit
|
|
51
47
|
**Continue?** — never a start-twice-then-ask, never a silent failure:
|
|
52
|
-
- **1/
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
`
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
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**
|
|
61
64
|
(`claude plugin marketplace add …` + `claude plugin install ours@ours.network`;
|
|
62
65
|
`codex plugin marketplace add …` + `codex plugin add ours@ours-codex-marketplace`). Choosing
|
|
63
66
|
Codex also installs the `ours-codex` live launcher in the same step. Any failure / alias
|
|
64
67
|
prints the exact manual commands and continues — it **never dead-ends**.
|
|
65
|
-
- **3/
|
|
68
|
+
- **3/5 ours-fleet** — makes your harnesses persistent, always-online agent teams that survive
|
|
66
69
|
a reboot; runs `ours-fleet init`. Default **Yes**.
|
|
67
|
-
- **4/
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
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.
|
|
72
85
|
|
|
73
86
|
The human identity is created idempotently after the daemon becomes reachable. Because
|
|
74
87
|
`curl … | bash` gives the script its input over the pipe, every prompt is read from the
|
|
75
88
|
controlling terminal (`/dev/tty`), so the flow still works piped.
|
|
76
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
|
+
|
|
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` |
|
|
155
|
+
|
|
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`.
|
|
160
|
+
|
|
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.
|
|
164
|
+
|
|
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.
|
|
169
|
+
|
|
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
|
+
|
|
77
244
|
## Non-interactive / CI / safe dry-run
|
|
78
245
|
|
|
79
246
|
```sh
|
|
@@ -96,6 +263,15 @@ is reported and left unchanged.
|
|
|
96
263
|
| `OURS_INSTALL_DRY_RUN` | walk the flow without installing or changing anything |
|
|
97
264
|
| `OURS_NPM` | npm binary to use (default `npm`) |
|
|
98
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.
|
|
99
275
|
|
|
100
276
|
## Uninstall
|
|
101
277
|
|
|
@@ -113,34 +289,59 @@ or over the same raw-URL pattern as `install.sh` (pointing at `uninstall.sh`):
|
|
|
113
289
|
curl -fsSL https://raw.githubusercontent.com/adapt-toolkit/ours-mcp/main/packages/installer/uninstall.sh | bash
|
|
114
290
|
```
|
|
115
291
|
|
|
116
|
-
It
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
292
|
+
It removes **only** what the installers created — the sentinel-delimited managed blocks, the
|
|
293
|
+
`ours` skill directories, the boot service, and the global packages — and it asks per harness
|
|
294
|
+
which plugins to detach. The one irreversible step, deleting the state directory, is behind
|
|
295
|
+
`--purge` **and** typing the directory's full path.
|
|
296
|
+
|
|
297
|
+
Which daemon is removed is chosen by its **state directory**, not by a profile id:
|
|
298
|
+
`ours-uninstall --state-dir ~/.ours-blue`. The uninstall refuses before it touches anything if
|
|
299
|
+
the Telegram connector or Rooms still points at that daemon (and refuses too if either config
|
|
300
|
+
file cannot be parsed, since a file we cannot read cannot be shown *not* to point here); a
|
|
301
|
+
dependent connector can be detached in the same run, which stops its service and removes only
|
|
302
|
+
its daemon keys, keeping the file with its bot token and settings. Detaching Rooms returns it
|
|
303
|
+
to embedded mode, and the run says so before it does it. Data removal (`--purge`) is separate,
|
|
304
|
+
requires typing the exact state directory path, warns about identity/key loss, and never happens
|
|
305
|
+
in a run with nobody watching. The global `@ours.network/cli` and `@ours.network/mcp` packages
|
|
306
|
+
are kept while any other daemon on the machine still needs them, and a harness that keeps its
|
|
307
|
+
plugin keeps its launcher package.
|
|
120
308
|
|
|
121
309
|
Headless (no terminal), drive it with environment variables:
|
|
122
310
|
|
|
123
311
|
```sh
|
|
124
312
|
OURS_UNINSTALL="hermes codex" \
|
|
125
|
-
OURS_UNINSTALL_DATA=yes \
|
|
126
313
|
OURS_UNINSTALL_DAEMON=yes \
|
|
127
314
|
bash uninstall.sh
|
|
128
315
|
```
|
|
129
316
|
|
|
317
|
+
Setting **any** of the seven variables below puts the run under this contract: nothing is
|
|
318
|
+
prompted, and `OURS_UNINSTALL_DAEMON` alone decides whether the daemon goes. Setting **none** of
|
|
319
|
+
them leaves the run exactly as it is interactively. Four of the seven describe things this
|
|
320
|
+
uninstaller cannot do; it **refuses with exit 2 and removes nothing** rather than ignoring them,
|
|
321
|
+
because a variable that silently does nothing reads as success to the script that set it.
|
|
322
|
+
|
|
130
323
|
| var | meaning |
|
|
131
324
|
|---|---|
|
|
132
|
-
| `OURS_UNINSTALL` |
|
|
133
|
-
| `
|
|
134
|
-
| `
|
|
325
|
+
| `OURS_UNINSTALL` | harness plugins to remove — space/comma list of `claude-code codex hermes`, or `all`, or `none`. Names not on that list are refused. |
|
|
326
|
+
| `OURS_UNINSTALL_DAEMON` | `yes` — also remove the daemon (its boot service, its process, and the global packages). Anything else, or unset, removes **only** the harness plugins named above and leaves the daemon running. |
|
|
327
|
+
| `OURS_UNINSTALL_TELEGRAM` | `detach` — detach the Telegram connector from this daemon. This is the confirmation an unattended run cannot otherwise be asked for; without it a run against a daemon Telegram points at refuses. |
|
|
328
|
+
| `OURS_UNINSTALL_ROOMS` | `detach` — the same for Rooms. |
|
|
329
|
+
| `OURS_UNINSTALL_DATA` | **Refused at `yes`.** State is never deleted with nobody present: it holds identity private keys that exist nowhere else. Run `ours-uninstall --state-dir <dir> --purge` from a terminal and type the full path. `no` is the default and is accepted. |
|
|
330
|
+
| `OURS_UNINSTALL_PROFILE` | **Refused.** There is no profile registry; select the daemon with `--state-dir <path>`. |
|
|
331
|
+
| `OURS_UNINSTALL_FORGET_PROFILE` | **Refused.** There is no daemon metadata to forget — a daemon this uninstaller is not pointed at is already left entirely alone. |
|
|
332
|
+
| `OURS_UNINSTALL_TELEGRAM` / `_ROOMS` at `uninstall` | **Refused.** This detaches a connector, it does not uninstall one. Use `detach`, then `npm rm -g @ours.network/tg-connector` (or `@ours.network/cowork`). |
|
|
333
|
+
| `OURS_UNINSTALL_TELEGRAM` / `_ROOMS` at `reassign:<id>` | **Refused.** There is no profile id to reassign to. Point the connector at the daemon you want with `ours-install --state-dir <path>`, then re-run the uninstall. |
|
|
135
334
|
|
|
136
335
|
## Notes
|
|
137
336
|
|
|
138
337
|
- This package is **not published to npm** (`private: true`); it ships as the hosted
|
|
139
338
|
`install.sh` bootstrap plus the `install.mjs` Node installer (and its `lib/`), and exposes the
|
|
140
339
|
`ours-install` bin. The pieces it installs — the daemon (`@ours.network/mcp`), the harness
|
|
141
|
-
plugins via each marketplace, `@ours.network/fleet`,
|
|
142
|
-
published components.
|
|
340
|
+
plugins via each marketplace, `@ours.network/fleet`, `@ours.network/tg-connector`, and
|
|
341
|
+
`@ours.network/cowork` — are the published components.
|
|
143
342
|
- **Idempotent + safe to re-run.** A re-run adds a skipped piece, re-points the plugins, or (only
|
|
144
343
|
when you say yes) updates a component; an already-current daemon is left untouched, its running
|
|
145
|
-
port and complete voice setup are reused everywhere
|
|
146
|
-
|
|
344
|
+
port and complete voice setup are reused everywhere — and the Telegram connector's daemon
|
|
345
|
+
selection, a dedicated daemon's config, and the Rooms config are each rewritten only if they
|
|
346
|
+
actually changed. Bot tokens and fleet roles remain in the copy-paste hand-off; provider keys
|
|
347
|
+
never enter that prompt or agent chat.
|