@ours.network/cli 2.0.1 → 2.0.3
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 +13 -4
- package/dist/{boot-LJ2V4NSS.js → boot-BUGOBQGZ.js} +1 -1
- package/dist/{chunk-TNNBVUKK.js → chunk-NBAZ2NEL.js} +2 -2
- package/dist/{chunk-FKSRG5UJ.js → chunk-NNHFXFLS.js} +3 -2
- package/dist/{chunk-NWRXKQBD.js → chunk-QKDH6CJR.js} +7 -7
- package/dist/{chunk-NRPPG3WH.js → chunk-UIWEBE5K.js} +42 -17
- package/dist/{chunk-LJ35FDQH.js → chunk-ZCCCTR27.js} +33 -6
- package/dist/commands.d.ts +7 -7
- package/dist/commands.js +1 -1
- package/dist/{daemon-YCC24SVU.js → daemon-3C5SDTHJ.js} +2 -2
- package/dist/help.js +2 -2
- package/dist/lifecycle.d.ts +5 -1
- package/dist/lifecycle.js +7 -1
- package/dist/main.d.ts +2 -1
- package/dist/main.js +23 -14
- package/dist/mufl_code/351F16D444E6DAA252D4A8D829D17204360D9015FEF670ACE68FB4747BB3B14E.muflo +0 -0
- package/dist/{server-2RIEOKDA.js → server-TVV2EZZ6.js} +11 -1
- package/dist/service.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -61,10 +61,19 @@ ours daemon stop
|
|
|
61
61
|
ours daemon restart
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
`
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
+
|
|
72
|
+
`stop` and `restart` control the selected daemon regardless of which CLI command
|
|
73
|
+
or service launcher originally started it. The endpoint must identify itself as
|
|
74
|
+
an ours daemon for the exact selected state directory; once that validation
|
|
75
|
+
passes, the daemon-reported PID is authoritative. PID files are compatibility
|
|
76
|
+
metadata and never an authorization or ownership gate.
|
|
68
77
|
`start`, `serve`, and `restart` reject `--endpoint` because they must launch a
|
|
69
78
|
locally selected daemon; this rejection happens before `restart` probes or stops
|
|
70
79
|
anything.
|
|
@@ -73,7 +73,7 @@ function createLinuxUserSystemdAdapter(deps = defaultDeps) {
|
|
|
73
73
|
const unitName = unitNameForStateDir(stateDir);
|
|
74
74
|
const serviceFile = join(home, ".config", "systemd", "user", unitName);
|
|
75
75
|
const unitConfig = context.configPath ? resolve(context.configPath) : void 0;
|
|
76
|
-
const args = [process.execPath, resolve(context.cliPath), "daemon", "serve"];
|
|
76
|
+
const args = [process.execPath, resolve(context.cliPath), "daemon", "serve", "--managed"];
|
|
77
77
|
if (unitConfig) args.push("--config", unitConfig);
|
|
78
78
|
const environment = `Environment=OURS_STATE_DIR=${systemdEscapeArgument(stateDir)}
|
|
79
79
|
`;
|
|
@@ -165,7 +165,7 @@ function createMacosUserLaunchdAdapter(deps = defaultDeps) {
|
|
|
165
165
|
async install(context) {
|
|
166
166
|
const { stateDir, unitName, serviceFile } = plan(context);
|
|
167
167
|
const unitConfig = context.configPath ? resolve(context.configPath) : void 0;
|
|
168
|
-
const args = [process.execPath, resolve(context.cliPath), "daemon", "serve"];
|
|
168
|
+
const args = [process.execPath, resolve(context.cliPath), "daemon", "serve", "--managed"];
|
|
169
169
|
if (unitConfig) args.push("--config", unitConfig);
|
|
170
170
|
const unit = `<?xml version="1.0" encoding="UTF-8"?>
|
|
171
171
|
${PLIST_MARKER}
|
|
@@ -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,10 +106,11 @@ 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."],
|
|
112
|
-
["--managed", action === "serve" ? "Write
|
|
113
|
+
["--managed", action === "serve" ? "Write daemon process metadata after the foreground daemon starts." : "Compatibility-only/ignored for this action."],
|
|
113
114
|
["--dry-run", service.has(action) ? "Return the planned service-manager changes without writing or running systemctl." : "Compatibility-only/ignored for this action."],
|
|
114
115
|
["--force", install ? "Allow replacement of an existing user unit not marked as CLI-managed." : "Compatibility-only/ignored for this action."],
|
|
115
116
|
["--help", "Show this help and exit without starting, stopping, probing, or installing anything."]
|
|
@@ -8,34 +8,34 @@ var COMMAND_GROUPS = {
|
|
|
8
8
|
kind: "daemon",
|
|
9
9
|
handler: "serve",
|
|
10
10
|
summary: "Run the daemon in the foreground until interrupted.",
|
|
11
|
-
effects: "Starts a daemon in this process. --managed
|
|
11
|
+
effects: "Starts a daemon in this process. --managed writes process metadata for compatibility and cleanup.",
|
|
12
12
|
example: "ours daemon serve --config /etc/ours/config.json"
|
|
13
13
|
},
|
|
14
14
|
start: {
|
|
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: {
|
|
22
22
|
kind: "daemon",
|
|
23
23
|
handler: "stop",
|
|
24
|
-
summary: "Stop
|
|
25
|
-
effects: "Signals
|
|
24
|
+
summary: "Stop the selected ours daemon.",
|
|
25
|
+
effects: "Signals the process identity reported by the selected valid ours daemon.",
|
|
26
26
|
example: "ours daemon stop --config /etc/ours/config.json"
|
|
27
27
|
},
|
|
28
28
|
restart: {
|
|
29
29
|
kind: "daemon",
|
|
30
30
|
handler: "restart",
|
|
31
|
-
summary: "Stop and start
|
|
32
|
-
effects: "
|
|
31
|
+
summary: "Stop and start the selected ours daemon.",
|
|
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: {
|
|
36
36
|
kind: "daemon",
|
|
37
37
|
handler: "status",
|
|
38
|
-
summary: "Report whether the selected daemon is running
|
|
38
|
+
summary: "Report whether the selected ours daemon is running.",
|
|
39
39
|
effects: "Read-only. Exit status is 0 when running and 3 when stopped.",
|
|
40
40
|
example: "ours daemon status --json"
|
|
41
41
|
},
|
|
@@ -9,7 +9,9 @@ import { spawn as nodeSpawn } from "node:child_process";
|
|
|
9
9
|
import { closeSync, existsSync, mkdirSync, openSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
10
10
|
import { join, resolve } from "node:path";
|
|
11
11
|
var MANAGED_PID_FILE = "ours-cli-daemon.json";
|
|
12
|
+
var LEGACY_MANAGED_PID_FILE = "daemon.pid";
|
|
12
13
|
var DAEMON_LOG_FILE = "ours-cli-daemon.log";
|
|
14
|
+
var DEFAULT_DAEMON_READINESS_TIMEOUT_MS = 20 * 60 * 1e3;
|
|
13
15
|
var depsDefault = {
|
|
14
16
|
fetch: globalThis.fetch,
|
|
15
17
|
spawn: nodeSpawn,
|
|
@@ -19,6 +21,17 @@ var depsDefault = {
|
|
|
19
21
|
cliPath: process.argv[1]
|
|
20
22
|
};
|
|
21
23
|
var pidPath = (stateDir) => join(stateDir, MANAGED_PID_FILE);
|
|
24
|
+
var legacyPidPath = (stateDir) => join(stateDir, LEGACY_MANAGED_PID_FILE);
|
|
25
|
+
function readLegacyManagedPid(stateDir) {
|
|
26
|
+
try {
|
|
27
|
+
const raw = readFileSync(legacyPidPath(stateDir), "utf8").trim();
|
|
28
|
+
if (!/^[0-9]+$/.test(raw)) return null;
|
|
29
|
+
const pid = Number(raw);
|
|
30
|
+
return Number.isSafeInteger(pid) && pid > 1 ? pid : null;
|
|
31
|
+
} catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
22
35
|
function readManagedRecord(stateDir) {
|
|
23
36
|
try {
|
|
24
37
|
const value = JSON.parse(readFileSync(pidPath(stateDir), "utf8"));
|
|
@@ -49,7 +62,9 @@ function writeManagedRecord(stateDir, port) {
|
|
|
49
62
|
async function inspectDaemon(config, deps = depsDefault) {
|
|
50
63
|
const stateDir = resolve(config.expectStateDir);
|
|
51
64
|
const record = readManagedRecord(stateDir);
|
|
52
|
-
const
|
|
65
|
+
const legacyPid = readLegacyManagedPid(stateDir);
|
|
66
|
+
const hasOwnershipRecord = record !== null || legacyPid !== null;
|
|
67
|
+
const base = { stateDir, pidFile: pidPath(stateDir), stalePidFile: hasOwnershipRecord };
|
|
53
68
|
let client;
|
|
54
69
|
try {
|
|
55
70
|
client = await attachClient(config, { fetch: deps.fetch, leaseToken: "ours-cli-status" });
|
|
@@ -71,10 +86,7 @@ async function inspectDaemon(config, deps = depsDefault) {
|
|
|
71
86
|
if (info.name !== "ours" || !Number.isInteger(info.pid) || info.pid <= 1 || resolve(info.stateDir) !== stateDir) {
|
|
72
87
|
throw new Error("the selected endpoint did not return a valid ours daemon identity");
|
|
73
88
|
}
|
|
74
|
-
|
|
75
|
-
const selectedPort = Number(selectedUrl.port || (selectedUrl.protocol === "https:" ? 443 : 80));
|
|
76
|
-
const managed = record !== null && record.pid === info.pid && resolve(record.stateDir) === stateDir && record.port === selectedPort;
|
|
77
|
-
return { state: "running", managed, info, ...base, stalePidFile: record !== null && !managed };
|
|
89
|
+
return { state: "running", managed: true, info, ...base, stalePidFile: false };
|
|
78
90
|
}
|
|
79
91
|
function childEnvironment(selection) {
|
|
80
92
|
const env = { ...process.env };
|
|
@@ -87,7 +99,7 @@ function childEnvironment(selection) {
|
|
|
87
99
|
async function serveDaemon(selection, managed = false) {
|
|
88
100
|
if (selection.endpoint !== void 0) throw new Error("--endpoint selects a running daemon and cannot be used with daemon serve");
|
|
89
101
|
Object.assign(process.env, childEnvironment(selection));
|
|
90
|
-
const { startDaemon } = await import("./daemon-
|
|
102
|
+
const { startDaemon } = await import("./daemon-3C5SDTHJ.js");
|
|
91
103
|
const handle = await startDaemon();
|
|
92
104
|
if (managed) {
|
|
93
105
|
const response = await fetch(`http://127.0.0.1:${handle.port}/state-dir`);
|
|
@@ -100,8 +112,11 @@ async function serveDaemon(selection, managed = false) {
|
|
|
100
112
|
}
|
|
101
113
|
return await new Promise(() => void 0);
|
|
102
114
|
}
|
|
103
|
-
async function startDaemonManaged(selection, deps = depsDefault) {
|
|
115
|
+
async function startDaemonManaged(selection, deps = depsDefault, readinessTimeoutMs = DEFAULT_DAEMON_READINESS_TIMEOUT_MS) {
|
|
104
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
|
+
}
|
|
105
120
|
const config = await resolveSelection(selection);
|
|
106
121
|
const before = await inspectDaemon(config, deps);
|
|
107
122
|
if (before.state === "running") return before;
|
|
@@ -121,11 +136,11 @@ async function startDaemonManaged(selection, deps = depsDefault) {
|
|
|
121
136
|
}
|
|
122
137
|
if (!child.pid || child.pid <= 1) throw new Error(`daemon start failed; see ${logPath}`);
|
|
123
138
|
child.unref();
|
|
124
|
-
const deadline = deps.now() +
|
|
139
|
+
const deadline = deps.now() + readinessTimeoutMs;
|
|
125
140
|
while (deps.now() < deadline) {
|
|
126
|
-
await deps.delay(100);
|
|
141
|
+
await deps.delay(Math.min(100, deadline - deps.now()));
|
|
127
142
|
const status = await inspectDaemon(config, deps);
|
|
128
|
-
if (status.state === "running" && status.
|
|
143
|
+
if (status.state === "running" && status.info?.pid === child.pid) return status;
|
|
129
144
|
try {
|
|
130
145
|
deps.kill(child.pid, 0);
|
|
131
146
|
} catch {
|
|
@@ -133,10 +148,13 @@ async function startDaemonManaged(selection, deps = depsDefault) {
|
|
|
133
148
|
}
|
|
134
149
|
}
|
|
135
150
|
try {
|
|
136
|
-
deps.kill(child.pid,
|
|
151
|
+
deps.kill(child.pid, 0);
|
|
137
152
|
} catch {
|
|
153
|
+
throw new Error(`daemon exited before becoming ready; see ${logPath}`);
|
|
138
154
|
}
|
|
139
|
-
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
|
+
);
|
|
140
158
|
}
|
|
141
159
|
function removeOwnedPidFile(status) {
|
|
142
160
|
const record = readManagedRecord(status.stateDir);
|
|
@@ -146,6 +164,12 @@ function removeOwnedPidFile(status) {
|
|
|
146
164
|
} catch {
|
|
147
165
|
}
|
|
148
166
|
}
|
|
167
|
+
if (readLegacyManagedPid(status.stateDir) !== null) {
|
|
168
|
+
try {
|
|
169
|
+
unlinkSync(legacyPidPath(status.stateDir));
|
|
170
|
+
} catch {
|
|
171
|
+
}
|
|
172
|
+
}
|
|
149
173
|
}
|
|
150
174
|
async function stopDaemonManaged(config, deps = depsDefault) {
|
|
151
175
|
const status = await inspectDaemon(config, deps);
|
|
@@ -153,10 +177,8 @@ async function stopDaemonManaged(config, deps = depsDefault) {
|
|
|
153
177
|
removeOwnedPidFile(status);
|
|
154
178
|
return status;
|
|
155
179
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
deps.kill(status.info.pid, "SIGTERM");
|
|
180
|
+
const pid = status.info.pid;
|
|
181
|
+
deps.kill(pid, "SIGTERM");
|
|
160
182
|
const deadline = deps.now() + 1e4;
|
|
161
183
|
while (deps.now() < deadline) {
|
|
162
184
|
await deps.delay(100);
|
|
@@ -166,12 +188,15 @@ async function stopDaemonManaged(config, deps = depsDefault) {
|
|
|
166
188
|
return { ...current, stalePidFile: false };
|
|
167
189
|
}
|
|
168
190
|
}
|
|
169
|
-
throw new Error(`daemon pid ${
|
|
191
|
+
throw new Error(`daemon pid ${pid} did not stop within 10 seconds`);
|
|
170
192
|
}
|
|
171
193
|
|
|
172
194
|
export {
|
|
173
195
|
MANAGED_PID_FILE,
|
|
196
|
+
LEGACY_MANAGED_PID_FILE,
|
|
174
197
|
DAEMON_LOG_FILE,
|
|
198
|
+
DEFAULT_DAEMON_READINESS_TIMEOUT_MS,
|
|
199
|
+
readLegacyManagedPid,
|
|
175
200
|
readManagedRecord,
|
|
176
201
|
writeManagedRecord,
|
|
177
202
|
inspectDaemon,
|
|
@@ -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.3" : "0.0.0-dev";
|
|
93
93
|
var CONFIG = loadConfig();
|
|
94
94
|
var STATE_DIR = CONFIG.stateDir;
|
|
95
95
|
var BROKER_URL = CONFIG.brokerUrl;
|
|
@@ -639,12 +639,30 @@ function listIncomingMessages(id) {
|
|
|
639
639
|
function takeUnreadMessages(id, input = {}) {
|
|
640
640
|
const db = openHistory(id);
|
|
641
641
|
const limit = batchLimit(input.limit);
|
|
642
|
+
const requested = input.wire_ids;
|
|
642
643
|
return db.transaction(() => {
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
644
|
+
let rows;
|
|
645
|
+
if (requested !== void 0) {
|
|
646
|
+
if (requested.length < 1 || requested.length > MAX_BATCH) {
|
|
647
|
+
throw new Error(`wire_ids must contain 1-${MAX_BATCH} items`);
|
|
648
|
+
}
|
|
649
|
+
if (new Set(requested).size !== requested.length) {
|
|
650
|
+
throw new Error("wire_ids must not contain duplicates");
|
|
651
|
+
}
|
|
652
|
+
const get = db.prepare(`
|
|
653
|
+
SELECT * FROM messages WHERE wire_id = ? AND direction = 'in' AND inbox_state = 'unread'
|
|
654
|
+
`);
|
|
655
|
+
rows = requested.map((wireId) => get.get(wireId)).filter((row) => !!row);
|
|
656
|
+
if (rows.length !== requested.length) {
|
|
657
|
+
throw new Error("selected message wire_id is unknown, stale, or no longer unread");
|
|
658
|
+
}
|
|
659
|
+
} else {
|
|
660
|
+
rows = db.prepare(`
|
|
661
|
+
SELECT * FROM messages
|
|
662
|
+
WHERE direction = 'in' AND inbox_state = 'unread'
|
|
663
|
+
ORDER BY seq ASC LIMIT ?
|
|
664
|
+
`).all(limit);
|
|
665
|
+
}
|
|
648
666
|
const mark = db.prepare(`
|
|
649
667
|
UPDATE messages SET inbox_state = 'read'
|
|
650
668
|
WHERE seq = ? AND direction = 'in' AND inbox_state = 'unread'
|
|
@@ -729,6 +747,14 @@ function getMessageHistoryItem(id, wireId) {
|
|
|
729
747
|
const row = openHistory(id).prepare("SELECT * FROM messages WHERE wire_id = ?").get(wireId);
|
|
730
748
|
return row ? messageFromRow(row) : null;
|
|
731
749
|
}
|
|
750
|
+
function getMessageHistorySummary(id, input) {
|
|
751
|
+
const row = openHistory(id).prepare(`
|
|
752
|
+
SELECT COUNT(*) AS total,
|
|
753
|
+
SUM(CASE WHEN direction = 'in' AND inbox_state = 'unread' THEN 1 ELSE 0 END) AS unread
|
|
754
|
+
FROM messages WHERE peer_cid = ?
|
|
755
|
+
`).get(input.peer_cid);
|
|
756
|
+
return { total: Number(row.total), unread: Number(row.unread ?? 0) };
|
|
757
|
+
}
|
|
732
758
|
function listFileHistory(id, query = {}) {
|
|
733
759
|
const db = openHistory(id);
|
|
734
760
|
const limit = batchLimit(query.limit);
|
|
@@ -2942,6 +2968,7 @@ export {
|
|
|
2942
2968
|
takeUnreadFiles,
|
|
2943
2969
|
listMessageHistory,
|
|
2944
2970
|
getMessageHistoryItem,
|
|
2971
|
+
getMessageHistorySummary,
|
|
2945
2972
|
listFileHistory,
|
|
2946
2973
|
getFileHistoryItem,
|
|
2947
2974
|
FILE_SELECTION_CAP,
|
package/dist/commands.d.ts
CHANGED
|
@@ -30,34 +30,34 @@ export declare const COMMAND_GROUPS: {
|
|
|
30
30
|
readonly kind: "daemon";
|
|
31
31
|
readonly handler: "serve";
|
|
32
32
|
readonly summary: "Run the daemon in the foreground until interrupted.";
|
|
33
|
-
readonly effects: "Starts a daemon in this process. --managed
|
|
33
|
+
readonly effects: "Starts a daemon in this process. --managed writes process metadata for compatibility and cleanup.";
|
|
34
34
|
readonly example: "ours daemon serve --config /etc/ours/config.json";
|
|
35
35
|
};
|
|
36
36
|
readonly start: {
|
|
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: {
|
|
44
44
|
readonly kind: "daemon";
|
|
45
45
|
readonly handler: "stop";
|
|
46
|
-
readonly summary: "Stop
|
|
47
|
-
readonly effects: "Signals
|
|
46
|
+
readonly summary: "Stop the selected ours daemon.";
|
|
47
|
+
readonly effects: "Signals the process identity reported by the selected valid ours daemon.";
|
|
48
48
|
readonly example: "ours daemon stop --config /etc/ours/config.json";
|
|
49
49
|
};
|
|
50
50
|
readonly restart: {
|
|
51
51
|
readonly kind: "daemon";
|
|
52
52
|
readonly handler: "restart";
|
|
53
|
-
readonly summary: "Stop and start
|
|
54
|
-
readonly effects: "
|
|
53
|
+
readonly summary: "Stop and start the selected ours daemon.";
|
|
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: {
|
|
58
58
|
readonly kind: "daemon";
|
|
59
59
|
readonly handler: "status";
|
|
60
|
-
readonly summary: "Report whether the selected daemon is running
|
|
60
|
+
readonly summary: "Report whether the selected ours daemon is running.";
|
|
61
61
|
readonly effects: "Read-only. Exit status is 0 when running and 3 when stopped.";
|
|
62
62
|
readonly example: "ours daemon status --json";
|
|
63
63
|
};
|
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-BUGOBQGZ.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-TVV2EZZ6.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
|
@@ -2,7 +2,10 @@ import { spawn as nodeSpawn, type ChildProcess } from 'node:child_process';
|
|
|
2
2
|
import type { DaemonInfo, ResolvedDaemonConfig } from '@ours.network/sdk/client';
|
|
3
3
|
import { type SelectionOptions } from './connection.js';
|
|
4
4
|
export declare const MANAGED_PID_FILE = "ours-cli-daemon.json";
|
|
5
|
+
/** PID file written by the pre-split @ours.network/mcp CLI. Retained only for cleanup. */
|
|
6
|
+
export declare const LEGACY_MANAGED_PID_FILE = "daemon.pid";
|
|
5
7
|
export declare const DAEMON_LOG_FILE = "ours-cli-daemon.log";
|
|
8
|
+
export declare const DEFAULT_DAEMON_READINESS_TIMEOUT_MS: number;
|
|
6
9
|
interface ManagedRecord {
|
|
7
10
|
version: 1;
|
|
8
11
|
owner: '@ours.network/cli';
|
|
@@ -27,10 +30,11 @@ export interface LifecycleDeps {
|
|
|
27
30
|
delay(ms: number): Promise<void>;
|
|
28
31
|
cliPath: string;
|
|
29
32
|
}
|
|
33
|
+
export declare function readLegacyManagedPid(stateDir: string): number | null;
|
|
30
34
|
export declare function readManagedRecord(stateDir: string): ManagedRecord | null;
|
|
31
35
|
export declare function writeManagedRecord(stateDir: string, port: number): string;
|
|
32
36
|
export declare function inspectDaemon(config: ResolvedDaemonConfig, deps?: LifecycleDeps): Promise<DaemonStatus>;
|
|
33
37
|
export declare function serveDaemon(selection: SelectionOptions, managed?: boolean): Promise<never>;
|
|
34
|
-
export declare function startDaemonManaged(selection: SelectionOptions, deps?: LifecycleDeps): Promise<DaemonStatus>;
|
|
38
|
+
export declare function startDaemonManaged(selection: SelectionOptions, deps?: LifecycleDeps, readinessTimeoutMs?: number): Promise<DaemonStatus>;
|
|
35
39
|
export declare function stopDaemonManaged(config: ResolvedDaemonConfig, deps?: LifecycleDeps): Promise<DaemonStatus>;
|
|
36
40
|
export {};
|
package/dist/lifecycle.js
CHANGED
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
import {
|
|
2
2
|
DAEMON_LOG_FILE,
|
|
3
|
+
DEFAULT_DAEMON_READINESS_TIMEOUT_MS,
|
|
4
|
+
LEGACY_MANAGED_PID_FILE,
|
|
3
5
|
MANAGED_PID_FILE,
|
|
4
6
|
inspectDaemon,
|
|
7
|
+
readLegacyManagedPid,
|
|
5
8
|
readManagedRecord,
|
|
6
9
|
serveDaemon,
|
|
7
10
|
startDaemonManaged,
|
|
8
11
|
stopDaemonManaged,
|
|
9
12
|
writeManagedRecord
|
|
10
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-UIWEBE5K.js";
|
|
11
14
|
import "./chunk-6K2JBKHI.js";
|
|
12
15
|
export {
|
|
13
16
|
DAEMON_LOG_FILE,
|
|
17
|
+
DEFAULT_DAEMON_READINESS_TIMEOUT_MS,
|
|
18
|
+
LEGACY_MANAGED_PID_FILE,
|
|
14
19
|
MANAGED_PID_FILE,
|
|
15
20
|
inspectDaemon,
|
|
21
|
+
readLegacyManagedPid,
|
|
16
22
|
readManagedRecord,
|
|
17
23
|
serveDaemon,
|
|
18
24
|
startDaemonManaged,
|
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
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from "./chunk-YC7QBGVC.js";
|
|
6
6
|
import {
|
|
7
7
|
createServiceAdapter
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-NBAZ2NEL.js";
|
|
9
9
|
import {
|
|
10
10
|
instanceNameFromStateDir
|
|
11
11
|
} from "./chunk-UZQWSOUB.js";
|
|
@@ -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-UIWEBE5K.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.3";
|
|
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
|
}
|
package/dist/mufl_code/351F16D444E6DAA252D4A8D829D17204360D9015FEF670ACE68FB4747BB3B14E.muflo
CHANGED
|
Binary file
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
findIdentityFile,
|
|
30
30
|
getFileHistoryItem,
|
|
31
31
|
getMessageHistoryItem,
|
|
32
|
+
getMessageHistorySummary,
|
|
32
33
|
hashLeaseToken,
|
|
33
34
|
identities,
|
|
34
35
|
isSelectableWireId,
|
|
@@ -78,7 +79,7 @@ import {
|
|
|
78
79
|
withScope,
|
|
79
80
|
withScopeAsync,
|
|
80
81
|
writeTempMetaFile
|
|
81
|
-
} from "./chunk-
|
|
82
|
+
} from "./chunk-ZCCCTR27.js";
|
|
82
83
|
import {
|
|
83
84
|
buildIdentityFile,
|
|
84
85
|
writeIdentityFile
|
|
@@ -1482,6 +1483,14 @@ function getHistoryItem(ctx, input) {
|
|
|
1482
1483
|
throw errTool("get_history_item", String(error));
|
|
1483
1484
|
}
|
|
1484
1485
|
}
|
|
1486
|
+
function getHistorySummary(ctx, input) {
|
|
1487
|
+
const id = requireBound(ctx);
|
|
1488
|
+
try {
|
|
1489
|
+
return getMessageHistorySummary(id, input);
|
|
1490
|
+
} catch (error) {
|
|
1491
|
+
throw errTool("get_history_summary", String(error));
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1485
1494
|
function listFiles(ctx, query = {}) {
|
|
1486
1495
|
const id = requireBound(ctx);
|
|
1487
1496
|
try {
|
|
@@ -1539,6 +1548,7 @@ var API_OPERATIONS = {
|
|
|
1539
1548
|
getMessages: op(getMessages),
|
|
1540
1549
|
listHistory: op(listHistory),
|
|
1541
1550
|
getHistoryItem: op(getHistoryItem),
|
|
1551
|
+
getHistorySummary: op(getHistorySummary),
|
|
1542
1552
|
listFiles: op(listFiles),
|
|
1543
1553
|
getFileInfo: op(getFileInfo),
|
|
1544
1554
|
// ----- files (Task 12) -----------------------------------------------------
|
package/dist/service.js
CHANGED