@ours.network/cli 2.0.2 → 2.0.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 +8 -0
- package/dist/{boot-Y3GXCN6X.js → boot-HNJ7ZR5L.js} +1 -1
- package/dist/{chunk-DTN7MN44.js → chunk-DZH2YEXT.js} +14 -6
- package/dist/{chunk-VFAC74XR.js → chunk-NNHFXFLS.js} +2 -1
- package/dist/{chunk-VA6UQKYN.js → chunk-QKDH6CJR.js} +2 -2
- package/dist/{chunk-IL724CY2.js → chunk-TSYICL3I.js} +141 -32
- package/dist/commands.d.ts +2 -2
- package/dist/commands.js +1 -1
- package/dist/{daemon-A2L6UMGZ.js → daemon-LTJHJDWZ.js} +2 -2
- package/dist/help.js +2 -2
- package/dist/lifecycle.d.ts +2 -1
- package/dist/lifecycle.js +3 -1
- package/dist/main.d.ts +2 -1
- package/dist/main.js +22 -13
- package/dist/mufl_code/351F16D444E6DAA252D4A8D829D17204360D9015FEF670ACE68FB4747BB3B14E.muflo +0 -0
- package/dist/{server-6BZCHN6Q.js → server-35QOYLY4.js} +302 -156
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -61,6 +61,14 @@ ours daemon stop
|
|
|
61
61
|
ours daemon restart
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
+
`start` and `restart` wait up to 20 minutes for the new daemon to become ready.
|
|
65
|
+
Override that wait with `--readiness-timeout-ms N` or
|
|
66
|
+
`OURS_DAEMON_READINESS_TIMEOUT_MS`. The flag takes precedence. If the deadline
|
|
67
|
+
is reached, the command exits with an error but never signals or stops the
|
|
68
|
+
spawned daemon: it remains running and may still be completing an upgrade. Use
|
|
69
|
+
`ours daemon status --json` to check it and inspect
|
|
70
|
+
`ours-cli-daemon.log` in the selected state directory for progress.
|
|
71
|
+
|
|
64
72
|
`stop` and `restart` control the selected daemon regardless of which CLI command
|
|
65
73
|
or service launcher originally started it. The endpoint must identify itself as
|
|
66
74
|
an ours daemon for the exact selected state directory; once that validation
|
|
@@ -11,6 +11,7 @@ import { join, resolve } from "node:path";
|
|
|
11
11
|
var MANAGED_PID_FILE = "ours-cli-daemon.json";
|
|
12
12
|
var LEGACY_MANAGED_PID_FILE = "daemon.pid";
|
|
13
13
|
var DAEMON_LOG_FILE = "ours-cli-daemon.log";
|
|
14
|
+
var DEFAULT_DAEMON_READINESS_TIMEOUT_MS = 20 * 60 * 1e3;
|
|
14
15
|
var depsDefault = {
|
|
15
16
|
fetch: globalThis.fetch,
|
|
16
17
|
spawn: nodeSpawn,
|
|
@@ -98,7 +99,7 @@ function childEnvironment(selection) {
|
|
|
98
99
|
async function serveDaemon(selection, managed = false) {
|
|
99
100
|
if (selection.endpoint !== void 0) throw new Error("--endpoint selects a running daemon and cannot be used with daemon serve");
|
|
100
101
|
Object.assign(process.env, childEnvironment(selection));
|
|
101
|
-
const { startDaemon } = await import("./daemon-
|
|
102
|
+
const { startDaemon } = await import("./daemon-LTJHJDWZ.js");
|
|
102
103
|
const handle = await startDaemon();
|
|
103
104
|
if (managed) {
|
|
104
105
|
const response = await fetch(`http://127.0.0.1:${handle.port}/state-dir`);
|
|
@@ -111,8 +112,11 @@ async function serveDaemon(selection, managed = false) {
|
|
|
111
112
|
}
|
|
112
113
|
return await new Promise(() => void 0);
|
|
113
114
|
}
|
|
114
|
-
async function startDaemonManaged(selection, deps = depsDefault) {
|
|
115
|
+
async function startDaemonManaged(selection, deps = depsDefault, readinessTimeoutMs = DEFAULT_DAEMON_READINESS_TIMEOUT_MS) {
|
|
115
116
|
if (selection.endpoint !== void 0) throw new Error("--endpoint selects an external daemon and cannot be used with daemon start");
|
|
117
|
+
if (!Number.isSafeInteger(readinessTimeoutMs) || readinessTimeoutMs < 1) {
|
|
118
|
+
throw new Error("daemon readiness timeout must be a positive integer number of milliseconds");
|
|
119
|
+
}
|
|
116
120
|
const config = await resolveSelection(selection);
|
|
117
121
|
const before = await inspectDaemon(config, deps);
|
|
118
122
|
if (before.state === "running") return before;
|
|
@@ -132,9 +136,9 @@ async function startDaemonManaged(selection, deps = depsDefault) {
|
|
|
132
136
|
}
|
|
133
137
|
if (!child.pid || child.pid <= 1) throw new Error(`daemon start failed; see ${logPath}`);
|
|
134
138
|
child.unref();
|
|
135
|
-
const deadline = deps.now() +
|
|
139
|
+
const deadline = deps.now() + readinessTimeoutMs;
|
|
136
140
|
while (deps.now() < deadline) {
|
|
137
|
-
await deps.delay(100);
|
|
141
|
+
await deps.delay(Math.min(100, deadline - deps.now()));
|
|
138
142
|
const status = await inspectDaemon(config, deps);
|
|
139
143
|
if (status.state === "running" && status.info?.pid === child.pid) return status;
|
|
140
144
|
try {
|
|
@@ -144,10 +148,13 @@ async function startDaemonManaged(selection, deps = depsDefault) {
|
|
|
144
148
|
}
|
|
145
149
|
}
|
|
146
150
|
try {
|
|
147
|
-
deps.kill(child.pid,
|
|
151
|
+
deps.kill(child.pid, 0);
|
|
148
152
|
} catch {
|
|
153
|
+
throw new Error(`daemon exited before becoming ready; see ${logPath}`);
|
|
149
154
|
}
|
|
150
|
-
throw new Error(
|
|
155
|
+
throw new Error(
|
|
156
|
+
`daemon is still running but did not become ready within ${readinessTimeoutMs} ms; it may still be starting. Check \`ours daemon status --json\` and ${logPath}`
|
|
157
|
+
);
|
|
151
158
|
}
|
|
152
159
|
function removeOwnedPidFile(status) {
|
|
153
160
|
const record = readManagedRecord(status.stateDir);
|
|
@@ -188,6 +195,7 @@ export {
|
|
|
188
195
|
MANAGED_PID_FILE,
|
|
189
196
|
LEGACY_MANAGED_PID_FILE,
|
|
190
197
|
DAEMON_LOG_FILE,
|
|
198
|
+
DEFAULT_DAEMON_READINESS_TIMEOUT_MS,
|
|
191
199
|
readLegacyManagedPid,
|
|
192
200
|
readManagedRecord,
|
|
193
201
|
writeManagedRecord,
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
IDENTITY_PREBIND_EXCEPTIONS,
|
|
14
14
|
commandSpec,
|
|
15
15
|
isCommandGroup
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-QKDH6CJR.js";
|
|
17
17
|
|
|
18
18
|
// src/help.ts
|
|
19
19
|
var pad = (value, width = 32) => value.length >= width ? `${value} ` : value.padEnd(width);
|
|
@@ -106,6 +106,7 @@ function nativeDaemonOptions(action) {
|
|
|
106
106
|
["--port N", selected ? "Select daemon port 1\u201365535." : "Checked against the port recorded for this state directory; a disagreement is refused and nothing is written."],
|
|
107
107
|
["--state-dir PATH", selected ? "Select the daemon state directory." : "Select the daemon whose unit this is; the unit name derives from it (~/.ours \u2192 ours.service, ~/.ours-tg \u2192 ours-tg.service)."],
|
|
108
108
|
["--config PATH", selected ? "Use this daemon config for lifecycle selection." : action === "install-service" ? "Embed this config file\u2019s absolute path in the installed unit." : "Compatibility-only/ignored by uninstall-service."],
|
|
109
|
+
["--readiness-timeout-ms N", action === "start" || action === "restart" ? "Wait this many milliseconds for readiness (default: 1200000; env: OURS_DAEMON_READINESS_TIMEOUT_MS). A timeout leaves the spawned daemon running." : "Compatibility-only/ignored for this action."],
|
|
109
110
|
["--identity NAME", "Compatibility-only/ignored by daemon lifecycle and service commands."],
|
|
110
111
|
["--json", action === "serve" ? "Compatibility-only/ignored because serve runs until interrupted." : "Write one JSON result to stdout."],
|
|
111
112
|
["--yes", service.has(action) ? "Confirm the service-manager change; not needed with --dry-run." : "Compatibility-only/ignored for this action."],
|
|
@@ -15,7 +15,7 @@ var COMMAND_GROUPS = {
|
|
|
15
15
|
kind: "daemon",
|
|
16
16
|
handler: "start",
|
|
17
17
|
summary: "Start a detached daemon managed by this CLI.",
|
|
18
|
-
effects: "Spawns a background process, writes a PID record and log in the selected state directory, and waits up to 20
|
|
18
|
+
effects: "Spawns a background process, writes a PID record and log in the selected state directory, and waits up to 20 minutes for readiness by default. Reaching the readiness deadline never stops the daemon.",
|
|
19
19
|
example: "ours daemon start --config /etc/ours/config.json"
|
|
20
20
|
},
|
|
21
21
|
stop: {
|
|
@@ -29,7 +29,7 @@ var COMMAND_GROUPS = {
|
|
|
29
29
|
kind: "daemon",
|
|
30
30
|
handler: "restart",
|
|
31
31
|
summary: "Stop and start the selected ours daemon.",
|
|
32
|
-
effects: "Stops the selected valid ours daemon, then starts a detached replacement.",
|
|
32
|
+
effects: "Stops the selected valid ours daemon, then starts a detached replacement and waits up to 20 minutes for readiness by default. Reaching the readiness deadline never stops the replacement.",
|
|
33
33
|
example: "ours daemon restart --config /etc/ours/config.json"
|
|
34
34
|
},
|
|
35
35
|
status: {
|
|
@@ -89,7 +89,7 @@ function createStartupProgressReporter(stateDir, opts = {}) {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
// ../../src/runtime/env.ts
|
|
92
|
-
var VERSION = true ? "3.0.
|
|
92
|
+
var VERSION = true ? "3.0.4" : "0.0.0-dev";
|
|
93
93
|
var CONFIG = loadConfig();
|
|
94
94
|
var STATE_DIR = CONFIG.stateDir;
|
|
95
95
|
var BROKER_URL = CONFIG.brokerUrl;
|
|
@@ -802,6 +802,7 @@ function setWrapper(w) {
|
|
|
802
802
|
wrapper = w;
|
|
803
803
|
}
|
|
804
804
|
var identities = /* @__PURE__ */ new Map();
|
|
805
|
+
var quarantinedIdentities = /* @__PURE__ */ new Map();
|
|
805
806
|
var registrar = null;
|
|
806
807
|
var registrarAdBlob = null;
|
|
807
808
|
function setRegistrar(id) {
|
|
@@ -894,13 +895,13 @@ var leases = /* @__PURE__ */ new Map();
|
|
|
894
895
|
var tombstones = /* @__PURE__ */ new Set();
|
|
895
896
|
var sessionHeaders = /* @__PURE__ */ new Map();
|
|
896
897
|
var outboundRemovalInFlight = /* @__PURE__ */ new Set();
|
|
897
|
-
function
|
|
898
|
-
if (!Number.isInteger(pid) || pid <=
|
|
898
|
+
function pidDefinitelyDead(pid) {
|
|
899
|
+
if (!Number.isInteger(pid) || pid <= 1) return false;
|
|
899
900
|
try {
|
|
900
901
|
process.kill(pid, 0);
|
|
901
|
-
return
|
|
902
|
+
return false;
|
|
902
903
|
} catch (err) {
|
|
903
|
-
return err.code === "
|
|
904
|
+
return err.code === "ESRCH";
|
|
904
905
|
}
|
|
905
906
|
}
|
|
906
907
|
function leaseByToken(token) {
|
|
@@ -1902,6 +1903,85 @@ async function pinRegistrar(id) {
|
|
|
1902
1903
|
});
|
|
1903
1904
|
}
|
|
1904
1905
|
|
|
1906
|
+
// ../../src/identity/migration.ts
|
|
1907
|
+
function wasPublished(id) {
|
|
1908
|
+
return Object.values(readBook()).some((entry) => entry.container_id === id.cid);
|
|
1909
|
+
}
|
|
1910
|
+
async function finishRestoredActivation(id) {
|
|
1911
|
+
wrapper.expose_packet(id.cid);
|
|
1912
|
+
identities.set(id.name, id);
|
|
1913
|
+
quarantinedIdentities.delete(id.name);
|
|
1914
|
+
log(`[${id.name}] EXPOSED (routing + broker registration) \u2014 hierarchy reconciled after restore`);
|
|
1915
|
+
try {
|
|
1916
|
+
await contactRestoreSweep(id);
|
|
1917
|
+
} catch (err) {
|
|
1918
|
+
log(`[${id.name}] post-activation contact restore sweep failed:`, String(err));
|
|
1919
|
+
}
|
|
1920
|
+
try {
|
|
1921
|
+
refreshUnread(id);
|
|
1922
|
+
} catch (err) {
|
|
1923
|
+
log(`[${id.name}] post-activation unread refresh failed:`, String(err));
|
|
1924
|
+
}
|
|
1925
|
+
return id;
|
|
1926
|
+
}
|
|
1927
|
+
function quarantine(id, status) {
|
|
1928
|
+
const exposeLocal = wasPublished(id);
|
|
1929
|
+
if (exposeLocal) unpublishFromBook(id);
|
|
1930
|
+
identities.delete(id.name);
|
|
1931
|
+
quarantinedIdentities.set(id.name, { identity: id, status, exposeLocal });
|
|
1932
|
+
log(`[${id.name}] QUARANTINED (${status}) \u2014 management-only, unexposed and unbindable`);
|
|
1933
|
+
return id;
|
|
1934
|
+
}
|
|
1935
|
+
async function reconcileRestoredIdentity(id) {
|
|
1936
|
+
if (rootName === id.name) return finishRestoredActivation(id);
|
|
1937
|
+
const hostRoot = rootName ? identities.get(rootName) : void 0;
|
|
1938
|
+
let info;
|
|
1939
|
+
try {
|
|
1940
|
+
info = describeIdentity(id);
|
|
1941
|
+
} catch (err) {
|
|
1942
|
+
log(`[${id.name}] hierarchy classification failed:`, String(err));
|
|
1943
|
+
return quarantine(id, "migration-failed");
|
|
1944
|
+
}
|
|
1945
|
+
if (hostRoot) {
|
|
1946
|
+
if (info.hasCert && info.roleId !== "" && info.rootCid !== hostRoot.cid) {
|
|
1947
|
+
log(`[${id.name}] preserved imported delegation from root ${info.rootCid.slice(0, 12)}\u2026`);
|
|
1948
|
+
return finishRestoredActivation(id);
|
|
1949
|
+
}
|
|
1950
|
+
try {
|
|
1951
|
+
await delegateRole(hostRoot, id);
|
|
1952
|
+
return finishRestoredActivation(id);
|
|
1953
|
+
} catch (err) {
|
|
1954
|
+
log(`[${id.name}] root delegation during restore failed:`, String(err));
|
|
1955
|
+
return quarantine(id, "migration-failed");
|
|
1956
|
+
}
|
|
1957
|
+
}
|
|
1958
|
+
if (info.hasCert && info.roleId !== "") return finishRestoredActivation(id);
|
|
1959
|
+
return quarantine(id, "awaiting-root");
|
|
1960
|
+
}
|
|
1961
|
+
async function adoptQuarantinedIdentities(root) {
|
|
1962
|
+
const adopted = [];
|
|
1963
|
+
const failed = [];
|
|
1964
|
+
for (const [name, held] of [...quarantinedIdentities]) {
|
|
1965
|
+
try {
|
|
1966
|
+
await delegateRole(root, held.identity);
|
|
1967
|
+
if (held.exposeLocal) await publishToBook(held.identity);
|
|
1968
|
+
await finishRestoredActivation(held.identity);
|
|
1969
|
+
quarantinedIdentities.delete(name);
|
|
1970
|
+
adopted.push(name);
|
|
1971
|
+
} catch (err) {
|
|
1972
|
+
try {
|
|
1973
|
+
unpublishFromBook(held.identity);
|
|
1974
|
+
} catch {
|
|
1975
|
+
}
|
|
1976
|
+
held.status = "migration-failed";
|
|
1977
|
+
quarantinedIdentities.set(name, held);
|
|
1978
|
+
log(`[${name}] quarantine adoption failed under root "${root.name}":`, String(err));
|
|
1979
|
+
failed.push(name);
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
return { adopted, failed };
|
|
1983
|
+
}
|
|
1984
|
+
|
|
1905
1985
|
// ../../src/identity/provision.ts
|
|
1906
1986
|
function createPacket(name, seed, dir, track = true, signingSecret, deferExposure = false) {
|
|
1907
1987
|
const config = new PacketWrapperConfigurator();
|
|
@@ -2086,12 +2166,9 @@ async function restoreIdentity(name) {
|
|
|
2086
2166
|
} catch (err) {
|
|
2087
2167
|
tearDownUnexposed("history_open", "FAILED (SQLite history unavailable)", err);
|
|
2088
2168
|
}
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
await contactRestoreSweep(id);
|
|
2093
|
-
refreshUnread(id);
|
|
2094
|
-
return id;
|
|
2169
|
+
const restoredTemp = readTempMetaFile(id.dir);
|
|
2170
|
+
if (restoredTemp) id.temp = restoredTemp;
|
|
2171
|
+
return reconcileRestoredIdentity(id);
|
|
2095
2172
|
}
|
|
2096
2173
|
|
|
2097
2174
|
// ../../src/state.ts
|
|
@@ -2345,8 +2422,11 @@ async function establishRoot(id) {
|
|
|
2345
2422
|
return { adopted, failed };
|
|
2346
2423
|
}
|
|
2347
2424
|
|
|
2348
|
-
// ../../src/identity/
|
|
2425
|
+
// ../../src/identity/reaper.ts
|
|
2349
2426
|
import { join as join9 } from "node:path";
|
|
2427
|
+
import * as fs12 from "node:fs";
|
|
2428
|
+
|
|
2429
|
+
// ../../src/identity/lifecycle.ts
|
|
2350
2430
|
import * as fs11 from "node:fs";
|
|
2351
2431
|
|
|
2352
2432
|
// ../../src/render/adapt-to-json.ts
|
|
@@ -2724,31 +2804,46 @@ function closeTemporaryIdentity(id, cause) {
|
|
|
2724
2804
|
})();
|
|
2725
2805
|
return t.closing;
|
|
2726
2806
|
}
|
|
2727
|
-
|
|
2728
|
-
|
|
2807
|
+
|
|
2808
|
+
// ../../src/identity/reaper.ts
|
|
2809
|
+
function sessionReaperIntervalMs(raw = process.env.OURS_SESSION_REAPER_INTERVAL_MS) {
|
|
2810
|
+
if (raw === void 0 || raw.trim() === "") return 5e3;
|
|
2811
|
+
const parsed = Number(raw);
|
|
2812
|
+
if (!Number.isFinite(parsed)) return 5e3;
|
|
2813
|
+
return Math.max(100, Math.trunc(parsed));
|
|
2814
|
+
}
|
|
2815
|
+
async function reapStaleTemporaryIdentities() {
|
|
2816
|
+
const all = [
|
|
2817
|
+
...identities.values(),
|
|
2818
|
+
...[...quarantinedIdentities.values()].map((held) => held.identity)
|
|
2819
|
+
];
|
|
2820
|
+
const closes = [];
|
|
2821
|
+
for (const id of all) {
|
|
2729
2822
|
const t = id.temp;
|
|
2730
|
-
if (!t || t.closing) continue;
|
|
2731
|
-
if (pidAlive(t.owner.pid)) continue;
|
|
2823
|
+
if (!t || t.closing || !pidDefinitelyDead(t.owner.pid)) continue;
|
|
2732
2824
|
const lease = leases.get(id.name);
|
|
2733
|
-
if (lease &&
|
|
2734
|
-
|
|
2735
|
-
(
|
|
2825
|
+
if (lease && !pidDefinitelyDead(lease.pid)) continue;
|
|
2826
|
+
closes.push(
|
|
2827
|
+
closeTemporaryIdentity(id, `stale lease \u2014 owner pid ${t.owner.pid} is dead`).then(() => {
|
|
2828
|
+
quarantinedIdentities.delete(id.name);
|
|
2829
|
+
}).catch((err) => log(`[${id.name}] stale-temp reclaim failed:`, String(err)))
|
|
2736
2830
|
);
|
|
2737
2831
|
}
|
|
2832
|
+
await Promise.all(closes);
|
|
2738
2833
|
}
|
|
2739
|
-
function
|
|
2740
|
-
if (!
|
|
2741
|
-
for (const d of
|
|
2742
|
-
if (!d.isDirectory() || identities.has(d.name)) continue;
|
|
2834
|
+
async function reapOrphanTemporaryDirectories() {
|
|
2835
|
+
if (!fs12.existsSync(STATE_DIR)) return;
|
|
2836
|
+
for (const d of fs12.readdirSync(STATE_DIR, { withFileTypes: true })) {
|
|
2837
|
+
if (!d.isDirectory() || identities.has(d.name) || quarantinedIdentities.has(d.name)) continue;
|
|
2743
2838
|
const dir = join9(STATE_DIR, d.name);
|
|
2744
2839
|
const meta = readTempMetaFile(dir);
|
|
2745
|
-
if (!meta ||
|
|
2840
|
+
if (!meta || !pidDefinitelyDead(meta.owner.pid)) continue;
|
|
2746
2841
|
try {
|
|
2747
|
-
|
|
2842
|
+
fs12.rmSync(dir, { recursive: true, force: true });
|
|
2748
2843
|
reservedNames.delete(d.name);
|
|
2749
|
-
log(`[${d.name}] removed orphaned temporary-identity dir (owner pid ${meta.owner.pid} dead
|
|
2844
|
+
log(`[${d.name}] removed orphaned temporary-identity dir (owner pid ${meta.owner.pid} confirmed dead)`);
|
|
2750
2845
|
} catch (err) {
|
|
2751
|
-
|
|
2846
|
+
throw new Error(`[${d.name}] failed to remove orphaned temporary-identity dir: ${String(err)}`);
|
|
2752
2847
|
}
|
|
2753
2848
|
}
|
|
2754
2849
|
}
|
|
@@ -2859,7 +2954,9 @@ async function bootWrapper() {
|
|
|
2859
2954
|
log("failed to start the contact-book registrar (local contact book disabled):", String(err));
|
|
2860
2955
|
}
|
|
2861
2956
|
tightenIdentityPerms();
|
|
2862
|
-
|
|
2957
|
+
setRootName(readRootMarker());
|
|
2958
|
+
const persistedNames = listPersistedNames();
|
|
2959
|
+
const names = rootName && persistedNames.includes(rootName) ? [rootName, ...persistedNames.filter((name) => name !== rootName)] : persistedNames;
|
|
2863
2960
|
const fakeRestoreCount = Math.max(0, Number(process.env.OURS_TEST_FAKE_RESTORE_COUNT || "") || 0);
|
|
2864
2961
|
const fakeRestoreMs = Math.max(0, Number(process.env.OURS_TEST_FAKE_RESTORE_MS || "") || 0);
|
|
2865
2962
|
const restoreTotal = names.length === 0 && fakeRestoreCount > 0 ? fakeRestoreCount : names.length;
|
|
@@ -2945,8 +3042,8 @@ async function bootWrapper() {
|
|
|
2945
3042
|
if (refreshedRoles.length > 0) log(`refreshed ${refreshedRoles.length} role delegation cert(s) against the live AD on boot`);
|
|
2946
3043
|
}
|
|
2947
3044
|
persistBindings();
|
|
2948
|
-
|
|
2949
|
-
|
|
3045
|
+
await reapOrphanTemporaryDirectories();
|
|
3046
|
+
await reapStaleTemporaryIdentities();
|
|
2950
3047
|
}
|
|
2951
3048
|
|
|
2952
3049
|
export {
|
|
@@ -2962,6 +3059,8 @@ export {
|
|
|
2962
3059
|
requireAuth,
|
|
2963
3060
|
validateName,
|
|
2964
3061
|
consumeOutboundHistoryFailure,
|
|
3062
|
+
openHistory,
|
|
3063
|
+
closeHistory,
|
|
2965
3064
|
listIncomingMessages,
|
|
2966
3065
|
takeUnreadMessages,
|
|
2967
3066
|
listIncomingFiles,
|
|
@@ -2972,8 +3071,12 @@ export {
|
|
|
2972
3071
|
listFileHistory,
|
|
2973
3072
|
getFileHistoryItem,
|
|
2974
3073
|
FILE_SELECTION_CAP,
|
|
3074
|
+
wrapper,
|
|
2975
3075
|
identities,
|
|
3076
|
+
quarantinedIdentities,
|
|
2976
3077
|
registrar,
|
|
3078
|
+
identityDir,
|
|
3079
|
+
keyPath,
|
|
2977
3080
|
hashLeaseToken,
|
|
2978
3081
|
writeTempMetaFile,
|
|
2979
3082
|
isSelectableWireId,
|
|
@@ -2982,7 +3085,7 @@ export {
|
|
|
2982
3085
|
tombstones,
|
|
2983
3086
|
sessionHeaders,
|
|
2984
3087
|
outboundRemovalInFlight,
|
|
2985
|
-
|
|
3088
|
+
pidDefinitelyDead,
|
|
2986
3089
|
leaseByToken,
|
|
2987
3090
|
persistBindings,
|
|
2988
3091
|
resolveBound,
|
|
@@ -3000,11 +3103,16 @@ export {
|
|
|
3000
3103
|
e2eRecoverySweep,
|
|
3001
3104
|
readBook,
|
|
3002
3105
|
exportAdBlob,
|
|
3106
|
+
exportSigningSecret,
|
|
3003
3107
|
publishToBook,
|
|
3004
3108
|
unpublishFromBook,
|
|
3109
|
+
pinRegistrar,
|
|
3110
|
+
adoptQuarantinedIdentities,
|
|
3111
|
+
createPacket,
|
|
3005
3112
|
reservedNames,
|
|
3006
3113
|
provisionIdentity,
|
|
3007
3114
|
saveState,
|
|
3115
|
+
saveStateFailClosed,
|
|
3008
3116
|
withScope,
|
|
3009
3117
|
withScopeAsync,
|
|
3010
3118
|
readonlyTx,
|
|
@@ -3025,7 +3133,8 @@ export {
|
|
|
3025
3133
|
decodeWireBin,
|
|
3026
3134
|
deleteIdentityCompletely,
|
|
3027
3135
|
closeTemporaryIdentity,
|
|
3028
|
-
|
|
3136
|
+
sessionReaperIntervalMs,
|
|
3137
|
+
reapStaleTemporaryIdentities,
|
|
3029
3138
|
envelopeDispatch,
|
|
3030
3139
|
PROTOCOL_VERSION,
|
|
3031
3140
|
clusterSweep,
|
package/dist/commands.d.ts
CHANGED
|
@@ -37,7 +37,7 @@ export declare const COMMAND_GROUPS: {
|
|
|
37
37
|
readonly kind: "daemon";
|
|
38
38
|
readonly handler: "start";
|
|
39
39
|
readonly summary: "Start a detached daemon managed by this CLI.";
|
|
40
|
-
readonly effects: "Spawns a background process, writes a PID record and log in the selected state directory, and waits up to 20
|
|
40
|
+
readonly effects: "Spawns a background process, writes a PID record and log in the selected state directory, and waits up to 20 minutes for readiness by default. Reaching the readiness deadline never stops the daemon.";
|
|
41
41
|
readonly example: "ours daemon start --config /etc/ours/config.json";
|
|
42
42
|
};
|
|
43
43
|
readonly stop: {
|
|
@@ -51,7 +51,7 @@ export declare const COMMAND_GROUPS: {
|
|
|
51
51
|
readonly kind: "daemon";
|
|
52
52
|
readonly handler: "restart";
|
|
53
53
|
readonly summary: "Stop and start the selected ours daemon.";
|
|
54
|
-
readonly effects: "Stops the selected valid ours daemon, then starts a detached replacement.";
|
|
54
|
+
readonly effects: "Stops the selected valid ours daemon, then starts a detached replacement and waits up to 20 minutes for readiness by default. Reaching the readiness deadline never stops the replacement.";
|
|
55
55
|
readonly example: "ours daemon restart --config /etc/ours/config.json";
|
|
56
56
|
};
|
|
57
57
|
readonly status: {
|
package/dist/commands.js
CHANGED
|
@@ -204,7 +204,7 @@ async function releaseHeld(lock) {
|
|
|
204
204
|
async function bootKernel() {
|
|
205
205
|
const { lock, acquired } = await ensureLock();
|
|
206
206
|
try {
|
|
207
|
-
const { bootWrapper } = await import("./boot-
|
|
207
|
+
const { bootWrapper } = await import("./boot-HNJ7ZR5L.js");
|
|
208
208
|
await bootWrapper();
|
|
209
209
|
} catch (error) {
|
|
210
210
|
if (acquired) await releaseHeld(lock);
|
|
@@ -214,7 +214,7 @@ async function bootKernel() {
|
|
|
214
214
|
async function startDaemon(opts = {}) {
|
|
215
215
|
const { lock, acquired } = await ensureLock();
|
|
216
216
|
try {
|
|
217
|
-
const runtime = await import("./server-
|
|
217
|
+
const runtime = await import("./server-35QOYLY4.js");
|
|
218
218
|
const { onRuntimeLoaded, ...daemonOptions } = opts;
|
|
219
219
|
onRuntimeLoaded?.();
|
|
220
220
|
const handle = await runtime.startDaemon({
|
package/dist/help.js
CHANGED
|
@@ -3,10 +3,10 @@ import {
|
|
|
3
3
|
helpRequestPath,
|
|
4
4
|
renderHelp,
|
|
5
5
|
renderTopHelp
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-NNHFXFLS.js";
|
|
7
7
|
import "./chunk-MS4TDUZY.js";
|
|
8
8
|
import "./chunk-6NFATG6P.js";
|
|
9
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-QKDH6CJR.js";
|
|
10
10
|
export {
|
|
11
11
|
helpHint,
|
|
12
12
|
helpRequestPath,
|
package/dist/lifecycle.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export declare const MANAGED_PID_FILE = "ours-cli-daemon.json";
|
|
|
5
5
|
/** PID file written by the pre-split @ours.network/mcp CLI. Retained only for cleanup. */
|
|
6
6
|
export declare const LEGACY_MANAGED_PID_FILE = "daemon.pid";
|
|
7
7
|
export declare const DAEMON_LOG_FILE = "ours-cli-daemon.log";
|
|
8
|
+
export declare const DEFAULT_DAEMON_READINESS_TIMEOUT_MS: number;
|
|
8
9
|
interface ManagedRecord {
|
|
9
10
|
version: 1;
|
|
10
11
|
owner: '@ours.network/cli';
|
|
@@ -34,6 +35,6 @@ export declare function readManagedRecord(stateDir: string): ManagedRecord | nul
|
|
|
34
35
|
export declare function writeManagedRecord(stateDir: string, port: number): string;
|
|
35
36
|
export declare function inspectDaemon(config: ResolvedDaemonConfig, deps?: LifecycleDeps): Promise<DaemonStatus>;
|
|
36
37
|
export declare function serveDaemon(selection: SelectionOptions, managed?: boolean): Promise<never>;
|
|
37
|
-
export declare function startDaemonManaged(selection: SelectionOptions, deps?: LifecycleDeps): Promise<DaemonStatus>;
|
|
38
|
+
export declare function startDaemonManaged(selection: SelectionOptions, deps?: LifecycleDeps, readinessTimeoutMs?: number): Promise<DaemonStatus>;
|
|
38
39
|
export declare function stopDaemonManaged(config: ResolvedDaemonConfig, deps?: LifecycleDeps): Promise<DaemonStatus>;
|
|
39
40
|
export {};
|
package/dist/lifecycle.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DAEMON_LOG_FILE,
|
|
3
|
+
DEFAULT_DAEMON_READINESS_TIMEOUT_MS,
|
|
3
4
|
LEGACY_MANAGED_PID_FILE,
|
|
4
5
|
MANAGED_PID_FILE,
|
|
5
6
|
inspectDaemon,
|
|
@@ -9,10 +10,11 @@ import {
|
|
|
9
10
|
startDaemonManaged,
|
|
10
11
|
stopDaemonManaged,
|
|
11
12
|
writeManagedRecord
|
|
12
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-DZH2YEXT.js";
|
|
13
14
|
import "./chunk-6K2JBKHI.js";
|
|
14
15
|
export {
|
|
15
16
|
DAEMON_LOG_FILE,
|
|
17
|
+
DEFAULT_DAEMON_READINESS_TIMEOUT_MS,
|
|
16
18
|
LEGACY_MANAGED_PID_FILE,
|
|
17
19
|
MANAGED_PID_FILE,
|
|
18
20
|
inspectDaemon,
|
package/dist/main.d.ts
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
+
import { type LifecycleDeps } from './lifecycle.js';
|
|
1
2
|
import { type CliIo } from './output.js';
|
|
2
|
-
export declare function runCli(argv: string[], io?: CliIo): Promise<number>;
|
|
3
|
+
export declare function runCli(argv: string[], io?: CliIo, lifecycleDeps?: LifecycleDeps): Promise<number>;
|
package/dist/main.js
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
helpHint,
|
|
18
18
|
helpRequestPath,
|
|
19
19
|
renderHelp
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-NNHFXFLS.js";
|
|
21
21
|
import {
|
|
22
22
|
invokeOperation,
|
|
23
23
|
isOperationName,
|
|
@@ -37,13 +37,14 @@ import {
|
|
|
37
37
|
IDENTITY_PREBIND_EXCEPTIONS,
|
|
38
38
|
commandSpec,
|
|
39
39
|
isCommandGroup
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-QKDH6CJR.js";
|
|
41
41
|
import {
|
|
42
|
+
DEFAULT_DAEMON_READINESS_TIMEOUT_MS,
|
|
42
43
|
inspectDaemon,
|
|
43
44
|
serveDaemon,
|
|
44
45
|
startDaemonManaged,
|
|
45
46
|
stopDaemonManaged
|
|
46
|
-
} from "./chunk-
|
|
47
|
+
} from "./chunk-DZH2YEXT.js";
|
|
47
48
|
import {
|
|
48
49
|
attachClient,
|
|
49
50
|
defaultConfigPath,
|
|
@@ -54,9 +55,10 @@ import {
|
|
|
54
55
|
import { existsSync, readFileSync } from "node:fs";
|
|
55
56
|
import { homedir } from "node:os";
|
|
56
57
|
import { join, resolve } from "node:path";
|
|
57
|
-
var CLI_VERSION = false ? "0.0.0-dev" : "2.0.
|
|
58
|
+
var CLI_VERSION = false ? "0.0.0-dev" : "2.0.4";
|
|
58
59
|
var COMMON_VALUES = /* @__PURE__ */ new Set(["--endpoint", "--port", "--state-dir", "--config", "--identity"]);
|
|
59
60
|
var COMMON_BOOLEANS = /* @__PURE__ */ new Set(["--json", "--yes", "--help"]);
|
|
61
|
+
var DAEMON_READINESS_TIMEOUT_ENV = "OURS_DAEMON_READINESS_TIMEOUT_MS";
|
|
60
62
|
function selectionFrom(values) {
|
|
61
63
|
return {
|
|
62
64
|
endpoint: values["--endpoint"],
|
|
@@ -186,11 +188,17 @@ async function runConfig(args, io) {
|
|
|
186
188
|
writeResult(io, setupConfig(flags.values["--config"] ?? defaultConfigPath(), patch, flags.booleans.has("--dry-run")), json, "config updated");
|
|
187
189
|
return 0;
|
|
188
190
|
}
|
|
189
|
-
|
|
191
|
+
function daemonReadinessTimeoutMs(values) {
|
|
192
|
+
const flag = values["--readiness-timeout-ms"];
|
|
193
|
+
if (flag !== void 0) return parseInteger(flag, "--readiness-timeout-ms", 1);
|
|
194
|
+
const configured = process.env[DAEMON_READINESS_TIMEOUT_ENV];
|
|
195
|
+
return configured === void 0 ? DEFAULT_DAEMON_READINESS_TIMEOUT_MS : parseInteger(configured, DAEMON_READINESS_TIMEOUT_ENV, 1);
|
|
196
|
+
}
|
|
197
|
+
async function runDaemon(args, io, lifecycleDeps) {
|
|
190
198
|
const action = args[0];
|
|
191
199
|
const spec = action === void 0 ? void 0 : commandSpec("daemon", action);
|
|
192
200
|
if (spec?.kind === "operation") return runGrouped("daemon", args, io);
|
|
193
|
-
const values = new Set(COMMON_VALUES);
|
|
201
|
+
const values = /* @__PURE__ */ new Set([...COMMON_VALUES, "--readiness-timeout-ms"]);
|
|
194
202
|
const booleans = /* @__PURE__ */ new Set([...COMMON_BOOLEANS, "--managed", "--dry-run", "--force"]);
|
|
195
203
|
const flags = parseFlags(args, values, booleans);
|
|
196
204
|
if (flags.positionals.length !== 1) throw new CliUsageError("daemon accepts exactly one command");
|
|
@@ -198,24 +206,25 @@ async function runDaemon(args, io) {
|
|
|
198
206
|
const handler = spec.handler;
|
|
199
207
|
const selection = selectionFrom(flags.values);
|
|
200
208
|
const json = flags.booleans.has("--json");
|
|
209
|
+
const readinessTimeoutMs = handler === "start" || handler === "restart" ? daemonReadinessTimeoutMs(flags.values) : void 0;
|
|
201
210
|
if (handler === "serve") return serveDaemon(selection, flags.booleans.has("--managed"));
|
|
202
211
|
if (handler === "status") {
|
|
203
|
-
const status = await inspectDaemon(await resolveSelection(selection));
|
|
212
|
+
const status = await inspectDaemon(await resolveSelection(selection), lifecycleDeps);
|
|
204
213
|
writeResult(io, status, json, status.state);
|
|
205
214
|
return status.state === "running" ? 0 : 3;
|
|
206
215
|
}
|
|
207
216
|
if (handler === "start") {
|
|
208
|
-
writeResult(io, await startDaemonManaged(selection), json, "started");
|
|
217
|
+
writeResult(io, await startDaemonManaged(selection, lifecycleDeps, readinessTimeoutMs), json, "started");
|
|
209
218
|
return 0;
|
|
210
219
|
}
|
|
211
220
|
if (handler === "stop") {
|
|
212
|
-
writeResult(io, await stopDaemonManaged(await resolveSelection(selection)), json, "stopped");
|
|
221
|
+
writeResult(io, await stopDaemonManaged(await resolveSelection(selection), lifecycleDeps), json, "stopped");
|
|
213
222
|
return 0;
|
|
214
223
|
}
|
|
215
224
|
if (handler === "restart") {
|
|
216
225
|
if (selection.endpoint !== void 0) throw new CliUsageError("--endpoint selects a running daemon and cannot be used with daemon restart");
|
|
217
|
-
await stopDaemonManaged(await resolveSelection(selection));
|
|
218
|
-
writeResult(io, await startDaemonManaged(selection), json, "restarted");
|
|
226
|
+
await stopDaemonManaged(await resolveSelection(selection), lifecycleDeps);
|
|
227
|
+
writeResult(io, await startDaemonManaged(selection, lifecycleDeps, readinessTimeoutMs), json, "restarted");
|
|
219
228
|
return 0;
|
|
220
229
|
}
|
|
221
230
|
if (handler === "install-service" || handler === "uninstall-service") {
|
|
@@ -234,7 +243,7 @@ async function runDaemon(args, io) {
|
|
|
234
243
|
const unsupported = handler;
|
|
235
244
|
throw new Error(`unsupported daemon handler: ${String(unsupported)}`);
|
|
236
245
|
}
|
|
237
|
-
async function runCli(argv, io = processIo) {
|
|
246
|
+
async function runCli(argv, io = processIo, lifecycleDeps) {
|
|
238
247
|
const wantsJson = argv.includes("--json");
|
|
239
248
|
try {
|
|
240
249
|
const helpPath = helpRequestPath(argv);
|
|
@@ -249,7 +258,7 @@ async function runCli(argv, io = processIo) {
|
|
|
249
258
|
}
|
|
250
259
|
if (command === "api") return await runApi(args, io);
|
|
251
260
|
if (isCommandGroup(command)) {
|
|
252
|
-
if (command === "daemon") return await runDaemon(args, io);
|
|
261
|
+
if (command === "daemon") return await runDaemon(args, io, lifecycleDeps);
|
|
253
262
|
if (command === "config") return await runConfig(args, io);
|
|
254
263
|
return await runGrouped(command, args, io);
|
|
255
264
|
}
|