@camerontaylor/paseo-cli 0.8.0-fork.1 → 0.8.0-fork.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/dist/cli.js +25 -37
- package/dist/commands/agent/archive.js +2 -15
- package/dist/commands/agent/attach.d.ts +1 -0
- package/dist/commands/agent/attach.js +8 -12
- package/dist/commands/agent/delete.js +4 -15
- package/dist/commands/agent/detach.js +2 -13
- package/dist/commands/agent/import.js +2 -16
- package/dist/commands/agent/inspect.js +2 -15
- package/dist/commands/agent/logs.js +12 -12
- package/dist/commands/agent/ls.js +2 -23
- package/dist/commands/agent/mode.js +2 -2
- package/dist/commands/agent/open.js +2 -8
- package/dist/commands/agent/reload.js +2 -15
- package/dist/commands/agent/run.js +11 -16
- package/dist/commands/agent/send.js +6 -15
- package/dist/commands/agent/stop.js +2 -15
- package/dist/commands/agent/update.js +2 -15
- package/dist/commands/agent/wait.js +4 -15
- package/dist/commands/clone.js +2 -8
- package/dist/commands/daemon/config.d.ts +3 -0
- package/dist/commands/daemon/config.js +102 -0
- package/dist/commands/daemon/index.js +20 -52
- package/dist/commands/daemon/local-daemon.d.ts +10 -75
- package/dist/commands/daemon/local-daemon.js +61 -503
- package/dist/commands/daemon/pair.d.ts +3 -8
- package/dist/commands/daemon/pair.js +31 -61
- package/dist/commands/daemon/reload.d.ts +3 -1
- package/dist/commands/daemon/reload.js +10 -2
- package/dist/commands/daemon/restart.d.ts +19 -11
- package/dist/commands/daemon/restart.js +102 -88
- package/dist/commands/daemon/set-password.js +6 -7
- package/dist/commands/daemon/start.d.ts +17 -3
- package/dist/commands/daemon/start.js +47 -52
- package/dist/commands/daemon/status.d.ts +12 -20
- package/dist/commands/daemon/status.js +90 -342
- package/dist/commands/daemon/stop.d.ts +19 -14
- package/dist/commands/daemon/stop.js +53 -55
- package/dist/commands/heartbeat/index.js +3 -3
- package/dist/commands/hub/connect.d.ts +1 -0
- package/dist/commands/hub/connect.js +1 -1
- package/dist/commands/hub/daemon-client.d.ts +3 -2
- package/dist/commands/hub/daemon-client.js +3 -3
- package/dist/commands/hub/disconnect.d.ts +1 -0
- package/dist/commands/hub/disconnect.js +1 -1
- package/dist/commands/hub/index.js +2 -2
- package/dist/commands/hub/init.d.ts +3 -1
- package/dist/commands/hub/init.js +13 -12
- package/dist/commands/hub/login.d.ts +4 -4
- package/dist/commands/hub/login.js +1 -1
- package/dist/commands/hub/logout.d.ts +1 -0
- package/dist/commands/hub/logout.js +2 -2
- package/dist/commands/hub/permissions.d.ts +1 -0
- package/dist/commands/hub/permissions.js +2 -2
- package/dist/commands/onboard.d.ts +7 -2
- package/dist/commands/onboard.js +58 -210
- package/dist/commands/open.js +2 -0
- package/dist/commands/permit/allow.js +2 -15
- package/dist/commands/permit/deny.js +2 -15
- package/dist/commands/permit/ls.js +2 -15
- package/dist/commands/plugin/index.js +7 -7
- package/dist/commands/plugin/shared.d.ts +4 -3
- package/dist/commands/plugin/shared.js +8 -8
- package/dist/commands/project/create.js +5 -3
- package/dist/commands/project/delete.js +4 -2
- package/dist/commands/project/ls.js +2 -2
- package/dist/commands/project/rename.js +4 -2
- package/dist/commands/provider/add.js +2 -2
- package/dist/commands/provider/diagnostic.js +1 -1
- package/dist/commands/provider/ls.js +2 -30
- package/dist/commands/provider/models.js +1 -1
- package/dist/commands/provider/rm.js +2 -2
- package/dist/commands/schedule/create.js +2 -2
- package/dist/commands/schedule/delete.js +1 -1
- package/dist/commands/schedule/inspect.js +1 -1
- package/dist/commands/schedule/logs.js +1 -1
- package/dist/commands/schedule/ls.js +1 -1
- package/dist/commands/schedule/pause.js +1 -1
- package/dist/commands/schedule/resume.js +1 -1
- package/dist/commands/schedule/run-once.js +1 -1
- package/dist/commands/schedule/shared.d.ts +3 -1
- package/dist/commands/schedule/shared.js +7 -5
- package/dist/commands/schedule/update.js +1 -1
- package/dist/commands/script/ls.js +1 -1
- package/dist/commands/script/shared.d.ts +2 -1
- package/dist/commands/script/shared.js +3 -3
- package/dist/commands/script/start.js +1 -1
- package/dist/commands/script/stop.js +1 -1
- package/dist/commands/terminal/capture.d.ts +1 -1
- package/dist/commands/terminal/capture.js +2 -3
- package/dist/commands/terminal/create.js +1 -1
- package/dist/commands/terminal/kill.js +1 -1
- package/dist/commands/terminal/ls.js +1 -1
- package/dist/commands/terminal/send-keys.d.ts +1 -1
- package/dist/commands/terminal/send-keys.js +2 -3
- package/dist/commands/terminal/shared.d.ts +2 -1
- package/dist/commands/terminal/shared.js +5 -3
- package/dist/commands/workspace/archive.d.ts +1 -0
- package/dist/commands/workspace/archive.js +4 -2
- package/dist/commands/workspace/create.js +4 -2
- package/dist/commands/workspace/ls.d.ts +1 -0
- package/dist/commands/workspace/ls.js +2 -2
- package/dist/commands/workspace/rename.d.ts +1 -0
- package/dist/commands/workspace/rename.js +4 -2
- package/dist/commands/workspace/setup.d.ts +1 -0
- package/dist/commands/workspace/setup.js +2 -2
- package/dist/commands/worktree/archive.js +4 -2
- package/dist/commands/worktree/create.js +2 -10
- package/dist/commands/worktree/ls.d.ts +0 -2
- package/dist/commands/worktree/ls.js +4 -30
- package/dist/output/with-output.d.ts +2 -0
- package/dist/run.js +11 -1
- package/dist/utils/client-id.d.ts +1 -1
- package/dist/utils/client-id.js +8 -14
- package/dist/utils/client.d.ts +16 -21
- package/dist/utils/client.js +57 -109
- package/dist/utils/command-options.d.ts +1 -0
- package/dist/utils/command-options.js +24 -2
- package/dist/utils/daemon-target.d.ts +13 -0
- package/dist/utils/daemon-target.js +51 -0
- package/package.json +8 -4
|
@@ -1,363 +1,111 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
function
|
|
9
|
-
|
|
10
|
-
return error.message;
|
|
11
|
-
}
|
|
12
|
-
return String(error);
|
|
13
|
-
}
|
|
14
|
-
function shortenMessage(message, max = 120) {
|
|
15
|
-
const normalized = message.replace(/\s+/g, " ").trim();
|
|
16
|
-
if (normalized.length <= max) {
|
|
17
|
-
return normalized;
|
|
18
|
-
}
|
|
19
|
-
return `${normalized.slice(0, max - 3)}...`;
|
|
20
|
-
}
|
|
21
|
-
function appendNote(current, next) {
|
|
22
|
-
if (!next)
|
|
23
|
-
return current;
|
|
24
|
-
if (!current)
|
|
25
|
-
return next;
|
|
26
|
-
return `${current}; ${next}`;
|
|
27
|
-
}
|
|
28
|
-
function resolveCliVersion() {
|
|
29
|
-
try {
|
|
30
|
-
const packageJson = require("../../../package.json");
|
|
31
|
-
if (typeof packageJson.version === "string" && packageJson.version.trim().length > 0) {
|
|
32
|
-
return packageJson.version.trim();
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
catch {
|
|
36
|
-
// Fall through.
|
|
37
|
-
}
|
|
38
|
-
return "unknown";
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { DaemonConnectionError } from "@camerontaylor/paseo-client/internal/daemon-client";
|
|
3
|
+
import { readDaemonInstance, readPersistedConfig, resolveConfigFromPersisted, daemonLogPath, isSameDaemonInstance, DaemonInstanceError, } from "@camerontaylor/paseo-server";
|
|
4
|
+
import { connectToDaemon, buildDaemonConnectionCommandError } from "../../utils/client.js";
|
|
5
|
+
import { withOutput, toCommandError } from "../../output/index.js";
|
|
6
|
+
import { addJsonAndDaemonHostOptions } from "../../utils/command-options.js";
|
|
7
|
+
import { describeDaemonTarget } from "../../utils/daemon-target.js";
|
|
8
|
+
export function daemonStatusCommand() {
|
|
9
|
+
return addJsonAndDaemonHostOptions(new Command("status").description("Observe the selected daemon and its published endpoint")).action(withOutput(runStatusCommand));
|
|
39
10
|
}
|
|
40
|
-
function
|
|
11
|
+
export async function runStatusCommand(options, _command) {
|
|
12
|
+
const target = options.daemonTarget;
|
|
13
|
+
const instance = target.kind === "instance" ? await readDaemonInstance(target.home) : null;
|
|
14
|
+
const local = target.kind === "instance"
|
|
15
|
+
? localStatus(target.home, instance)
|
|
16
|
+
: { host: describeDaemonTarget(target) };
|
|
17
|
+
const observed = target.kind === "endpoint" || instance?.listen
|
|
18
|
+
? await probeDaemonStatus(target, instance, local)
|
|
19
|
+
: { connectedDaemon: "not_probed" };
|
|
20
|
+
const data = { ...local, ...observed };
|
|
41
21
|
return {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (item.value === "unresponsive")
|
|
53
|
-
return "yellow";
|
|
54
|
-
return "red";
|
|
55
|
-
}
|
|
56
|
-
if (item.key === "Connected Daemon") {
|
|
57
|
-
if (item.value === "reachable")
|
|
58
|
-
return "green";
|
|
59
|
-
if (item.value === "not_probed" || item.value === "auth_required")
|
|
60
|
-
return "yellow";
|
|
61
|
-
return "red";
|
|
62
|
-
}
|
|
63
|
-
if (item.key.startsWith(" ")) {
|
|
64
|
-
if (item.value === "not found" || item.value === "not found (daemon)")
|
|
65
|
-
return "red";
|
|
66
|
-
if (item.value.endsWith("(--version failed)"))
|
|
67
|
-
return "yellow";
|
|
68
|
-
return "green";
|
|
69
|
-
}
|
|
70
|
-
return undefined;
|
|
71
|
-
},
|
|
72
|
-
},
|
|
73
|
-
],
|
|
74
|
-
serialize: () => status,
|
|
22
|
+
type: "single",
|
|
23
|
+
data,
|
|
24
|
+
schema: {
|
|
25
|
+
idField: () => "daemon",
|
|
26
|
+
columns: [],
|
|
27
|
+
renderHuman: () => Object.entries(data)
|
|
28
|
+
.filter(([, value]) => value !== undefined)
|
|
29
|
+
.map(([key, value]) => `${key}: ${typeof value === "object" ? JSON.stringify(value) : value}`)
|
|
30
|
+
.join("\n"),
|
|
31
|
+
},
|
|
75
32
|
};
|
|
76
33
|
}
|
|
77
|
-
function
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
{ key: "Connected Daemon", value: status.connectedDaemon },
|
|
82
|
-
{ key: "Home", value: status.home },
|
|
83
|
-
{ key: "Listen", value: status.listen },
|
|
84
|
-
{ key: "Relay", value: status.relay },
|
|
85
|
-
{ key: "Hostname", value: status.hostname ?? "-" },
|
|
86
|
-
{ key: "PID", value: status.pid === null ? "-" : String(status.pid) },
|
|
87
|
-
{ key: "Started", value: status.startedAt ?? "-" },
|
|
88
|
-
{ key: "Owner", value: status.owner ?? "-" },
|
|
89
|
-
{ key: "Logs", value: status.logPath },
|
|
90
|
-
{ key: "Daemon Node", value: status.daemonNode },
|
|
91
|
-
{ key: "CLI Node", value: status.cliNode },
|
|
92
|
-
{ key: "CLI", value: status.cliVersion },
|
|
93
|
-
{ key: "Daemon Version", value: status.daemonVersion ?? "-" },
|
|
94
|
-
];
|
|
95
|
-
if (status.note) {
|
|
96
|
-
rows.push({ key: "Note", value: status.note });
|
|
97
|
-
}
|
|
98
|
-
rows.push({ key: "", value: "" });
|
|
99
|
-
rows.push({ key: "Providers", value: "" });
|
|
100
|
-
for (const provider of status.providers) {
|
|
101
|
-
if (provider.source === "daemon") {
|
|
102
|
-
if (!provider.path) {
|
|
103
|
-
rows.push({ key: ` ${provider.label}`, value: "not found (daemon)" });
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
rows.push({ key: ` ${provider.label}`, value: `${provider.path} (daemon)` });
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
else if (!provider.path) {
|
|
110
|
-
rows.push({ key: ` ${provider.label}`, value: "not found" });
|
|
111
|
-
}
|
|
112
|
-
else if (!provider.version) {
|
|
113
|
-
rows.push({ key: ` ${provider.label}`, value: `${provider.path} (--version failed)` });
|
|
114
|
-
}
|
|
115
|
-
else {
|
|
116
|
-
rows.push({ key: ` ${provider.label}`, value: `${provider.path} (${provider.version})` });
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
return rows;
|
|
120
|
-
}
|
|
121
|
-
const PROVIDER_BINARIES = [
|
|
122
|
-
{ label: "Claude", binary: "claude" },
|
|
123
|
-
{ label: "Codex", binary: "codex" },
|
|
124
|
-
{ label: "OpenCode", binary: "opencode" },
|
|
125
|
-
];
|
|
126
|
-
async function checkProviderBinary(binary) {
|
|
127
|
-
const binaryPath = await findExecutable(binary);
|
|
128
|
-
if (!binaryPath) {
|
|
129
|
-
return { path: null, version: null };
|
|
130
|
-
}
|
|
131
|
-
try {
|
|
132
|
-
const { stdout } = await execCommand(binaryPath, ["--version"], {
|
|
133
|
-
timeout: 5000,
|
|
134
|
-
});
|
|
135
|
-
return { path: binaryPath, version: stdout.trim() || null };
|
|
136
|
-
}
|
|
137
|
-
catch {
|
|
138
|
-
return { path: binaryPath, version: null };
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
async function checkProviderBinaries() {
|
|
142
|
-
const results = await Promise.all(PROVIDER_BINARIES.map(async ({ label, binary }) => {
|
|
143
|
-
const result = await checkProviderBinary(binary);
|
|
144
|
-
return Object.assign({ label }, result);
|
|
145
|
-
}));
|
|
146
|
-
return results;
|
|
147
|
-
}
|
|
148
|
-
function resolveOwnerLabel(uid, hostname) {
|
|
149
|
-
if (uid === undefined && !hostname) {
|
|
150
|
-
return null;
|
|
151
|
-
}
|
|
152
|
-
const uidPart = uid === undefined ? "?" : String(uid);
|
|
153
|
-
const hostPart = hostname ?? "unknown-host";
|
|
154
|
-
return `${uidPart}@${hostPart}`;
|
|
155
|
-
}
|
|
156
|
-
function classifyDaemonAuthProbeFailure(error) {
|
|
157
|
-
if (!(error instanceof Error))
|
|
158
|
-
return null;
|
|
159
|
-
if (error.message === "Password required")
|
|
160
|
-
return "auth_required";
|
|
161
|
-
if (error.message === "Incorrect password")
|
|
162
|
-
return "auth_failed";
|
|
163
|
-
return null;
|
|
164
|
-
}
|
|
165
|
-
function describeDaemonAuthProbeFailure(host, failure) {
|
|
166
|
-
if (failure === "auth_required") {
|
|
167
|
-
return `Daemon is reachable at ${host} but requires a password. Set PASEO_PASSWORD and retry.`;
|
|
168
|
-
}
|
|
169
|
-
return `Daemon is reachable at ${host} but the supplied password was rejected. Check PASEO_PASSWORD and retry.`;
|
|
170
|
-
}
|
|
171
|
-
async function probeDaemonOverWebsocket(args) {
|
|
172
|
-
const { host, state } = args;
|
|
34
|
+
async function probeDaemonStatus(target, instance, local) {
|
|
35
|
+
let connectedDaemon = "unreachable";
|
|
36
|
+
let note;
|
|
37
|
+
let live = {};
|
|
173
38
|
let client;
|
|
174
39
|
try {
|
|
175
|
-
client = await connectToDaemon({
|
|
40
|
+
client = await connectToDaemon({ target, instance: instance ?? undefined, timeout: 1500 });
|
|
176
41
|
}
|
|
177
42
|
catch (error) {
|
|
178
|
-
const
|
|
179
|
-
if (
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
};
|
|
191
|
-
}
|
|
192
|
-
return { connectedDaemon: "unreachable" };
|
|
193
|
-
}
|
|
194
|
-
const daemonVersion = client.getLastServerInfoMessage()?.version ?? null;
|
|
43
|
+
const failure = buildDaemonConnectionCommandError({ target, error });
|
|
44
|
+
if (target.kind === "endpoint")
|
|
45
|
+
throw failure;
|
|
46
|
+
connectedDaemon = "unreachable";
|
|
47
|
+
if (failure.code === "AUTH_REQUIRED")
|
|
48
|
+
connectedDaemon = "auth_required";
|
|
49
|
+
if (failure.code === "AUTH_FAILED")
|
|
50
|
+
connectedDaemon = "auth_failed";
|
|
51
|
+
note = failure.message;
|
|
52
|
+
}
|
|
53
|
+
if (!client)
|
|
54
|
+
return { connectedDaemon, note };
|
|
195
55
|
try {
|
|
196
|
-
|
|
197
|
-
|
|
56
|
+
let requestError;
|
|
57
|
+
const status = await client.getDaemonStatus({ timeout: 1500 }).catch((error) => {
|
|
58
|
+
requestError = error;
|
|
59
|
+
return null;
|
|
198
60
|
});
|
|
199
|
-
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
61
|
+
// Recheck even when the RPC times out before combining local and live observations.
|
|
62
|
+
const current = target.kind === "instance" ? await readDaemonInstance(target.home) : null;
|
|
63
|
+
if (instance && (!current || !isSameDaemonInstance(instance, current)))
|
|
64
|
+
throw new DaemonInstanceError("DAEMON_REPLACED", "Supervisor exited or was replaced during status observation.");
|
|
65
|
+
const info = client.getLastServerInfoMessage();
|
|
66
|
+
live = { serverId: info?.serverId, daemonVersion: info?.version };
|
|
67
|
+
connectedDaemon = client.isConnected ? "reachable" : "unreachable";
|
|
68
|
+
if (!status) {
|
|
69
|
+
const failure = toCommandError(requestError);
|
|
70
|
+
note = `Status details unavailable (${failure.code}): ${failure.message}`;
|
|
71
|
+
if (target.kind === "endpoint" ||
|
|
72
|
+
!client.isConnected ||
|
|
73
|
+
!(requestError instanceof DaemonConnectionError) ||
|
|
74
|
+
requestError.code !== "DAEMON_REQUEST_TIMEOUT") {
|
|
75
|
+
throw { ...failure, message: note, details: { ...local, ...live, connectedDaemon } };
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
live = {
|
|
80
|
+
...live,
|
|
81
|
+
daemonVersion: status.version,
|
|
82
|
+
workerPid: status.pid,
|
|
83
|
+
daemonNode: status.nodePath,
|
|
84
|
+
providers: status.providers,
|
|
85
|
+
relay: status.relay,
|
|
222
86
|
};
|
|
223
87
|
}
|
|
224
|
-
return {
|
|
225
|
-
connectedDaemon: "reachable",
|
|
226
|
-
daemonVersion: statusPayload.version ?? daemonVersion,
|
|
227
|
-
daemonNodeOverride: statusPayload.nodePath,
|
|
228
|
-
daemonProviders,
|
|
229
|
-
relayStatus,
|
|
230
|
-
};
|
|
231
|
-
}
|
|
232
|
-
catch {
|
|
233
|
-
return {
|
|
234
|
-
connectedDaemon: "reachable",
|
|
235
|
-
daemonVersion,
|
|
236
|
-
note: state.running
|
|
237
|
-
? `Local daemon PID is running but daemon detail request to ${host} failed`
|
|
238
|
-
: `Connected daemon websocket is reachable at ${host} but daemon status request failed`,
|
|
239
|
-
};
|
|
240
88
|
}
|
|
241
89
|
finally {
|
|
242
|
-
await client.close()
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
function applyProbeToStatus(input) {
|
|
246
|
-
const { probe } = input;
|
|
247
|
-
return {
|
|
248
|
-
connectedDaemon: probe.connectedDaemon,
|
|
249
|
-
localDaemon: probe.localDaemonOverride ?? input.localDaemon,
|
|
250
|
-
daemonNode: probe.daemonNodeOverride ?? input.daemonNode,
|
|
251
|
-
daemonVersion: probe.daemonVersion !== undefined ? probe.daemonVersion : input.daemonVersion,
|
|
252
|
-
daemonProviders: probe.daemonProviders ?? input.daemonProviders,
|
|
253
|
-
relayStatus: probe.relayStatus ?? input.relayStatus,
|
|
254
|
-
note: probe.note ? appendNote(input.note, probe.note) : input.note,
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
function resolveServerIdSafely(home) {
|
|
258
|
-
try {
|
|
259
|
-
return { serverId: getOrCreateServerId(home), error: null };
|
|
260
|
-
}
|
|
261
|
-
catch (error) {
|
|
262
|
-
return {
|
|
263
|
-
serverId: null,
|
|
264
|
-
error: `serverId unavailable: ${shortenMessage(normalizeError(error))}`,
|
|
265
|
-
};
|
|
90
|
+
await client.close();
|
|
266
91
|
}
|
|
92
|
+
return { ...live, connectedDaemon, note };
|
|
267
93
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
if (
|
|
272
|
-
|
|
273
|
-
const fromPid = await resolveNodePathFromPid(state.pidInfo.pid);
|
|
274
|
-
return fromPid.nodePath ?? `unknown (${fromPid.error ?? "could not resolve from PID"})`;
|
|
275
|
-
}
|
|
276
|
-
function relayConfigFromLocalState(state) {
|
|
94
|
+
function localStatus(home, instance) {
|
|
95
|
+
const config = resolveConfigFromPersisted(home, readPersistedConfig(home, { defaultsIfMissing: true }), { env: {} });
|
|
96
|
+
let localDaemon = "stopped";
|
|
97
|
+
if (instance)
|
|
98
|
+
localDaemon = instance.listen ? "running" : "not_ready";
|
|
277
99
|
return {
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
}
|
|
285
|
-
export function selectRelayStatus(input) {
|
|
286
|
-
const relay = input.live ?? input.persisted;
|
|
287
|
-
if (!relay.enabled)
|
|
288
|
-
return "disabled";
|
|
289
|
-
const scheme = relay.publicUseTls ? "wss" : "ws";
|
|
290
|
-
return `${scheme}://${relay.publicEndpoint}`;
|
|
291
|
-
}
|
|
292
|
-
export async function runStatusCommand(options, _command) {
|
|
293
|
-
const home = typeof options.home === "string" ? options.home : undefined;
|
|
294
|
-
const state = resolveLocalDaemonState({ home });
|
|
295
|
-
const daemonTarget = state.listen.trim();
|
|
296
|
-
const owner = resolveOwnerLabel(state.pidInfo?.uid, state.pidInfo?.hostname);
|
|
297
|
-
let daemonNode = await resolveDaemonNodeLabel(state);
|
|
298
|
-
const cliNode = process.execPath;
|
|
299
|
-
let localDaemon = state.running ? "running" : "stopped";
|
|
300
|
-
let connectedDaemon = "not_probed";
|
|
301
|
-
let daemonVersion = null;
|
|
302
|
-
let daemonProviders;
|
|
303
|
-
let relayStatus = selectRelayStatus({ persisted: relayConfigFromLocalState(state) });
|
|
304
|
-
let note;
|
|
305
|
-
if (!state.running && state.stalePidFile && state.pidInfo) {
|
|
306
|
-
localDaemon = "stale_pid";
|
|
307
|
-
note = `Stale PID file found for PID ${state.pidInfo.pid}`;
|
|
308
|
-
}
|
|
309
|
-
if (daemonTarget) {
|
|
310
|
-
const probe = await probeDaemonOverWebsocket({ host: daemonTarget, state });
|
|
311
|
-
({
|
|
312
|
-
connectedDaemon,
|
|
313
|
-
localDaemon,
|
|
314
|
-
daemonNode,
|
|
315
|
-
daemonVersion,
|
|
316
|
-
daemonProviders,
|
|
317
|
-
relayStatus,
|
|
318
|
-
note,
|
|
319
|
-
} = applyProbeToStatus({
|
|
320
|
-
probe,
|
|
321
|
-
connectedDaemon,
|
|
322
|
-
localDaemon,
|
|
323
|
-
daemonNode,
|
|
324
|
-
daemonVersion,
|
|
325
|
-
daemonProviders,
|
|
326
|
-
relayStatus,
|
|
327
|
-
note,
|
|
328
|
-
}));
|
|
329
|
-
}
|
|
330
|
-
const cliVersion = resolveCliVersion();
|
|
331
|
-
const serverIdResult = resolveServerIdSafely(state.home);
|
|
332
|
-
const serverId = serverIdResult.serverId;
|
|
333
|
-
if (serverIdResult.error) {
|
|
334
|
-
note = appendNote(note, serverIdResult.error);
|
|
335
|
-
}
|
|
336
|
-
const providers = daemonProviders ?? (await checkProviderBinaries());
|
|
337
|
-
const daemonStatus = {
|
|
338
|
-
serverId,
|
|
100
|
+
home,
|
|
101
|
+
pid: instance?.pid ?? null,
|
|
102
|
+
startedAt: instance?.startedAt ?? null,
|
|
103
|
+
listen: instance?.listen ?? null,
|
|
104
|
+
hostname: instance?.hostname ?? null,
|
|
105
|
+
configuredListen: config.listen,
|
|
339
106
|
localDaemon,
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
listen: state.listen,
|
|
343
|
-
relay: relayStatus,
|
|
344
|
-
hostname: state.pidInfo?.hostname ?? null,
|
|
345
|
-
pid: state.pidInfo?.pid ?? null,
|
|
346
|
-
startedAt: state.pidInfo?.startedAt ?? null,
|
|
347
|
-
owner,
|
|
348
|
-
logPath: state.logPath,
|
|
349
|
-
daemonNode,
|
|
350
|
-
cliNode,
|
|
351
|
-
cliVersion,
|
|
352
|
-
daemonVersion,
|
|
353
|
-
desktopManaged: state.pidInfo?.desktopManaged === true,
|
|
354
|
-
providers,
|
|
355
|
-
note,
|
|
356
|
-
};
|
|
357
|
-
return {
|
|
358
|
-
type: "list",
|
|
359
|
-
data: toStatusRows(daemonStatus),
|
|
360
|
-
schema: createStatusSchema(daemonStatus),
|
|
107
|
+
desktopManaged: instance?.desktopManaged === true,
|
|
108
|
+
logPath: daemonLogPath(home),
|
|
361
109
|
};
|
|
362
110
|
}
|
|
363
111
|
//# sourceMappingURL=status.js.map
|
|
@@ -1,15 +1,20 @@
|
|
|
1
|
-
import
|
|
2
|
-
import type
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { type CommandOptions } from "../../output/index.js";
|
|
3
|
+
export declare function daemonStopCommand(): Command;
|
|
4
|
+
export declare function runStopCommand(options: CommandOptions, _command: Command): Promise<{
|
|
5
|
+
type: "single";
|
|
6
|
+
data: {
|
|
7
|
+
action: string;
|
|
8
|
+
home?: string;
|
|
9
|
+
host?: string;
|
|
10
|
+
pid?: number | null;
|
|
11
|
+
forced?: boolean;
|
|
12
|
+
usedLifecycleRpc?: boolean;
|
|
13
|
+
};
|
|
14
|
+
schema: {
|
|
15
|
+
idField: "action";
|
|
16
|
+
columns: never[];
|
|
17
|
+
renderHuman: () => string;
|
|
18
|
+
};
|
|
19
|
+
}>;
|
|
15
20
|
//# sourceMappingURL=stop.d.ts.map
|
|
@@ -1,60 +1,58 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
};
|
|
15
|
-
function parseSecondsOption(raw, fallbackMs, label) {
|
|
16
|
-
if (typeof raw !== "string" || raw.trim().length === 0) {
|
|
17
|
-
return fallbackMs;
|
|
18
|
-
}
|
|
19
|
-
const seconds = Number(raw);
|
|
20
|
-
if (!Number.isFinite(seconds) || seconds <= 0) {
|
|
21
|
-
const error = {
|
|
22
|
-
code: "INVALID_TIMEOUT",
|
|
23
|
-
message: `Invalid ${label} value: ${raw}`,
|
|
24
|
-
details: `${label} must be a positive number of seconds`,
|
|
25
|
-
};
|
|
26
|
-
throw error;
|
|
27
|
-
}
|
|
28
|
-
return Math.ceil(seconds * 1000);
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { stopDaemonInstance } from "@camerontaylor/paseo-server";
|
|
3
|
+
import { connectToDaemon } from "../../utils/client.js";
|
|
4
|
+
import { withOutput } from "../../output/index.js";
|
|
5
|
+
import { addJsonAndDaemonHostOptions } from "../../utils/command-options.js";
|
|
6
|
+
import { describeDaemonTarget } from "../../utils/daemon-target.js";
|
|
7
|
+
import { parseTimeoutMs } from "./local-daemon.js";
|
|
8
|
+
export function daemonStopCommand() {
|
|
9
|
+
return addJsonAndDaemonHostOptions(new Command("stop").description("Stop the selected daemon"))
|
|
10
|
+
.option("--timeout <seconds>", "Graceful exit deadline (default: 15)")
|
|
11
|
+
.option("--force", "Permit forced local process cleanup")
|
|
12
|
+
.option("--kill-timeout <seconds>", "Forced exit deadline (default: 3)")
|
|
13
|
+
.action(withOutput(runStopCommand));
|
|
29
14
|
}
|
|
30
15
|
export async function runStopCommand(options, _command) {
|
|
31
|
-
const
|
|
32
|
-
const
|
|
33
|
-
const
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
schema: stopResultSchema,
|
|
16
|
+
const target = options.daemonTarget;
|
|
17
|
+
const timeoutMs = parseTimeoutMs(options.timeout, 15000);
|
|
18
|
+
const deadline = Date.now() + timeoutMs;
|
|
19
|
+
const remaining = () => Math.max(1, deadline - Date.now());
|
|
20
|
+
const requestShutdown = async (instance) => {
|
|
21
|
+
const client = await connectToDaemon({ target, timeout: remaining(), instance });
|
|
22
|
+
try {
|
|
23
|
+
await client.shutdownServer({ timeout: remaining() });
|
|
24
|
+
}
|
|
25
|
+
finally {
|
|
26
|
+
await client.close();
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
if (target.kind === "endpoint" && options.force)
|
|
30
|
+
throw {
|
|
31
|
+
code: "INVALID_OPTIONS",
|
|
32
|
+
message: "--force requires a local --home; an endpoint gives no remote process authority.",
|
|
49
33
|
};
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
34
|
+
const result = target.kind === "instance"
|
|
35
|
+
? {
|
|
36
|
+
...(await stopDaemonInstance(target.home, {
|
|
37
|
+
force: options.force === true,
|
|
38
|
+
timeoutMs,
|
|
39
|
+
killTimeoutMs: parseTimeoutMs(options.killTimeout, 3000),
|
|
40
|
+
requestShutdown,
|
|
41
|
+
})),
|
|
42
|
+
home: target.home,
|
|
43
|
+
}
|
|
44
|
+
: (await requestShutdown(),
|
|
45
|
+
{ action: "shutdown_requested", host: describeDaemonTarget(target) });
|
|
46
|
+
return {
|
|
47
|
+
type: "single",
|
|
48
|
+
data: result,
|
|
49
|
+
schema: {
|
|
50
|
+
idField: "action",
|
|
51
|
+
columns: [],
|
|
52
|
+
renderHuman: () => target.kind === "endpoint"
|
|
53
|
+
? "Shutdown requested; remote process exit was not verified."
|
|
54
|
+
: `${result.action.replaceAll("_", " ")}: ${target.home}`,
|
|
55
|
+
},
|
|
56
|
+
};
|
|
59
57
|
}
|
|
60
58
|
//# sourceMappingURL=stop.js.map
|
|
@@ -33,7 +33,7 @@ async function runCreateHeartbeat(prompt, options, _command) {
|
|
|
33
33
|
if (!cron) {
|
|
34
34
|
throw new Error("--cron is required");
|
|
35
35
|
}
|
|
36
|
-
const { client } = await connectScheduleClient(options.
|
|
36
|
+
const { client } = await connectScheduleClient(options.daemonTarget);
|
|
37
37
|
try {
|
|
38
38
|
const maxRuns = options.maxRuns ? Number.parseInt(options.maxRuns, 10) : undefined;
|
|
39
39
|
if (maxRuns !== undefined && (!Number.isSafeInteger(maxRuns) || maxRuns <= 0)) {
|
|
@@ -71,7 +71,7 @@ async function runUpdateHeartbeat(id, options, _command) {
|
|
|
71
71
|
if (!cron) {
|
|
72
72
|
throw new Error("--cron is required");
|
|
73
73
|
}
|
|
74
|
-
const { client } = await connectScheduleClient(options.
|
|
74
|
+
const { client } = await connectScheduleClient(options.daemonTarget);
|
|
75
75
|
try {
|
|
76
76
|
await requireOwnedHeartbeat(client, id, agentId);
|
|
77
77
|
const payload = await client.scheduleUpdate({
|
|
@@ -96,7 +96,7 @@ async function runUpdateHeartbeat(id, options, _command) {
|
|
|
96
96
|
}
|
|
97
97
|
async function runDeleteHeartbeat(id, options, _command) {
|
|
98
98
|
const agentId = requireCallerAgentId();
|
|
99
|
-
const { client } = await connectScheduleClient(options.
|
|
99
|
+
const { client } = await connectScheduleClient(options.daemonTarget);
|
|
100
100
|
try {
|
|
101
101
|
await requireOwnedHeartbeat(client, id, agentId);
|
|
102
102
|
const payload = await client.scheduleDelete({ id });
|
|
@@ -6,6 +6,7 @@ import { type HubReporter } from "./reporter.js";
|
|
|
6
6
|
interface HubConnectOptions {
|
|
7
7
|
apiKey?: string;
|
|
8
8
|
host?: string;
|
|
9
|
+
daemonTarget: import("../../utils/daemon-target.js").DaemonTarget;
|
|
9
10
|
json?: boolean;
|
|
10
11
|
permission?: readonly string[];
|
|
11
12
|
permissions?: readonly string[];
|
|
@@ -16,7 +16,7 @@ export async function runHubConnect(originInput, options, dependencies) {
|
|
|
16
16
|
const credential = resolveHubCredential({ ...resolution, origin });
|
|
17
17
|
const token = await dependencies.hub.issueEnrollmentToken(origin, credential);
|
|
18
18
|
const permissions = options.permissions ?? options.permission ?? [];
|
|
19
|
-
return withHubDaemon(dependencies.daemon, options.
|
|
19
|
+
return withHubDaemon(dependencies.daemon, options.daemonTarget, async (daemon) => {
|
|
20
20
|
const response = await daemon.connectHub(origin, token, permissions);
|
|
21
21
|
if (response.status.hubOrigin !== null &&
|
|
22
22
|
!samePermissions(response.status.permissions, permissions)) {
|