@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/lib/usage.mjs ADDED
@@ -0,0 +1,47 @@
1
+ // ours-install v3 — the help text.
2
+ //
3
+ // It lives here rather than in the bin because `--help` is a behaviour with a
4
+ // contract (the flags it names must be the flags target.mjs accepts), and a bin
5
+ // that is three lines long cannot be the place a contract is asserted.
6
+
7
+ export const USAGE = `ours-install — the unified ours.network stack installer.
8
+
9
+ Install: npm i -g @ours.network/install && ours-install (recommended)
10
+ npx @ours.network/install (one-off)
11
+
12
+ ours-install [--state-dir PATH] [--port N] [--dry-run] [--help] [--version]
13
+
14
+ Guided setup for the whole stack: the ours daemon, its components (the MCP
15
+ server, the Telegram connector, cowork), your harness plugins (Claude Code /
16
+ Codex / Hermes), ours-fleet and voice transcription — then one copy-paste
17
+ hand-off prompt. You approve each step; re-run any time to add a piece or update.
18
+
19
+ --state-dir the daemon's STATE DIRECTORY, which is what identifies a daemon
20
+ (default ~/.ours). A second state directory is a second daemon.
21
+ --port used only when CREATING a daemon. For a daemon that already owns
22
+ the state directory the port comes from its own record, and a
23
+ --port that disagrees with it is refused rather than corrected.
24
+ --dry-run walk the whole flow and print what it WOULD do — change nothing
25
+ --help show this help and exit
26
+ --version print the installer version and exit
27
+
28
+ Env: OURS_ASSUME_YES=1 (accept defaults, no prompts) · OURS_INSTALL_DRY_RUN=1 ·
29
+ OURS_CHANNEL=nightly · OURS_BROKER_URL · OURS_NPM. Docs: https://ours.network`;
30
+
31
+ export const UNINSTALL_USAGE = `ours-uninstall — remove one ours daemon and what attaches to it.
32
+
33
+ ours-uninstall [--state-dir PATH] [--purge] [--dry-run] [--help] [--version]
34
+
35
+ Removes the boot service, stops the daemon, and removes the global packages —
36
+ but ONLY when no other daemon on this machine still needs them. A component
37
+ still pointing at this daemon stops the run before anything is removed, so a
38
+ run that refuses leaves the daemon whole rather than half-dismantled.
39
+
40
+ --state-dir which daemon to remove (default ~/.ours). A daemon IS its state
41
+ directory, so this is the only thing that names one.
42
+ --purge also delete the state directory itself. Never the default, never
43
+ done non-interactively, and it asks you to type the full path —
44
+ identity keys exist nowhere else and no peer can give them back.
45
+ --dry-run print what it WOULD remove and remove nothing
46
+ --help show this help and exit
47
+ --version print the version and exit`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ours.network/install",
3
- "version": "0.16.0",
3
+ "version": "0.17.0-nightly.10",
4
4
  "private": false,
5
5
  "description": "The unified ours.network stack installer (ours-install): one guided ~3-minute flow for ours core (the daemon) + the harness plugins (Claude Code / Codex) + ours-fleet + the Telegram connector, then a single copy-paste hand-off prompt. Self-contained (Node built-ins only); run as `ours-install` or via curl|bash (install.sh).",
6
6
  "type": "module",
package/uninstall.mjs CHANGED
@@ -19,7 +19,9 @@ import { homedir } from 'node:os';
19
19
  import { join } from 'node:path';
20
20
  import { banner, heading, c, openTty, makeWriter, closeSync } from './lib/ui.mjs';
21
21
  import { askLine, checkboxSelect } from './lib/prompt.mjs';
22
- import { canonHarnesses } from './lib/logic.mjs';
22
+ import { canonHarnesses, resolveChannel } from './lib/logic.mjs';
23
+ import { realEffects } from './lib/effects.mjs';
24
+ import { runUninstall as runUninstallV3 } from './lib/orchestrate-uninstall.mjs';
23
25
 
24
26
  const NPM = process.env.OURS_NPM || 'npm';
25
27
  const HOME = homedir();
@@ -28,6 +30,11 @@ const CODEX_DIR = process.env.CODEX_DIR || join(HOME, '.codex');
28
30
  const SKILLS_DIR = process.env.SKILLS_DIR || join(HOME, '.agents', 'skills');
29
31
  const OURS_STATE_DIR = process.env.OURS_STATE_DIR || join(HOME, '.ours');
30
32
  const ASSUME_YES = !!process.env.OURS_ASSUME_YES;
33
+ const packageVersion = (() => {
34
+ try { return JSON.parse(readFileSync(new URL('./package.json', import.meta.url), 'utf8')).version || ''; }
35
+ catch { return ''; }
36
+ })();
37
+ const CHANNEL = resolveChannel(process.env.OURS_CHANNEL || process.env.OURS_INSTALL_CHANNEL, packageVersion);
31
38
 
