@edspencer/paddock 0.79.1 → 0.81.0
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/package.json +1 -1
- package/packages/server/dist/app.d.ts +7 -0
- package/packages/server/dist/app.js +19 -15
- package/packages/server/dist/auth.d.ts +3 -2
- package/packages/server/dist/auth.js +11 -0
- package/packages/server/dist/background-live.d.ts +17 -0
- package/packages/server/dist/background-live.js +24 -0
- package/packages/server/dist/busy.d.ts +96 -0
- package/packages/server/dist/busy.js +88 -0
- package/packages/server/dist/cli/args.d.ts +63 -7
- package/packages/server/dist/cli/args.js +227 -20
- package/packages/server/dist/cli/busy-command.d.ts +58 -0
- package/packages/server/dist/cli/busy-command.js +108 -0
- package/packages/server/dist/cli/busy.d.ts +69 -0
- package/packages/server/dist/cli/busy.js +150 -0
- package/packages/server/dist/cli/exit-codes.d.ts +49 -0
- package/packages/server/dist/cli/exit-codes.js +48 -0
- package/packages/server/dist/cli/output.d.ts +52 -0
- package/packages/server/dist/cli/output.js +84 -0
- package/packages/server/dist/cli/paddock.js +98 -37
- package/packages/server/dist/cli/service/backend.d.ts +48 -4
- package/packages/server/dist/cli/service/index.d.ts +68 -2
- package/packages/server/dist/cli/service/index.js +321 -60
- package/packages/server/dist/cli/service/launchd.d.ts +23 -1
- package/packages/server/dist/cli/service/launchd.js +145 -25
- package/packages/server/dist/cli/service/logs.d.ts +39 -0
- package/packages/server/dist/cli/service/logs.js +115 -0
- package/packages/server/dist/cli/service/path-env.d.ts +27 -0
- package/packages/server/dist/cli/service/path-env.js +120 -0
- package/packages/server/dist/cli/service/ready.d.ts +22 -0
- package/packages/server/dist/cli/service/ready.js +30 -0
- package/packages/server/dist/cli/service/spec.d.ts +45 -6
- package/packages/server/dist/cli/service/spec.js +34 -7
- package/packages/server/dist/cli/service/systemd.d.ts +47 -4
- package/packages/server/dist/cli/service/systemd.js +127 -22
- package/packages/server/dist/herdctl.d.ts +19 -0
- package/packages/server/dist/herdctl.js +26 -0
- package/packages/server/dist/management-mcp-server.d.ts +21 -3
- package/packages/server/dist/management-mcp-server.js +4 -13
- package/packages/server/dist/routes/meta.js +56 -3
- package/packages/server/dist/runtime-file.d.ts +26 -0
- package/packages/server/dist/runtime-file.js +91 -0
- package/packages/server/dist/service-log.d.ts +29 -0
- package/packages/server/dist/service-log.js +104 -0
- package/packages/server/dist/start.d.ts +33 -0
- package/packages/server/dist/start.js +126 -8
- package/packages/server/dist/version.d.ts +3 -0
- package/packages/server/dist/version.js +32 -0
- package/packages/server/dist/ws-context.d.ts +9 -0
- package/packages/server/dist/ws-turn.js +1 -0
- package/packages/server/package.json +1 -1
- package/packages/web/dist/assets/{DeleteProjectDialog-BcxJMA1w.js → DeleteProjectDialog-D8oIJ8HC.js} +1 -1
- package/packages/web/dist/assets/DiscoverPage-CQlzxsn4.js +2 -0
- package/packages/web/dist/assets/{DiscoverView-DOmTmiVv.js → DiscoverView-o0KCr3aB.js} +1 -1
- package/packages/web/dist/assets/{InstanceConfigPage-DWLQqHW0.js → InstanceConfigPage-RSJz7oL3.js} +1 -1
- package/packages/web/dist/assets/{MarkdownRenderer-Y25JWlMR.js → MarkdownRenderer-39mafH8b.js} +1 -1
- package/packages/web/dist/assets/{ProjectRedirect-CCI1VWM6.js → ProjectRedirect-Bq7GVguY.js} +1 -1
- package/packages/web/dist/assets/{ProjectView-DzfVhjya.js → ProjectView-BoBIru1g.js} +3 -3
- package/packages/web/dist/assets/{ProjectsGrid-yIyyb2HA.js → ProjectsGrid-mJ4gqbr6.js} +1 -1
- package/packages/web/dist/assets/{RootHome-x0Om9-Fd.js → RootHome-C0kwFyYp.js} +1 -1
- package/packages/web/dist/assets/{Surfaces-wbcMQIsK.js → Surfaces-MkhXlB8z.js} +1 -1
- package/packages/web/dist/assets/{arc-tWN7FYIq.js → arc-DExlwAwO.js} +1 -1
- package/packages/web/dist/assets/{architectureDiagram-3BPJPVTR-Ccj6Vlyy.js → architectureDiagram-3BPJPVTR-CZly3W79.js} +1 -1
- package/packages/web/dist/assets/{blockDiagram-GPEHLZMM-B7iGXwqY.js → blockDiagram-GPEHLZMM-AgES73i3.js} +1 -1
- package/packages/web/dist/assets/{c4Diagram-AAUBKEIU-SkLKFSso.js → c4Diagram-AAUBKEIU-N9mD8Pfk.js} +1 -1
- package/packages/web/dist/assets/channel-ExsAG7qT.js +2 -0
- package/packages/web/dist/assets/{chunk-2J33WTMH-BrS8C-YK.js → chunk-2J33WTMH-Dsx_kfUl.js} +1 -1
- package/packages/web/dist/assets/{chunk-4BX2VUAB-BUR6nG8w.js → chunk-4BX2VUAB-BQjqcjPj.js} +1 -1
- package/packages/web/dist/assets/{chunk-55IACEB6-BRBNJb-u.js → chunk-55IACEB6-CCxpYQpY.js} +1 -1
- package/packages/web/dist/assets/{chunk-727SXJPM-DKeNPTXz.js → chunk-727SXJPM-DpFRdnUa.js} +1 -1
- package/packages/web/dist/assets/{chunk-AQP2D5EJ-BOS1K-DX.js → chunk-AQP2D5EJ-D2BAlzoO.js} +1 -1
- package/packages/web/dist/assets/{chunk-FMBD7UC4-D3PZL8fo.js → chunk-FMBD7UC4-BFaMRzUF.js} +1 -1
- package/packages/web/dist/assets/{chunk-ND2GUHAM-DA9Y77Zj.js → chunk-ND2GUHAM-B9NGJ9xZ.js} +1 -1
- package/packages/web/dist/assets/{chunk-QZHKN3VN-wPph-weq.js → chunk-QZHKN3VN-CF1IHyMS.js} +1 -1
- package/packages/web/dist/assets/classDiagram-4FO5ZUOK-si7g9okq.js +2 -0
- package/packages/web/dist/assets/classDiagram-v2-Q7XG4LA2-si7g9okq.js +2 -0
- package/packages/web/dist/assets/{core-I1PQdGMJ.js → core-XjElsvQ7.js} +1 -1
- package/packages/web/dist/assets/{cose-bilkent-S5V4N54A-BA7N2vVK.js → cose-bilkent-S5V4N54A-CF972Flx.js} +1 -1
- package/packages/web/dist/assets/{dagre-BM42HDAG-DKpON5p2.js → dagre-BM42HDAG-DsneI8YE.js} +1 -1
- package/packages/web/dist/assets/{diagram-2AECGRRQ-Db0I-eEL.js → diagram-2AECGRRQ-BHyrVzIk.js} +1 -1
- package/packages/web/dist/assets/{diagram-5GNKFQAL-BEuqOTfn.js → diagram-5GNKFQAL-Dj4q8xEi.js} +1 -1
- package/packages/web/dist/assets/{diagram-KO2AKTUF-CnyX_0Xk.js → diagram-KO2AKTUF-BHe3Puv_.js} +1 -1
- package/packages/web/dist/assets/{diagram-LMA3HP47-CYvknzDd.js → diagram-LMA3HP47-wzt6G2FE.js} +1 -1
- package/packages/web/dist/assets/{diagram-OG6HWLK6-DlgKxbYz.js → diagram-OG6HWLK6-CZMbMRJq.js} +1 -1
- package/packages/web/dist/assets/{documentTitle-Cvob-HVs.js → documentTitle-BwTIA8rI.js} +1 -1
- package/packages/web/dist/assets/{erDiagram-TEJ5UH35-BkO5JmiM.js → erDiagram-TEJ5UH35-Dtu14BL1.js} +1 -1
- package/packages/web/dist/assets/{flowDiagram-I6XJVG4X-DG0o_zc1.js → flowDiagram-I6XJVG4X-T34ql9j0.js} +1 -1
- package/packages/web/dist/assets/{ganttDiagram-6RSMTGT7-Bs71No2S.js → ganttDiagram-6RSMTGT7-DQLOeCXK.js} +1 -1
- package/packages/web/dist/assets/{gitGraphDiagram-PVQCEYII-CxODhRKd.js → gitGraphDiagram-PVQCEYII-Bk11zea6.js} +1 -1
- package/packages/web/dist/assets/{index-C4tsPSvG.js → index-CvaWErFv.js} +2 -2
- package/packages/web/dist/assets/{infoDiagram-5YYISTIA-C412TQaw.js → infoDiagram-5YYISTIA-CsE83lu0.js} +1 -1
- package/packages/web/dist/assets/{ishikawaDiagram-YF4QCWOH-DUlcZXQQ.js → ishikawaDiagram-YF4QCWOH-DNVgA93n.js} +1 -1
- package/packages/web/dist/assets/{journeyDiagram-JHISSGLW-iL0klVhn.js → journeyDiagram-JHISSGLW-CLpSNjM3.js} +1 -1
- package/packages/web/dist/assets/{kanban-definition-UN3LZRKU-CO02Zl_Q.js → kanban-definition-UN3LZRKU-BRzuXcKr.js} +1 -1
- package/packages/web/dist/assets/{linear-DtmLY8AG.js → linear-BxOawONt.js} +1 -1
- package/packages/web/dist/assets/{mermaid.core-Dvzxug80.js → mermaid.core-Dvs1QCcz.js} +4 -4
- package/packages/web/dist/assets/{mindmap-definition-RKZ34NQL-q950AAWL.js → mindmap-definition-RKZ34NQL-Dmt7pYwF.js} +1 -1
- package/packages/web/dist/assets/{pieDiagram-4H26LBE5-BVrv8A-H.js → pieDiagram-4H26LBE5-BHiZaJD-.js} +1 -1
- package/packages/web/dist/assets/{quadrantDiagram-W4KKPZXB-Bp5D2MFq.js → quadrantDiagram-W4KKPZXB-U0A1noRE.js} +1 -1
- package/packages/web/dist/assets/{requirementDiagram-4Y6WPE33-yZfQU4Ix.js → requirementDiagram-4Y6WPE33-CZLsr6yi.js} +1 -1
- package/packages/web/dist/assets/{sankeyDiagram-5OEKKPKP-5R8_OZir.js → sankeyDiagram-5OEKKPKP-BTlLGILU.js} +1 -1
- package/packages/web/dist/assets/{sequenceDiagram-3UESZ5HK-C3A9rinG.js → sequenceDiagram-3UESZ5HK-DpSS_Gb1.js} +1 -1
- package/packages/web/dist/assets/{stateDiagram-AJRCARHV-Cshrt_XB.js → stateDiagram-AJRCARHV-D9srE-tV.js} +1 -1
- package/packages/web/dist/assets/stateDiagram-v2-BHNVJYJU-AxWqsDh-.js +2 -0
- package/packages/web/dist/assets/{timeline-definition-PNZ67QCA-kNkiddCX.js → timeline-definition-PNZ67QCA-BYQ5CKab.js} +1 -1
- package/packages/web/dist/assets/{vennDiagram-CIIHVFJN-BiNJm17g.js → vennDiagram-CIIHVFJN-B4W2CtSV.js} +1 -1
- package/packages/web/dist/assets/{wardley-L42UT6IY-CjExbCuX.js → wardley-L42UT6IY--XUV7GvD.js} +1 -1
- package/packages/web/dist/assets/{wardleyDiagram-YWT4CUSO-CMn3z-G2.js → wardleyDiagram-YWT4CUSO-Bl2VIEGY.js} +1 -1
- package/packages/web/dist/assets/{xychartDiagram-2RQKCTM6-B-UhnooW.js → xychartDiagram-2RQKCTM6-Bj2H9Rcf.js} +1 -1
- package/packages/web/dist/index.html +1 -1
- package/packages/web/dist/sw.js +1 -1
- package/packages/web/dist/assets/DiscoverPage-CSPRrFVk.js +0 -2
- package/packages/web/dist/assets/channel-B2NSmlBB.js +0 -2
- package/packages/web/dist/assets/classDiagram-4FO5ZUOK-B3tLFeZC.js +0 -2
- package/packages/web/dist/assets/classDiagram-v2-Q7XG4LA2-B3tLFeZC.js +0 -2
- package/packages/web/dist/assets/stateDiagram-v2-BHNVJYJU-DgiazF78.js +0 -2
|
@@ -23,8 +23,34 @@
|
|
|
23
23
|
* the need for the guard entirely, so there is no condition left to get wrong on
|
|
24
24
|
* the next shim, platform or package manager. `paddock.ts` now always runs.
|
|
25
25
|
*/
|
|
26
|
-
|
|
26
|
+
import { EXIT } from "./exit-codes.js";
|
|
27
|
+
/**
|
|
28
|
+
* A failure with an exit code attached (SPEC §2).
|
|
29
|
+
*
|
|
30
|
+
* Thrown rather than exiting, so parsing stays testable. The code travels with
|
|
31
|
+
* the error because the thing that KNOWS which code applies is whatever threw —
|
|
32
|
+
* `main` cannot tell an unsupported platform (a failure, 1) from a misspelled
|
|
33
|
+
* flag (usage, 2) by looking at a string.
|
|
34
|
+
*/
|
|
27
35
|
export class CliError extends Error {
|
|
36
|
+
exitCode;
|
|
37
|
+
constructor(message, exitCode = EXIT.failure) {
|
|
38
|
+
super(message);
|
|
39
|
+
this.exitCode = exitCode;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* An unknown command, an unknown flag, or a bad value: exit 2 (SPEC §2).
|
|
44
|
+
*
|
|
45
|
+
* **This is a behaviour change, and a deliberate one.** Every one of these used
|
|
46
|
+
* to exit 1, which is also what a genuine failure exits — so a script could not
|
|
47
|
+
* tell "you typed it wrong" from "it tried and could not". The contract splits
|
|
48
|
+
* them, and a script that tested `$? -eq 1` for a typo now needs `-eq 2`.
|
|
49
|
+
*/
|
|
50
|
+
export class UsageError extends CliError {
|
|
51
|
+
constructor(message) {
|
|
52
|
+
super(message, EXIT.usage);
|
|
53
|
+
}
|
|
28
54
|
}
|
|
29
55
|
export const MIN_NODE_MAJOR = 22;
|
|
30
56
|
/**
|
|
@@ -43,6 +69,7 @@ export const SERVICE_ACTIONS = [
|
|
|
43
69
|
"start",
|
|
44
70
|
"stop",
|
|
45
71
|
"restart",
|
|
72
|
+
"logs",
|
|
46
73
|
];
|
|
47
74
|
/**
|
|
48
75
|
* What `paddock config` can be asked to do (#878).
|
|
@@ -53,8 +80,15 @@ export const SERVICE_ACTIONS = [
|
|
|
53
80
|
* the command that makes it unnecessary.
|
|
54
81
|
*/
|
|
55
82
|
export const CONFIG_ACTIONS = ["show", "eject"];
|
|
56
|
-
/**
|
|
57
|
-
|
|
83
|
+
/**
|
|
84
|
+
* The leading words {@link parseCommand} recognises. Anything else is a flag.
|
|
85
|
+
*
|
|
86
|
+
* `serve` is the contract's name for running in the foreground (SPEC §1), and
|
|
87
|
+
* `start` is kept as an alias **for good** — the spec says so explicitly for
|
|
88
|
+
* Paddock and Managers, every existing service unit's `ExecStart` names it, and
|
|
89
|
+
* it is in two years of other people's scripts. They parse to the same command.
|
|
90
|
+
*/
|
|
91
|
+
export const VERBS = ["serve", "start", "service", "config", "busy"];
|
|
58
92
|
/**
|
|
59
93
|
* Split an action off a verb's argv, then parse what follows as flags.
|
|
60
94
|
*
|
|
@@ -67,7 +101,7 @@ function parseAction(verb, actions, rest) {
|
|
|
67
101
|
let action;
|
|
68
102
|
if (head !== undefined && !head.startsWith("-")) {
|
|
69
103
|
if (!actions.includes(head)) {
|
|
70
|
-
throw new
|
|
104
|
+
throw new UsageError(`unknown ${verb} action: ${head}\n` +
|
|
71
105
|
`Expected one of: ${actions.join(", ")}.\n` +
|
|
72
106
|
`Run \`paddock ${verb} --help\` for usage.`);
|
|
73
107
|
}
|
|
@@ -76,7 +110,7 @@ function parseAction(verb, actions, rest) {
|
|
|
76
110
|
const opts = parseArgs(action === undefined ? rest : rest.slice(1));
|
|
77
111
|
// `--help` wins over a missing action: asking for usage is not a usage error.
|
|
78
112
|
if (action === undefined && !opts.help) {
|
|
79
|
-
throw new
|
|
113
|
+
throw new UsageError(`\`paddock ${verb}\` needs an action: ${actions.join(", ")}.\n` +
|
|
80
114
|
`Run \`paddock ${verb} --help\` for usage.`);
|
|
81
115
|
}
|
|
82
116
|
return { action, opts };
|
|
@@ -108,6 +142,30 @@ function parseAction(verb, actions, rest) {
|
|
|
108
142
|
* and load-bearing; it was only the example's error that was misdescribed.
|
|
109
143
|
*/
|
|
110
144
|
export function parseCommand(argv) {
|
|
145
|
+
const command = dispatch(argv);
|
|
146
|
+
// `--force` overrides the busy check (SPEC §1, §4) and nothing else, so it
|
|
147
|
+
// only means anything on the two commands that run one. Rejecting it
|
|
148
|
+
// everywhere else, rather than parsing and dropping it, is the difference
|
|
149
|
+
// between being told and guessing: the two readings of a quiet
|
|
150
|
+
// `service uninstall --force` are "it overrode something" and "it did
|
|
151
|
+
// nothing", and the exit code is 0 either way.
|
|
152
|
+
//
|
|
153
|
+
// Checked here, once, rather than in each command: a second copy of this rule
|
|
154
|
+
// is a second chance for the two to drift apart about what `--force` means.
|
|
155
|
+
if (command.opts.force === true &&
|
|
156
|
+
// `--help` wins, the same way it wins over a missing action above: asking
|
|
157
|
+
// for the usage is not a usage error.
|
|
158
|
+
!command.opts.help &&
|
|
159
|
+
!(command.verb === "service" && (command.action === "stop" || command.action === "restart"))) {
|
|
160
|
+
const what = command.verb === "service" && command.action !== undefined
|
|
161
|
+
? `\`service ${command.action}\``
|
|
162
|
+
: `\`${command.verb}\``;
|
|
163
|
+
throw new UsageError(`--force applies to \`service stop\` and \`service restart\` only, not ${what}.\n` +
|
|
164
|
+
"It overrides the busy check, and those are the two commands that run one.");
|
|
165
|
+
}
|
|
166
|
+
return command;
|
|
167
|
+
}
|
|
168
|
+
function dispatch(argv) {
|
|
111
169
|
const [first, ...rest] = argv;
|
|
112
170
|
if (first === "service") {
|
|
113
171
|
return { verb: "service", ...parseAction("service", SERVICE_ACTIONS, rest) };
|
|
@@ -115,9 +173,15 @@ export function parseCommand(argv) {
|
|
|
115
173
|
if (first === "config") {
|
|
116
174
|
return { verb: "config", ...parseAction("config", CONFIG_ACTIONS, rest) };
|
|
117
175
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
176
|
+
// `busy` (SPEC §1, §4). A top-level verb rather than a `service` action
|
|
177
|
+
// because its whole point is to be askable where there is no service to ask:
|
|
178
|
+
// a Docker container, a Watchtower pre-update hook, an Ansible task.
|
|
179
|
+
if (first === "busy")
|
|
180
|
+
return { verb: "busy", opts: parseArgs(rest) };
|
|
181
|
+
// `serve` and `start` are one command: see VERBS.
|
|
182
|
+
if (first === "serve" || first === "start")
|
|
183
|
+
return { verb: "serve", opts: parseArgs(rest) };
|
|
184
|
+
return { verb: "serve", opts: parseArgs(argv) };
|
|
121
185
|
}
|
|
122
186
|
export function parseArgs(argv) {
|
|
123
187
|
const opts = {
|
|
@@ -131,7 +195,7 @@ export function parseArgs(argv) {
|
|
|
131
195
|
const next = () => {
|
|
132
196
|
const v = argv[++i];
|
|
133
197
|
if (v === undefined)
|
|
134
|
-
throw new
|
|
198
|
+
throw new UsageError(`${arg} needs a value`);
|
|
135
199
|
return v;
|
|
136
200
|
};
|
|
137
201
|
switch (arg) {
|
|
@@ -148,7 +212,7 @@ export function parseArgs(argv) {
|
|
|
148
212
|
// into a hard error.
|
|
149
213
|
const v = next();
|
|
150
214
|
if (v !== "" && (!/^\d+$/.test(v) || Number(v) < 1 || Number(v) > 65535)) {
|
|
151
|
-
throw new
|
|
215
|
+
throw new UsageError(`${arg} needs a number between 1 and 65535, got: ${v}`);
|
|
152
216
|
}
|
|
153
217
|
opts.port = v;
|
|
154
218
|
break;
|
|
@@ -185,6 +249,25 @@ export function parseArgs(argv) {
|
|
|
185
249
|
case "--include-env":
|
|
186
250
|
opts.includeEnv = true;
|
|
187
251
|
break;
|
|
252
|
+
// `service stop` / `service restart`: override the busy check (SPEC §4).
|
|
253
|
+
case "--force":
|
|
254
|
+
opts.force = true;
|
|
255
|
+
break;
|
|
256
|
+
// `service logs` flags, the names `tail`, `journalctl` and every other
|
|
257
|
+
// log reader uses — this is not the place to be original.
|
|
258
|
+
case "-f":
|
|
259
|
+
case "--follow":
|
|
260
|
+
opts.follow = true;
|
|
261
|
+
break;
|
|
262
|
+
case "-n":
|
|
263
|
+
case "--lines": {
|
|
264
|
+
const v = next();
|
|
265
|
+
if (!/^\d+$/.test(v) || Number(v) < 1) {
|
|
266
|
+
throw new UsageError(`${arg} needs a positive number of lines, got: ${v}`);
|
|
267
|
+
}
|
|
268
|
+
opts.lines = Number(v);
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
188
271
|
case "-h":
|
|
189
272
|
case "--help":
|
|
190
273
|
opts.help = true;
|
|
@@ -194,7 +277,7 @@ export function parseArgs(argv) {
|
|
|
194
277
|
opts.version = true;
|
|
195
278
|
break;
|
|
196
279
|
default:
|
|
197
|
-
throw new
|
|
280
|
+
throw new UsageError(`unknown option: ${arg}`);
|
|
198
281
|
}
|
|
199
282
|
}
|
|
200
283
|
return opts;
|
|
@@ -232,14 +315,38 @@ export function explainListenError(err, host, port) {
|
|
|
232
315
|
}
|
|
233
316
|
return String(err?.message ?? err);
|
|
234
317
|
}
|
|
318
|
+
/**
|
|
319
|
+
* The command list, on its own.
|
|
320
|
+
*
|
|
321
|
+
* Printed after a usage error, because SPEC §2 says the usage follows the
|
|
322
|
+
* message — and because {@link USAGE} is a thousand words of guide that would
|
|
323
|
+
* bury the one line explaining what went wrong. The full text is one
|
|
324
|
+
* `--help` away, and the last line here says so.
|
|
325
|
+
*/
|
|
326
|
+
export const USAGE_SYNOPSIS = `Usage
|
|
327
|
+
paddock [serve] [options] run the server in this terminal
|
|
328
|
+
paddock service <action> [options] install|uninstall|status|start|stop|restart|logs
|
|
329
|
+
paddock busy [--json] is the running instance busy?
|
|
330
|
+
paddock config <show|eject> [options] what this instance's config resolved to
|
|
331
|
+
paddock -v | --version print the version and exit
|
|
332
|
+
paddock -h | --help the full help`;
|
|
235
333
|
export const USAGE = `paddock — run a Paddock instance locally
|
|
236
334
|
|
|
237
335
|
Usage
|
|
238
|
-
npx @edspencer/paddock [options]
|
|
239
|
-
paddock
|
|
240
|
-
|
|
241
|
-
|
|
336
|
+
npx @edspencer/paddock [options] run the server (the default)
|
|
337
|
+
paddock serve [options] the same thing, said out loud
|
|
338
|
+
(\`paddock start\` is a permanent alias)
|
|
339
|
+
paddock service install [options] run it in the background from login
|
|
340
|
+
paddock service uninstall stop it and remove the unit
|
|
341
|
+
paddock service status [--json] installed? running? which version?
|
|
342
|
+
paddock service start start an installed service
|
|
343
|
+
paddock service stop [--force] stop it, leave it installed
|
|
344
|
+
paddock service restart [--force] stop it and start it again
|
|
345
|
+
paddock service logs [-f] [-n <lines>] the service's log
|
|
242
346
|
(\`paddock service --help\`)
|
|
347
|
+
paddock busy [--json] is it busy? exit 0 no, 75 yes, 3
|
|
348
|
+
nothing answering
|
|
349
|
+
(\`paddock busy --help\`)
|
|
243
350
|
paddock config show [--resolved] what this instance's config resolved
|
|
244
351
|
to, and where each value came from
|
|
245
352
|
paddock config eject [--write] freeze that resolution into
|
|
@@ -255,6 +362,14 @@ Options
|
|
|
255
362
|
-v, --version Print the Paddock version and exit
|
|
256
363
|
-h, --help Show this help
|
|
257
364
|
|
|
365
|
+
Exit codes
|
|
366
|
+
0 done; running and answering; not busy
|
|
367
|
+
1 failed
|
|
368
|
+
2 usage: an unknown command or flag, or a bad value
|
|
369
|
+
3 not running: installed, but not running or not answering; nothing answers
|
|
370
|
+
4 not installed: there is no service unit
|
|
371
|
+
75 busy: refused, and why was printed. Try later, or use --force
|
|
372
|
+
|
|
258
373
|
Opening your own directories
|
|
259
374
|
Where you run this from does not matter — the instance is decided by its data
|
|
260
375
|
dir alone. Directories are added inside the app, on the Discover screen: it
|
|
@@ -358,10 +473,30 @@ export const SERVICE_USAGE = `paddock service — keep Paddock running in the ba
|
|
|
358
473
|
Usage
|
|
359
474
|
paddock service install [options] register it and start it now
|
|
360
475
|
paddock service uninstall stop it and deregister it
|
|
361
|
-
paddock service status
|
|
476
|
+
paddock service status [--json] is it registered, is it running, which
|
|
477
|
+
version, where are the logs
|
|
362
478
|
paddock service start start an installed service that is stopped
|
|
363
|
-
paddock service stop
|
|
364
|
-
paddock service restart
|
|
479
|
+
paddock service stop [--force] stop it, and leave it installed
|
|
480
|
+
paddock service restart [--force] stop it and start it again, re-reading config
|
|
481
|
+
paddock service logs [-f] [-n <n>] the service's log (the last 50 lines)
|
|
482
|
+
|
|
483
|
+
Options (logs)
|
|
484
|
+
-f, --follow keep printing what gets appended, until Ctrl-C
|
|
485
|
+
-n, --lines <n> how many lines to print first (default 50)
|
|
486
|
+
|
|
487
|
+
Options (stop, restart)
|
|
488
|
+
--force stop it even though it is busy
|
|
489
|
+
|
|
490
|
+
Busy
|
|
491
|
+
\`stop\` and \`restart\` ask the running instance whether it is busy first, and
|
|
492
|
+
refuse with exit 75 if it is — a chat turn in flight, a background task, a
|
|
493
|
+
scheduled job. \`paddock busy\` asks the same question on its own.
|
|
494
|
+
|
|
495
|
+
Being refused is the useful case: a restart kills a chat turn mid-sentence,
|
|
496
|
+
and the turn cannot be resumed from where it was. Wait, or --force.
|
|
497
|
+
|
|
498
|
+
\`uninstall\` does not ask. Removing the service is an explicit act, and
|
|
499
|
+
someone uninstalling it has already decided.
|
|
365
500
|
|
|
366
501
|
Options (install only — recorded in the generated unit)
|
|
367
502
|
-p, --port <port> HTTP/WS port (default 7233)
|
|
@@ -393,9 +528,21 @@ At login, not at boot
|
|
|
393
528
|
|
|
394
529
|
Where it lives
|
|
395
530
|
macOS ~/Library/LaunchAgents/net.edspencer.paddock.plist
|
|
396
|
-
logs in <data-dir>/service/
|
|
397
531
|
Linux ~/.config/systemd/user/paddock.service
|
|
398
|
-
|
|
532
|
+
|
|
533
|
+
Both write stdout and stderr to ONE file, <data-dir>/logs/service.log, which
|
|
534
|
+
is what \`paddock service logs\` reads. Not the journal on Linux: a volatile
|
|
535
|
+
journal has nothing in it after the reboot you are asking about, and one file
|
|
536
|
+
means one answer to "where are the logs" on every OS. Paddock rotates it
|
|
537
|
+
itself at 8 MB, keeping three older generations beside it.
|
|
538
|
+
|
|
539
|
+
Exit codes
|
|
540
|
+
0 it is installed, running and answering
|
|
541
|
+
3 installed, but not running or not answering
|
|
542
|
+
4 not installed: there is no unit (status, logs, start, stop, restart)
|
|
543
|
+
1 something failed
|
|
544
|
+
2 usage: an unknown action or flag, or a bad value
|
|
545
|
+
75 busy, and --force was not given (stop, restart)
|
|
399
546
|
|
|
400
547
|
Installed from npx?
|
|
401
548
|
\`service install\` refuses. An npx cache path is hash-keyed and npm may prune
|
|
@@ -411,6 +558,66 @@ A note on access
|
|
|
411
558
|
process that could reach the port could already read the same Claude login as
|
|
412
559
|
you. Set PADDOCK_AUTH_MODE if you want a credential on it anyway.
|
|
413
560
|
|
|
561
|
+
Docs: https://github.com/edspencer/paddock`;
|
|
562
|
+
export const BUSY_USAGE = `paddock busy — is the running instance in the middle of something?
|
|
563
|
+
|
|
564
|
+
Usage
|
|
565
|
+
paddock busy ask, and say so in a sentence
|
|
566
|
+
paddock busy --json the same answer as one JSON document
|
|
567
|
+
paddock busy -d <path> ask a particular instance
|
|
568
|
+
|
|
569
|
+
Exit codes — this command is FOR its exit code
|
|
570
|
+
0 not busy. Nothing a restart would cut short.
|
|
571
|
+
75 busy. What is running was printed.
|
|
572
|
+
3 nothing answered. No instance is running, so there is nothing to wait for.
|
|
573
|
+
1 something answered, but not as Paddock's busy check.
|
|
574
|
+
|
|
575
|
+
Options
|
|
576
|
+
-d, --data-dir <path> Which instance to ask (default ~/.paddock, or
|
|
577
|
+
\$PADDOCK_DATA_DIR) — the same rule \`paddock serve\`
|
|
578
|
+
uses, so the two always mean the same instance
|
|
579
|
+
--json One JSON document on stdout: running, busy, url,
|
|
580
|
+
version, counts. Prose goes to stderr
|
|
581
|
+
-h, --help Show this help
|
|
582
|
+
|
|
583
|
+
What counts as busy
|
|
584
|
+
Only work a restart would LOSE or cut short:
|
|
585
|
+
|
|
586
|
+
chat a chat turn running — a human turn, a fired trigger, a cron
|
|
587
|
+
fire. A turn is a live claude subprocess writing a transcript,
|
|
588
|
+
and it cannot be resumed from where it was interrupted.
|
|
589
|
+
background a background task a live session is still holding after its
|
|
590
|
+
turn ended: a background shell, a sub-agent, a Monitor.
|
|
591
|
+
job a scheduled job herdctl is running.
|
|
592
|
+
|
|
593
|
+
Not busy: an idle session (it resumes), an open browser tab (it reconnects),
|
|
594
|
+
a pending curation sweep (it re-runs from its watermark).
|
|
595
|
+
|
|
596
|
+
Where to run it from
|
|
597
|
+
From a terminal. A Paddock chat that runs this is itself one of the chat turns
|
|
598
|
+
it counts, so it will always see itself as busy — and \`service stop --force\`
|
|
599
|
+
from inside a chat stops the service that is running the chat, which does not
|
|
600
|
+
come back with an explanation. That is why the refusal says so.
|
|
601
|
+
|
|
602
|
+
For scripts, containers and Watchtower
|
|
603
|
+
\`busy\` is there for callers with no service to ask. 75 is EX_TEMPFAIL, which
|
|
604
|
+
is the code Watchtower's pre-update hook reads as "skip this one":
|
|
605
|
+
|
|
606
|
+
labels:
|
|
607
|
+
com.centurylinklabs.watchtower.lifecycle.pre-update: paddock busy
|
|
608
|
+
|
|
609
|
+
An Ansible task does the same thing with a retry. Nothing here contacts the
|
|
610
|
+
network or the npm registry, and the question is answered from memory — it is
|
|
611
|
+
cheap enough to ask in a loop.
|
|
612
|
+
|
|
613
|
+
How it finds the instance
|
|
614
|
+
\`<data-dir>/runtime.json\`, written 0600 when the server starts listening and
|
|
615
|
+
removed when it stops. There is no fallback to a default port: guessing one
|
|
616
|
+
would ask whatever else happened to be listening there, and get an answer.
|
|
617
|
+
|
|
618
|
+
A Paddock from before this feature shipped writes no such file, so it reads as
|
|
619
|
+
not running (exit 3). Upgrade it and restart it once.
|
|
620
|
+
|
|
414
621
|
Docs: https://github.com/edspencer/paddock`;
|
|
415
622
|
export const CONFIG_USAGE = `paddock config — what this instance's configuration actually resolved to
|
|
416
623
|
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type BusyResult, type Nouns } from "./busy.js";
|
|
2
|
+
/**
|
|
3
|
+
* How each count in `/api/busy` is said in a sentence (`busy.ts` on the server
|
|
4
|
+
* decides the keys).
|
|
5
|
+
*
|
|
6
|
+
* `background` is spelled out rather than left to the default pluraliser,
|
|
7
|
+
* because "3 backgrounds are running" is not English — and because "background
|
|
8
|
+
* task" is the thing a user recognises from the running-work bar in the UI,
|
|
9
|
+
* where they watched it start.
|
|
10
|
+
*/
|
|
11
|
+
export declare const PADDOCK_NOUNS: Nouns;
|
|
12
|
+
/**
|
|
13
|
+
* What to do about a busy answer — and for Paddock, the first thing to say is
|
|
14
|
+
* **where to run the command from**.
|
|
15
|
+
*
|
|
16
|
+
* This is the keeper-chat trap, and it is specific to Paddock and Managers
|
|
17
|
+
* among the six tools: Paddock's agents can drive Paddock. A keeper chat that
|
|
18
|
+
* runs `paddock service restart` or `paddock update` is itself one of the chat
|
|
19
|
+
* turns the answer just counted, so it will always see itself as busy — and
|
|
20
|
+
* `--force` from there does not override a mistaken reading, it kills the chat
|
|
21
|
+
* that typed it, mid-sentence, along with whatever it had not finished. The
|
|
22
|
+
* agent then has no way to report what happened, because the thing that would
|
|
23
|
+
* report it is the thing that died.
|
|
24
|
+
*
|
|
25
|
+
* So when `chat` is non-zero the advice is not "wait or use --force": it is
|
|
26
|
+
* "run this from a terminal". When the busy work is background tasks or
|
|
27
|
+
* scheduled jobs only, nobody is holding the keyboard inside the service and
|
|
28
|
+
* the ordinary advice applies.
|
|
29
|
+
*/
|
|
30
|
+
export declare function paddockBusyHint(result: BusyResult): string | undefined;
|
|
31
|
+
/**
|
|
32
|
+
* `paddock busy [--json]` — exit 0 not busy, 75 busy, 3 nothing answering
|
|
33
|
+
* (SPEC §1, §2).
|
|
34
|
+
*
|
|
35
|
+
* The instance is found through `<data-dir>/runtime.json` and nowhere else, so
|
|
36
|
+
* this reports on the same instance `paddock serve` and `paddock config show`
|
|
37
|
+
* resolve — `resolveDataDir` in paddock.ts is the one rule all three use.
|
|
38
|
+
*/
|
|
39
|
+
export declare function runBusy(home: string, json: boolean): Promise<number>;
|
|
40
|
+
/**
|
|
41
|
+
* The check before `service stop` and `service restart` (SPEC §1): refuse with
|
|
42
|
+
* 75 unless `--force`, which prints the same message prefixed `Overriding:` and
|
|
43
|
+
* carries on. Returns null to go ahead.
|
|
44
|
+
*
|
|
45
|
+
* `home` is the data dir of the INSTALLED unit, not whatever `--data-dir` this
|
|
46
|
+
* invocation carries — the same rule `status` and `logs` follow (#824), because
|
|
47
|
+
* the instance being stopped is the one the unit describes.
|
|
48
|
+
*
|
|
49
|
+
* Two deliberate non-refusals. Nothing answering goes ahead: there is nothing
|
|
50
|
+
* to cut short, which is also the answer for a service that is already stopped.
|
|
51
|
+
* And an instance running a Paddock from before this release has no
|
|
52
|
+
* `runtime.json`, so it reads as not running and the stop proceeds exactly as
|
|
53
|
+
* it did before — an upgrade cannot make a stop start failing.
|
|
54
|
+
*/
|
|
55
|
+
export declare function guardServiceBusy(action: "stop" | "restart", home: string, opts: {
|
|
56
|
+
force?: boolean;
|
|
57
|
+
json?: boolean;
|
|
58
|
+
}): Promise<number | null>;
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Paddock's bindings for the copied busy client (`cli/busy.ts`): the nouns its
|
|
3
|
+
* message uses, the keeper-chat trap, and the two entry points — `paddock busy`
|
|
4
|
+
* and the check `service stop` / `service restart` run first (CLI contract
|
|
5
|
+
* SPEC §1, §4).
|
|
6
|
+
*
|
|
7
|
+
* Kept separate from `cli/busy.ts` so that file stays a straight copy of
|
|
8
|
+
* `reference/busy-client.ts`, which is what wave C4 re-syncs. Everything in
|
|
9
|
+
* here is Paddock-specific by definition: the contract says each tool's kinds of
|
|
10
|
+
* work are its own.
|
|
11
|
+
*/
|
|
12
|
+
import { EXIT } from "./exit-codes.js";
|
|
13
|
+
import { createOutput } from "./output.js";
|
|
14
|
+
import { guardBusy, runBusyCommand, } from "./busy.js";
|
|
15
|
+
import { PADDOCK_NAME } from "../version.js";
|
|
16
|
+
/**
|
|
17
|
+
* How each count in `/api/busy` is said in a sentence (`busy.ts` on the server
|
|
18
|
+
* decides the keys).
|
|
19
|
+
*
|
|
20
|
+
* `background` is spelled out rather than left to the default pluraliser,
|
|
21
|
+
* because "3 backgrounds are running" is not English — and because "background
|
|
22
|
+
* task" is the thing a user recognises from the running-work bar in the UI,
|
|
23
|
+
* where they watched it start.
|
|
24
|
+
*/
|
|
25
|
+
export const PADDOCK_NOUNS = {
|
|
26
|
+
chat: ["chat turn", "chat turns"],
|
|
27
|
+
background: ["background task", "background tasks"],
|
|
28
|
+
job: ["scheduled job", "scheduled jobs"],
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* What to do about a busy answer — and for Paddock, the first thing to say is
|
|
32
|
+
* **where to run the command from**.
|
|
33
|
+
*
|
|
34
|
+
* This is the keeper-chat trap, and it is specific to Paddock and Managers
|
|
35
|
+
* among the six tools: Paddock's agents can drive Paddock. A keeper chat that
|
|
36
|
+
* runs `paddock service restart` or `paddock update` is itself one of the chat
|
|
37
|
+
* turns the answer just counted, so it will always see itself as busy — and
|
|
38
|
+
* `--force` from there does not override a mistaken reading, it kills the chat
|
|
39
|
+
* that typed it, mid-sentence, along with whatever it had not finished. The
|
|
40
|
+
* agent then has no way to report what happened, because the thing that would
|
|
41
|
+
* report it is the thing that died.
|
|
42
|
+
*
|
|
43
|
+
* So when `chat` is non-zero the advice is not "wait or use --force": it is
|
|
44
|
+
* "run this from a terminal". When the busy work is background tasks or
|
|
45
|
+
* scheduled jobs only, nobody is holding the keyboard inside the service and
|
|
46
|
+
* the ordinary advice applies.
|
|
47
|
+
*/
|
|
48
|
+
export function paddockBusyHint(result) {
|
|
49
|
+
if (result.state !== "busy")
|
|
50
|
+
return undefined;
|
|
51
|
+
const chats = result.answer.counts?.chat ?? 0;
|
|
52
|
+
if (chats > 0) {
|
|
53
|
+
// The lead sentence is short because `guardBusy` prints it on the same line
|
|
54
|
+
// as the message, after `paddock: `. The reasoning goes underneath, indented
|
|
55
|
+
// like every other continuation this CLI prints.
|
|
56
|
+
return ("Run it from a terminal, not from inside a Paddock chat.\n" +
|
|
57
|
+
" One of the turns counted above may be the chat asking, and --force from there\n" +
|
|
58
|
+
" stops the service running that chat — which then never gets to say so.\n" +
|
|
59
|
+
" Otherwise: wait for the turns to finish.");
|
|
60
|
+
}
|
|
61
|
+
return "Try again once it has finished, or use --force.";
|
|
62
|
+
}
|
|
63
|
+
/** The options both entry points share: name, nouns and hint are fixed for Paddock. */
|
|
64
|
+
function paddockBusyOptions(home, out) {
|
|
65
|
+
return { home, out, name: PADDOCK_NAME, nouns: PADDOCK_NOUNS, hint: paddockBusyHint };
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* `paddock busy [--json]` — exit 0 not busy, 75 busy, 3 nothing answering
|
|
69
|
+
* (SPEC §1, §2).
|
|
70
|
+
*
|
|
71
|
+
* The instance is found through `<data-dir>/runtime.json` and nowhere else, so
|
|
72
|
+
* this reports on the same instance `paddock serve` and `paddock config show`
|
|
73
|
+
* resolve — `resolveDataDir` in paddock.ts is the one rule all three use.
|
|
74
|
+
*/
|
|
75
|
+
export async function runBusy(home, json) {
|
|
76
|
+
const out = createOutput({ tool: "paddock", json });
|
|
77
|
+
return runBusyCommand(paddockBusyOptions(home, out));
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* The check before `service stop` and `service restart` (SPEC §1): refuse with
|
|
81
|
+
* 75 unless `--force`, which prints the same message prefixed `Overriding:` and
|
|
82
|
+
* carries on. Returns null to go ahead.
|
|
83
|
+
*
|
|
84
|
+
* `home` is the data dir of the INSTALLED unit, not whatever `--data-dir` this
|
|
85
|
+
* invocation carries — the same rule `status` and `logs` follow (#824), because
|
|
86
|
+
* the instance being stopped is the one the unit describes.
|
|
87
|
+
*
|
|
88
|
+
* Two deliberate non-refusals. Nothing answering goes ahead: there is nothing
|
|
89
|
+
* to cut short, which is also the answer for a service that is already stopped.
|
|
90
|
+
* And an instance running a Paddock from before this release has no
|
|
91
|
+
* `runtime.json`, so it reads as not running and the stop proceeds exactly as
|
|
92
|
+
* it did before — an upgrade cannot make a stop start failing.
|
|
93
|
+
*/
|
|
94
|
+
export async function guardServiceBusy(action, home, opts) {
|
|
95
|
+
// No `result()` call on this path, so `--json` only decides which stream the
|
|
96
|
+
// prose goes to — the one JSON document a `--json` stop may print belongs to
|
|
97
|
+
// the stop, not to the check it ran first.
|
|
98
|
+
const out = createOutput({ tool: "paddock", json: opts.json === true });
|
|
99
|
+
const refusal = await guardBusy({
|
|
100
|
+
...paddockBusyOptions(home, out),
|
|
101
|
+
...(opts.force === true ? { force: true } : {}),
|
|
102
|
+
});
|
|
103
|
+
if (refusal === null)
|
|
104
|
+
return null;
|
|
105
|
+
// Spell out the way past it, after the hint about where to run it from.
|
|
106
|
+
out.warn(`\n paddock service ${action} --force ${action} it anyway\n`);
|
|
107
|
+
return EXIT.busy;
|
|
108
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { Output } from "./output.js";
|
|
2
|
+
/** What `/api/busy` answers (SPEC §4). Counts only, until §10's first question is settled. */
|
|
3
|
+
export interface BusyAnswer {
|
|
4
|
+
busy: boolean;
|
|
5
|
+
version?: string;
|
|
6
|
+
counts?: Record<string, number>;
|
|
7
|
+
}
|
|
8
|
+
export type BusyResult = {
|
|
9
|
+
state: "not-running";
|
|
10
|
+
} | {
|
|
11
|
+
state: "idle" | "busy";
|
|
12
|
+
url: string;
|
|
13
|
+
answer: BusyAnswer;
|
|
14
|
+
}
|
|
15
|
+
/** Something answered, but not with a busy answer: an older version without `/api/busy`, or another program. */
|
|
16
|
+
| {
|
|
17
|
+
state: "unknown";
|
|
18
|
+
url: string;
|
|
19
|
+
detail: string;
|
|
20
|
+
};
|
|
21
|
+
export interface FindOptions {
|
|
22
|
+
/** The tool's data home, holding `runtime.json`. */
|
|
23
|
+
home: string;
|
|
24
|
+
/** Ask this URL instead of the one in `runtime.json` (`--url`, or a port the user named). */
|
|
25
|
+
url?: string;
|
|
26
|
+
/** How long to wait for an answer. §4 asks for well under a second; this allows for a slow machine. */
|
|
27
|
+
timeoutMs?: number;
|
|
28
|
+
fetch?: typeof globalThis.fetch;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Asks the running instance whether it's busy. There's no fallback to a default port: an instance that's running
|
|
32
|
+
* wrote `runtime.json`, and guessing a port would ask whatever else is listening there.
|
|
33
|
+
*/
|
|
34
|
+
export declare function checkBusy(o: FindOptions): Promise<BusyResult>;
|
|
35
|
+
/** A kind's singular and plural, where adding an "s" is wrong: `{ batch: ['batch', 'batches'] }`. */
|
|
36
|
+
export type Nouns = Record<string, [singular: string, plural: string]>;
|
|
37
|
+
/** `2 chats and 1 job are running`, from the non-zero counts; '' if there are none. */
|
|
38
|
+
export declare function describeCounts(counts?: Record<string, number>, nouns?: Nouns): string;
|
|
39
|
+
/** The sentence for a person (SPEC §4): `Paddock is busy: 2 chats are running.` */
|
|
40
|
+
export declare function busyMessage(name: string, result: BusyResult, nouns?: Nouns): string;
|
|
41
|
+
/** 0 idle, 75 busy, 3 nothing answers, 1 an answer that isn't a busy answer. */
|
|
42
|
+
export declare function busyExitCode(result: BusyResult): number;
|
|
43
|
+
/** What `<tool> busy --json` prints: valid against `schemas/busy.schema.json`. */
|
|
44
|
+
export declare function busyJson(result: BusyResult): Record<string, unknown>;
|
|
45
|
+
/**
|
|
46
|
+
* LOCAL CHANGE (see the header): what to suggest when the answer is busy, as a string or as a function of the
|
|
47
|
+
* result. {@link resolveHint} is what both the command and the guard call, so the two can never print different
|
|
48
|
+
* advice about the same answer.
|
|
49
|
+
*/
|
|
50
|
+
export type BusyHint = string | ((result: BusyResult) => string | undefined);
|
|
51
|
+
export interface BusyCommandOptions extends FindOptions {
|
|
52
|
+
/** The product's name, for messages: `Local Review`. */
|
|
53
|
+
name: string;
|
|
54
|
+
out: Output;
|
|
55
|
+
nouns?: Nouns;
|
|
56
|
+
/** LOCAL CHANGE: the advice printed after a busy message — Paddock's keeper-chat trap. */
|
|
57
|
+
hint?: BusyHint;
|
|
58
|
+
}
|
|
59
|
+
/** `<tool> busy [--json]`. Returns the exit code; the caller sets `process.exitCode`. */
|
|
60
|
+
export declare function runBusyCommand(o: BusyCommandOptions): Promise<number>;
|
|
61
|
+
export interface GuardOptions extends BusyCommandOptions {
|
|
62
|
+
force?: boolean;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* The check before `service stop`, `service restart` and `update` (SPEC §4). Returns null to go ahead, or 75 after
|
|
66
|
+
* printing the refusal. With `--force` it prints the same message, prefixed `Overriding:`, and goes ahead.
|
|
67
|
+
* Nothing answering goes ahead: there's nothing to cut short.
|
|
68
|
+
*/
|
|
69
|
+
export declare function guardBusy(o: GuardOptions): Promise<number | null>;
|