@rubytech/taskmaster 1.0.15 → 1.0.16

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.
@@ -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-J5yAenad.js"></script>
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
- try {
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)) {
@@ -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
- setTimeout(() => {
115
- void scheduleRestartSentinelWake({ deps: params.deps });
116
- }, 750);
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/taskmaster",
3
- "version": "1.0.15",
3
+ "version": "1.0.16",
4
4
  "description": "AI-powered business assistant for small businesses",
5
5
  "publishConfig": {
6
6
  "access": "public"