@ours.network/cli 0.4.0 → 0.4.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.
@@ -35,6 +35,25 @@ function bakedStateDir(unit) {
35
35
  }
36
36
  return void 0;
37
37
  }
38
+ function describeConflict(previous, serviceFile, stateDir, verb = "overwrite") {
39
+ if (!previous.startsWith(MARKER)) {
40
+ return {
41
+ kind: "unmanaged",
42
+ serviceFile,
43
+ message: verb === "remove" ? `${serviceFile} is not managed by @ours.network/cli; refusing to remove it` : `${serviceFile} is not managed by @ours.network/cli; refusing to overwrite it without --force`
44
+ };
45
+ }
46
+ const owner = bakedStateDir(previous);
47
+ if (owner !== void 0 && owner !== stateDir) {
48
+ return {
49
+ kind: "other-state-dir",
50
+ serviceFile,
51
+ owner,
52
+ message: `${serviceFile} is the boot service for the daemon at ${owner}, not ${stateDir}; refusing to ${verb} it without --force`
53
+ };
54
+ }
55
+ return void 0;
56
+ }
38
57
  function resolveStateDir(context) {
39
58
  return resolve(context.stateDir ?? join(context.homeDir ?? homedir(), ".ours"));
40
59
  }
@@ -68,12 +87,10 @@ WantedBy=default.target
68
87
  `;
69
88
  if (existsSync(serviceFile)) {
70
89
  const previous = readFileSync(serviceFile, "utf8");
71
- if (!previous.startsWith(MARKER) && !context.force) {
72
- throw new Error(`${serviceFile} is not managed by @ours.network/cli; refusing to overwrite it without --force`);
73
- }
74
- const owner = previous.startsWith(MARKER) ? bakedStateDir(previous) : void 0;
75
- if (owner !== void 0 && owner !== stateDir && !context.force) {
76
- throw new Error(`${serviceFile} is the boot service for the daemon at ${owner}, not ${stateDir}; refusing to overwrite it without --force`);
90
+ const conflict = describeConflict(previous, serviceFile, stateDir);
91
+ if (conflict && !context.force) {
92
+ if (!context.dryRun) throw new Error(conflict.message);
93
+ return { adapter: this.name, action: "install", serviceFile, unitName, stateDir, configPath: unitConfig, commands: [], changed: false, dryRun: true, conflict };
77
94
  }
78
95
  if (previous === unit) return { adapter: this.name, action: "install", serviceFile, unitName, stateDir, configPath: unitConfig, commands: [], changed: false, dryRun: context.dryRun === true };
79
96
  }
@@ -92,10 +109,10 @@ WantedBy=default.target
92
109
  const serviceFile = join(home, ".config", "systemd", "user", unitName);
93
110
  if (!existsSync(serviceFile)) return { adapter: this.name, action: "uninstall", serviceFile, unitName, stateDir, commands: [], changed: false, dryRun: context.dryRun === true };
94
111
  const previous = readFileSync(serviceFile, "utf8");
95
- if (!previous.startsWith(MARKER)) throw new Error(`${serviceFile} is not managed by @ours.network/cli; refusing to remove it`);
96
- const owner = bakedStateDir(previous);
97
- if (owner !== void 0 && owner !== stateDir && !context.force) {
98
- throw new Error(`${serviceFile} is the boot service for the daemon at ${owner}, not ${stateDir}; refusing to remove it without --force`);
112
+ const conflict = describeConflict(previous, serviceFile, stateDir, "remove");
113
+ if (conflict && !(context.force && previous.startsWith(MARKER))) {
114
+ if (!context.dryRun) throw new Error(conflict.message);
115
+ return { adapter: this.name, action: "uninstall", serviceFile, unitName, stateDir, commands: [], changed: false, dryRun: true, conflict };
99
116
  }
100
117
  const commands = [["systemctl", "--user", "disable", "--now", unitName], ["systemctl", "--user", "daemon-reload"]];
101
118
  if (!context.dryRun) {
package/dist/main.js CHANGED
@@ -5,7 +5,7 @@ import {
5
5
  } from "./chunk-YC7QBGVC.js";
6
6
  import {
7
7
  createLinuxUserSystemdAdapter
8
- } from "./chunk-SSUJ5QGC.js";
8
+ } from "./chunk-227DBTDK.js";
9
9
  import {
10
10
  instanceNameFromStateDir
11
11
  } from "./chunk-3RLAHOPS.js";
@@ -55,7 +55,7 @@ import {
55
55
  import { existsSync, readFileSync } from "node:fs";
56
56
  import { homedir } from "node:os";
57
57
  import { join, resolve } from "node:path";
58
- var CLI_VERSION = false ? "0.0.0-dev" : "0.4.0";
58
+ var CLI_VERSION = false ? "0.0.0-dev" : "0.4.1";
59
59
  var COMMON_VALUES = /* @__PURE__ */ new Set(["--endpoint", "--port", "--state-dir", "--config", "--identity"]);
60
60
  var COMMON_BOOLEANS = /* @__PURE__ */ new Set(["--json", "--yes", "--help"]);
61
61
  function selectionFrom(values) {
package/dist/service.d.ts CHANGED
@@ -12,10 +12,24 @@ export interface ServicePlan {
12
12
  stateDir: string;
13
13
  /** The config file the unit's ExecStart selects, or undefined when it selects none. */
14
14
  configPath?: string;
15
+ /**
16
+ * Set ONLY on a --dry-run that found something in the way. A real run throws
17
+ * on the same condition; a preview reports it, because a preview that raises
18
+ * on the one case it exists to preview is not a preview.
19
+ */
20
+ conflict?: ServiceConflict;
15
21
  commands: string[][];
16
22
  changed: boolean;
17
23
  dryRun: boolean;
18
24
  }
25
+ /** Why a run would refuse to touch the unit file that is already there. */
26
+ export interface ServiceConflict {
27
+ kind: 'unmanaged' | 'other-state-dir';
28
+ serviceFile: string;
29
+ /** For 'other-state-dir': the state directory the existing unit belongs to. */
30
+ owner?: string;
31
+ message: string;
32
+ }
19
33
  export interface ServiceContext {
20
34
  cliPath: string;
21
35
  configPath?: string;
package/dist/service.js CHANGED
@@ -2,7 +2,7 @@ import {
2
2
  SERVICE_NAME,
3
3
  bakedStateDir,
4
4
  createLinuxUserSystemdAdapter
5
- } from "./chunk-SSUJ5QGC.js";
5
+ } from "./chunk-227DBTDK.js";
6
6
  import "./chunk-3RLAHOPS.js";
7
7
  export {
8
8
  SERVICE_NAME,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ours.network/cli",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Transport-neutral operator CLI for the ours shared daemon",
5
5
  "type": "module",
6
6
  "license": "FSL-1.1-Apache-2.0",