32
39
  const say = (s) => process.stdout.write(`ours: ${s}\n`);
33
40
  const line = (s = '') => process.stdout.write(`${s}\n`);
@@ -38,7 +45,10 @@ const YAML_END = '# <<< ours.network plugin';
38
45
  const MD_START = '<!-- >>> ours.network plugin (managed block) -->';
39
46
  const MD_END = '<!-- <<< ours.network plugin -->';
40
47
 
41
- const run = (bin, args) => spawnSync(bin, args, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
48
+ const run = (bin, args, { env } = {}) => spawnSync(bin, args, {
49
+ encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'],
50
+ env: env ? { ...process.env, ...env } : process.env,
51
+ });
42
52
 
43
53
  // --- safe removal helpers ----------------------------------------------------------------------
44
54
  function rmDir(d) { if (d && existsSync(d)) { rmSync(d, { recursive: true, force: true }); say(` removed ${d}`); } }
@@ -107,10 +117,26 @@ function confirmDestructive(write, fd, what) {
107
117
  }
108
118
 
109
119
  // ===============================================================================================
110
- function main() {
120
+ async function main() {
111
121
  const ttyFd = openTty();
112
122
  const write = makeWriter(ttyFd);
113
123
 
124
+ // ─── THE CHANNEL FORK ───────────────────────────────────────────────────────
125
+ // The partner to install.mjs's, and it has to move in the SAME commit. v3
126
+ // installing while this body uninstalls would run the old flow against state it
127
+ // no longer understands — the kind of half-state that only surfaces when someone
128
+ // tries to undo something.
129
+ //
130
+ // Before the banner, so the screen the operator sees is the one belonging to the
131
+ // flow that is about to run.
132
+ if (CHANNEL === 'nightly') {
133
+ const code = await runUninstallV3(process.argv.slice(2), realEffects({
134
+ write, ttyFd, env: process.env, version: packageVersion,
135
+ }));
136
+ finish(ttyFd);
137
+ process.exit(code);
138
+ }
139
+
114
140
  line(banner());
115
141
  line(c.bold(' ours.network uninstaller'));
116
142
  say('Removes only what the ours installers created. Nothing is removed until you confirm.');
@@ -195,4 +221,10 @@ function main() {
195
221
 
196
222
  function finish(ttyFd) { if (ttyFd != null) { try { closeSync(ttyFd); } catch { /* ignore */ } } }
197
223
 
198
- main();
224
+ // `main` became async when the nightly fork moved in: the v3 uninstaller is async
225
+ // all the way down. An unhandled rejection here would exit 0 with no output, so the
226
+ // failure is caught and reported like the installer's.
227
+ main().catch((error) => {
228
+ say(`unexpected error: ${String(error)}`);
229
+ process.exitCode = 1;
230
+ });
package/uninstall.sh CHANGED
@@ -10,10 +10,19 @@
10
10
  # explanation of what will be removed. It removes ONLY what the ours installers created; the two
11
11
  # destructive items (data dir, daemon) require an explicit typed 'yes'.
12
12
  #
13
- # Non-interactive env overrides (all optional) — consumed by the Node uninstaller:
14
- # OURS_UNINSTALL="hermes codex" which harness plugins to remove (space/comma; names or "all")
15
- # OURS_UNINSTALL_DATA=yes also remove the ours data dir (~/.ours) [destructive]
16
- # OURS_UNINSTALL_DAEMON=yes also stop + remove the ours-mcp daemon + its service
13
+ # Non-interactive env overrides (all optional) — consumed by the Node uninstaller. Setting ANY of
14
+ # the OURS_UNINSTALL* variables makes the run non-interactive; setting none leaves it unchanged.
15
+ # Four documented variables describe things this uninstaller cannot do, and it REFUSES (exit 2,
16
+ # nothing removed) rather than ignoring them — see the table in README.md. Full list there.
17
+ # OURS_UNINSTALL="hermes codex" which harness plugins to remove (space/comma; names, "all", "none")
18
+ # OURS_UNINSTALL_DAEMON=yes also stop + remove the daemon + its service + the global packages.
19
+ # Unset (or any other value) removes ONLY the plugins named above
20
+ # and leaves the daemon running.
21
+ # OURS_UNINSTALL_TELEGRAM=detach detach the Telegram connector from this daemon (same for
22
+ # OURS_UNINSTALL_ROOMS=detach Rooms). Without it, a run a connector still points at refuses.
23
+ # OURS_UNINSTALL_DATA=yes REFUSED. State is never deleted unattended — it holds identity
24
+ # private keys. Use 'ours-uninstall --state-dir <dir> --purge'
25
+ # from a terminal and type the full path.
17
26
  # OURS_ASSUME_YES=1 accept defaults; skip the typed confirmations (implies no tty)
18
27
  # OURS_NPM="npm" npm binary to use
19
28
  # OURS_UNINSTALLER_MJS / OURS_INSTALLER_BASE run/fetch overrides (dev/testing)