@rubytech/taskmaster 1.0.15 → 1.0.17
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/build-info.json +3 -3
- package/dist/control-ui/assets/{index-J5yAenad.js → index-CII8VIT3.js} +2 -2
- package/dist/control-ui/assets/index-CII8VIT3.js.map +1 -0
- package/dist/control-ui/index.html +1 -1
- package/dist/gateway/server-methods/update.js +45 -29
- package/dist/gateway/server-startup.js +8 -3
- package/package.json +1 -1
- package/dist/control-ui/assets/index-J5yAenad.js.map +0 -1
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
<title>Taskmaster Control</title>
|
|
7
7
|
<meta name="color-scheme" content="dark light" />
|
|
8
8
|
<link rel="icon" type="image/png" href="./favicon.png" />
|
|
9
|
-
<script type="module" crossorigin src="./assets/index-
|
|
9
|
+
<script type="module" crossorigin src="./assets/index-CII8VIT3.js"></script>
|
|
10
10
|
<link rel="stylesheet" crossorigin href="./assets/index-CJPjbUly.css">
|
|
11
11
|
</head>
|
|
12
12
|
<body>
|
|
@@ -7,6 +7,38 @@ import { normalizeUpdateChannel, resolveEffectiveUpdateChannel, } from "../../in
|
|
|
7
7
|
import { runGatewayUpdate } from "../../infra/update-runner.js";
|
|
8
8
|
import { VERSION } from "../../version.js";
|
|
9
9
|
import { ErrorCodes, errorShape, formatValidationErrors, validateUpdateRunParams, } from "../protocol/index.js";
|
|
10
|
+
let lastUpdateResult = null;
|
|
11
|
+
function cacheFromSentinel(status, ts, stats) {
|
|
12
|
+
const failedStep = stats?.steps?.find((s) => s.log?.exitCode != null && s.log.exitCode !== 0);
|
|
13
|
+
lastUpdateResult = {
|
|
14
|
+
status,
|
|
15
|
+
ts,
|
|
16
|
+
mode: stats?.mode ?? null,
|
|
17
|
+
before: stats?.before ?? null,
|
|
18
|
+
after: stats?.after ?? null,
|
|
19
|
+
reason: stats?.reason ?? null,
|
|
20
|
+
durationMs: stats?.durationMs ?? null,
|
|
21
|
+
failedStep: failedStep
|
|
22
|
+
? { name: failedStep.name, exitCode: failedStep.log?.exitCode ?? null }
|
|
23
|
+
: null,
|
|
24
|
+
currentVersion: VERSION,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Read the restart sentinel into memory before it gets consumed by
|
|
29
|
+
* scheduleRestartSentinelWake. Must be called early in gateway startup.
|
|
30
|
+
*/
|
|
31
|
+
export async function cacheLastUpdateSentinel() {
|
|
32
|
+
try {
|
|
33
|
+
const sentinel = await readRestartSentinel();
|
|
34
|
+
if (sentinel?.payload.kind === "update") {
|
|
35
|
+
cacheFromSentinel(sentinel.payload.status, sentinel.payload.ts, sentinel.payload.stats);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
// Non-critical — worst case we don't show a result banner
|
|
40
|
+
}
|
|
41
|
+
}
|
|
10
42
|
export const updateHandlers = {
|
|
11
43
|
"gateway.restart": async ({ params, respond }) => {
|
|
12
44
|
const reason = typeof params.reason === "string"
|
|
@@ -71,34 +103,7 @@ export const updateHandlers = {
|
|
|
71
103
|
}
|
|
72
104
|
},
|
|
73
105
|
"update.lastResult": async ({ respond }) => {
|
|
74
|
-
|
|
75
|
-
const sentinel = await readRestartSentinel();
|
|
76
|
-
if (!sentinel || sentinel.payload.kind !== "update") {
|
|
77
|
-
respond(true, { ok: true, result: null }, undefined);
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
const p = sentinel.payload;
|
|
81
|
-
const failedStep = p.stats?.steps?.find((s) => s.log?.exitCode != null && s.log.exitCode !== 0);
|
|
82
|
-
respond(true, {
|
|
83
|
-
ok: true,
|
|
84
|
-
result: {
|
|
85
|
-
status: p.status,
|
|
86
|
-
ts: p.ts,
|
|
87
|
-
mode: p.stats?.mode ?? null,
|
|
88
|
-
before: p.stats?.before ?? null,
|
|
89
|
-
after: p.stats?.after ?? null,
|
|
90
|
-
reason: p.stats?.reason ?? null,
|
|
91
|
-
durationMs: p.stats?.durationMs ?? null,
|
|
92
|
-
failedStep: failedStep
|
|
93
|
-
? { name: failedStep.name, exitCode: failedStep.log?.exitCode ?? null }
|
|
94
|
-
: null,
|
|
95
|
-
currentVersion: VERSION,
|
|
96
|
-
},
|
|
97
|
-
}, undefined);
|
|
98
|
-
}
|
|
99
|
-
catch {
|
|
100
|
-
respond(true, { ok: true, result: null }, undefined);
|
|
101
|
-
}
|
|
106
|
+
respond(true, { ok: true, result: lastUpdateResult }, undefined);
|
|
102
107
|
},
|
|
103
108
|
"update.run": async ({ params, respond, context }) => {
|
|
104
109
|
if (!validateUpdateRunParams(params)) {
|
|
@@ -119,8 +124,11 @@ export const updateHandlers = {
|
|
|
119
124
|
const timeoutMs = typeof timeoutMsRaw === "number" && Number.isFinite(timeoutMsRaw)
|
|
120
125
|
? Math.max(1000, Math.floor(timeoutMsRaw))
|
|
121
126
|
: undefined;
|
|
127
|
+
const log = context.logGateway;
|
|
128
|
+
log.info(`software update started`);
|
|
122
129
|
const progress = {
|
|
123
130
|
onStepStart: (step) => {
|
|
131
|
+
log.info(`update step ${step.index + 1}/${step.total}: ${step.name}`);
|
|
124
132
|
context.broadcast("update.progress", {
|
|
125
133
|
phase: "step-start",
|
|
126
134
|
name: step.name,
|
|
@@ -129,13 +137,15 @@ export const updateHandlers = {
|
|
|
129
137
|
});
|
|
130
138
|
},
|
|
131
139
|
onStepComplete: (step) => {
|
|
140
|
+
const ok = step.exitCode === 0 || step.exitCode === null;
|
|
141
|
+
log.info(`update step ${step.index + 1}/${step.total}: ${step.name} ${ok ? "ok" : `FAILED (exit ${step.exitCode})`} (${step.durationMs}ms)`);
|
|
132
142
|
context.broadcast("update.progress", {
|
|
133
143
|
phase: "step-done",
|
|
134
144
|
name: step.name,
|
|
135
145
|
index: step.index,
|
|
136
146
|
total: step.total,
|
|
137
147
|
durationMs: step.durationMs,
|
|
138
|
-
ok
|
|
148
|
+
ok,
|
|
139
149
|
});
|
|
140
150
|
},
|
|
141
151
|
};
|
|
@@ -162,6 +172,12 @@ export const updateHandlers = {
|
|
|
162
172
|
durationMs: 0,
|
|
163
173
|
};
|
|
164
174
|
}
|
|
175
|
+
if (result.status === "ok") {
|
|
176
|
+
log.info(`software update completed (${result.mode}, ${result.durationMs}ms)`);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
log.warn(`software update ${result.status}: ${result.reason ?? "unknown"} (${result.mode}, ${result.durationMs}ms)`);
|
|
180
|
+
}
|
|
165
181
|
const payload = {
|
|
166
182
|
kind: "update",
|
|
167
183
|
status: result.status,
|
|
@@ -8,6 +8,7 @@ import { loadInternalHooks } from "../hooks/loader.js";
|
|
|
8
8
|
import { startPluginServices } from "../plugins/services.js";
|
|
9
9
|
import { startBrowserControlServerIfEnabled } from "./server-browser.js";
|
|
10
10
|
import { scheduleRestartSentinelWake, shouldWakeFromRestartSentinel, } from "./server-restart-sentinel.js";
|
|
11
|
+
import { cacheLastUpdateSentinel } from "./server-methods/update.js";
|
|
11
12
|
export async function startGatewaySidecars(params) {
|
|
12
13
|
// Start Taskmaster browser control server (unless disabled via config).
|
|
13
14
|
let browserControl = null;
|
|
@@ -111,9 +112,13 @@ export async function startGatewaySidecars(params) {
|
|
|
111
112
|
params.log.warn(`plugin services failed to start: ${String(err)}`);
|
|
112
113
|
}
|
|
113
114
|
if (shouldWakeFromRestartSentinel()) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
// Cache the sentinel in memory BEFORE it gets consumed, so update.lastResult
|
|
116
|
+
// can serve it to the UI after reconnect.
|
|
117
|
+
void cacheLastUpdateSentinel().finally(() => {
|
|
118
|
+
setTimeout(() => {
|
|
119
|
+
void scheduleRestartSentinelWake({ deps: params.deps });
|
|
120
|
+
}, 750);
|
|
121
|
+
});
|
|
117
122
|
}
|
|
118
123
|
return { browserControl, pluginServices };
|
|
119
124
|
}
|