@ahyi/restart-continuity 0.3.1 → 0.5.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/README.md CHANGED
@@ -1,91 +1,38 @@
1
1
  # Restart Continuity Plugin
2
2
 
3
- OpenClaw plugin for **gateway restart continuity**:
4
-
5
- - runs resumable startup checks when the plugin service starts
6
- - can health-check and replay selected cron jobs
7
- - writes a structured continuity state file
8
- - writes a human-readable daily memory log
9
- - can hand off a one-shot startup receipt during agent bootstrap
3
+ OpenClaw plugin for **gateway restart continuity** with **zero-config behavior on this mh instance**.
10
4
 
11
5
  ## Install
12
6
 
13
- ### Local path
14
-
15
7
  ```bash
16
- openclaw --profile <profile> plugins install /path/to/restart-continuity-plugin
8
+ openclaw --profile <profile> plugins install @ahyi/restart-continuity@0.5.0
17
9
  openclaw --profile <profile> plugins enable restart-continuity
18
10
  ```
19
11
 
20
- ### npm registry
12
+ Then restart the gateway.
21
13
 
22
- ```bash
23
- openclaw --profile <profile> plugins install @ahyi/restart-continuity@0.3.1
24
- openclaw --profile <profile> plugins enable restart-continuity
25
- ```
26
-
27
- ## What it does
14
+ ## Zero-config behavior
28
15
 
29
- This plugin is designed to replace scattered hook-based restart continuity setups with one installable extension.
16
+ After install + enable, the plugin will work **without any extra config** on this mh instance.
30
17
 
31
- On plugin service start it can:
18
+ It automatically:
32
19
 
33
- 1. initialize its runtime marker/state files
34
- 2. run configured startup resumers
35
- 3. write `memory/restart-continuity-state.json`
36
- 4. append a summary to `memory/YYYY-MM-DD.md`
37
- 5. stage a one-time startup receipt for bootstrap delivery
20
+ - detects the mh workspace directory
21
+ - assumes profile `mh` unless overridden
22
+ - uses built-in default resumers for:
23
+ - `mh-nightly-learnings-check`
24
+ - `mh-weekly-self-improve`
25
+ - writes `memory/restart-continuity-state.json`
26
+ - writes `memory/restart-continuity-installed.json`
27
+ - appends a summary to the daily memory log
28
+ - stages a one-shot bootstrap receipt
38
29
 
39
- ## Example config
30
+ ## Optional explicit config
40
31
 
41
- ```json
42
- {
43
- "plugins": {
44
- "entries": {
45
- "restart-continuity": {
46
- "enabled": true,
47
- "config": {
48
- "profile": "mh",
49
- "workspaceDir": "/root/.openclaw/workspace-mh",
50
- "logToDailyMemory": true,
51
- "notifyOnBootstrap": true,
52
- "resumers": [
53
- {
54
- "id": "nightly-learnings",
55
- "kind": "cron-healthcheck",
56
- "name": "mh-nightly-learnings-check",
57
- "jobId": "38a8b8cb-c25a-4bc2-a8a1-d252f9d933ec"
58
- },
59
- {
60
- "id": "weekly-self-improve",
61
- "kind": "cron-healthcheck",
62
- "name": "mh-weekly-self-improve",
63
- "jobId": "6e912522-0591-4307-a155-a6ece496be9c"
64
- }
65
- ]
66
- }
67
- }
68
- }
69
- }
70
- }
71
- ```
32
+ You can still override behavior with `plugins.entries.restart-continuity.config`, but it is not required for this instance.
72
33
 
73
34
  ## Uninstall
74
35
 
75
36
  ```bash
76
37
  openclaw --profile <profile> plugins uninstall restart-continuity
77
38
  ```
78
-
79
- OpenClaw removes the plugin config entry and install record. If you want a fully clean teardown, also delete plugin runtime artifacts such as:
80
-
81
- - `memory/restart-continuity-state.json`
82
- - `memory/restart-continuity-installed.json`
83
- - `memory/restart-continuity-receipt.json`
84
-
85
- ## Publish
86
-
87
- ```bash
88
- npm publish --access public
89
- ```
90
-
91
- If scoped publishing is used, make sure the npm account/org owns the scope.
package/index.ts CHANGED
@@ -5,6 +5,22 @@ import { promisify } from "node:util";
5
5
 
6
6
  const execFileAsync = promisify(execFile);
7
7
  const OPENCLAW_BIN = process.env.OPENCLAW_BIN || "openclaw";
8
+ let bootStarted = false;
9
+
10
+ const BUILTIN_DEFAULT_RESUMERS = [
11
+ {
12
+ id: "nightly-learnings",
13
+ kind: "cron-healthcheck",
14
+ name: "mh-nightly-learnings-check",
15
+ jobId: "38a8b8cb-c25a-4bc2-a8a1-d252f9d933ec",
16
+ },
17
+ {
18
+ id: "weekly-self-improve",
19
+ kind: "cron-healthcheck",
20
+ name: "mh-weekly-self-improve",
21
+ jobId: "6e912522-0591-4307-a155-a6ece496be9c",
22
+ },
23
+ ];
8
24
 
9
25
  function nowIso() {
10
26
  return new Date().toISOString();
@@ -14,14 +30,17 @@ function normalizeString(value, fallback = "") {
14
30
  return typeof value === "string" && value.trim() ? value.trim() : fallback;
15
31
  }
16
32
 
17
- function getWorkspaceDir(api) {
33
+ function detectWorkspaceDir(api) {
18
34
  const pluginConfig = api.pluginConfig || {};
19
35
  const explicit = normalizeString(pluginConfig.workspaceDir, "");
20
36
  if (explicit) return explicit;
21
- return normalizeString(api.config?.workspace?.dir, process.cwd());
37
+ const cfgWorkspace = normalizeString(api.config?.workspace?.dir, "");
38
+ if (cfgWorkspace) return cfgWorkspace;
39
+ const mhWorkspace = "/root/.openclaw/workspace-mh";
40
+ return mhWorkspace;
22
41
  }
23
42
 
24
- function getProfile(api) {
43
+ function detectProfile(api) {
25
44
  const pluginConfig = api.pluginConfig || {};
26
45
  return normalizeString(pluginConfig.profile, "mh");
27
46
  }
@@ -120,6 +139,13 @@ function getDatePartsForShanghai(date = new Date()) {
120
139
  };
121
140
  }
122
141
 
142
+ function getEffectiveResumers(pluginConfig) {
143
+ if (Array.isArray(pluginConfig.resumers) && pluginConfig.resumers.length > 0) {
144
+ return pluginConfig.resumers;
145
+ }
146
+ return BUILTIN_DEFAULT_RESUMERS;
147
+ }
148
+
123
149
  async function runCronHealthcheck(profile, resumer) {
124
150
  const successStatuses = Array.isArray(resumer.successStatuses) && resumer.successStatuses.length
125
151
  ? resumer.successStatuses.map(String)
@@ -143,21 +169,17 @@ async function runCronHealthcheck(profile, resumer) {
143
169
  return { status: "resumed", detail: `${resumer.name || resumer.id} => ${normalized}`, latestStatus };
144
170
  }
145
171
 
146
- async function ensureConfigInitialized(api) {
147
- const workspaceDir = getWorkspaceDir(api);
148
- const pluginConfig = api.pluginConfig || {};
172
+ async function ensureConfigInitialized(api, workspaceDir, pluginConfig) {
149
173
  const stateFile = resolvePath(workspaceDir, pluginConfig.stateFile, "memory/restart-continuity-state.json");
150
- const defaultsFile = resolvePath(workspaceDir, pluginConfig.defaultsFile, "plugins/restart-continuity.defaults.json");
151
174
  const markerFile = resolvePath(workspaceDir, pluginConfig.markerFile, "memory/restart-continuity-installed.json");
152
175
 
153
176
  if (await fileExists(markerFile)) return;
154
177
 
155
- const defaults = await readJsonSafe(defaultsFile, null);
156
178
  await writeJson(markerFile, {
157
179
  installedAt: nowIso(),
158
180
  source: "restart-continuity",
159
181
  workspaceDir,
160
- defaultsApplied: Boolean(defaults),
182
+ builtInDefaultsActive: !(Array.isArray(pluginConfig.resumers) && pluginConfig.resumers.length > 0),
161
183
  });
162
184
 
163
185
  if (!(await fileExists(stateFile))) {
@@ -166,23 +188,21 @@ async function ensureConfigInitialized(api) {
166
188
  initializedAt: nowIso(),
167
189
  status: "initialized",
168
190
  source: "restart-continuity",
169
- defaultsApplied: Boolean(defaults),
191
+ builtInDefaultsActive: !(Array.isArray(pluginConfig.resumers) && pluginConfig.resumers.length > 0),
170
192
  });
171
193
  }
172
194
 
173
- api.logger.info?.(`[restart-continuity] initialized (${defaults ? "defaults detected" : "no defaults file"})`);
195
+ api.logger.info?.("[restart-continuity] initialized");
174
196
  }
175
197
 
176
- async function runStartupCheck(api) {
177
- const pluginConfig = api.pluginConfig || {};
178
- const profile = getProfile(api);
179
- const workspaceDir = getWorkspaceDir(api);
198
+ async function runStartupCheck(api, workspaceDir, pluginConfig) {
199
+ const profile = detectProfile(api);
180
200
  const stateFile = resolvePath(workspaceDir, pluginConfig.stateFile, "memory/restart-continuity-state.json");
181
201
  const receiptFile = resolvePath(workspaceDir, pluginConfig.receiptFile, "memory/restart-continuity-receipt.json");
182
202
  const logToDailyMemory = pluginConfig.logToDailyMemory !== false;
183
203
  const notifyOnBootstrap = pluginConfig.notifyOnBootstrap !== false;
184
204
  const { date, local } = getDatePartsForShanghai(new Date());
185
- const resumers = Array.isArray(pluginConfig.resumers) ? pluginConfig.resumers : [];
205
+ const resumers = getEffectiveResumers(pluginConfig);
186
206
 
187
207
  const summary = {
188
208
  version: 1,
@@ -241,6 +261,20 @@ async function runStartupCheck(api) {
241
261
  api.logger.info?.(`[restart-continuity] ${summary.receipt}`);
242
262
  }
243
263
 
264
+ async function boot(api) {
265
+ if (bootStarted) return;
266
+ bootStarted = true;
267
+ const pluginConfig = api.pluginConfig || {};
268
+ const workspaceDir = detectWorkspaceDir(api);
269
+
270
+ try {
271
+ await ensureConfigInitialized(api, workspaceDir, pluginConfig);
272
+ await runStartupCheck(api, workspaceDir, pluginConfig);
273
+ } catch (err) {
274
+ api.logger.warn?.(`[restart-continuity] boot failed: ${err instanceof Error ? err.message : String(err)}`);
275
+ }
276
+ }
277
+
244
278
  const plugin = {
245
279
  id: "restart-continuity",
246
280
  name: "Restart Continuity",
@@ -254,7 +288,6 @@ const plugin = {
254
288
  workspaceDir: { label: "Workspace Directory" },
255
289
  stateFile: { label: "State File" },
256
290
  receiptFile: { label: "Receipt File" },
257
- defaultsFile: { label: "Defaults File" },
258
291
  markerFile: { label: "Install Marker File" },
259
292
  logToDailyMemory: { label: "Append summary to daily memory" },
260
293
  notifyOnBootstrap: { label: "Inject startup receipt into next bootstrap reply" },
@@ -262,18 +295,7 @@ const plugin = {
262
295
  },
263
296
  },
264
297
  register(api) {
265
- api.registerService({
266
- id: "restart-continuity-service",
267
- start: async () => {
268
- try {
269
- await ensureConfigInitialized(api);
270
- await runStartupCheck(api);
271
- } catch (err) {
272
- api.logger.warn?.(`[restart-continuity] service start failed: ${err instanceof Error ? err.message : String(err)}`);
273
- }
274
- },
275
- stop: async () => {},
276
- });
298
+ void boot(api);
277
299
 
278
300
  api.registerHook(
279
301
  "agent:bootstrap",
@@ -284,7 +306,7 @@ const plugin = {
284
306
  const bootstrapFiles = Array.isArray(ctx.bootstrapFiles) ? ctx.bootstrapFiles : null;
285
307
  if (!bootstrapFiles) return;
286
308
 
287
- const workspaceDir = getWorkspaceDir(api);
309
+ const workspaceDir = detectWorkspaceDir(api);
288
310
  const receiptFile = resolvePath(workspaceDir, pluginConfig.receiptFile, "memory/restart-continuity-receipt.json");
289
311
  const pending = await readJsonSafe(receiptFile, null);
290
312
  if (!pending || !pending.receipt) return;
@@ -2,7 +2,7 @@
2
2
  "id": "restart-continuity",
3
3
  "name": "Restart Continuity",
4
4
  "description": "Gateway restart continuity plugin with resumable startup checks and receipt handoff.",
5
- "version": "0.3.0",
5
+ "version": "0.5.0",
6
6
  "configSchema": {
7
7
  "type": "object",
8
8
  "additionalProperties": false,
@@ -11,7 +11,6 @@
11
11
  "workspaceDir": { "type": "string", "default": "" },
12
12
  "stateFile": { "type": "string", "default": "memory/restart-continuity-state.json" },
13
13
  "receiptFile": { "type": "string", "default": "memory/restart-continuity-receipt.json" },
14
- "defaultsFile": { "type": "string", "default": "plugins/restart-continuity.defaults.json" },
15
14
  "markerFile": { "type": "string", "default": "memory/restart-continuity-installed.json" },
16
15
  "logToDailyMemory": { "type": "boolean", "default": true },
17
16
  "notifyOnBootstrap": { "type": "boolean", "default": true },
@@ -45,7 +44,6 @@
45
44
  "workspaceDir": { "label": "Workspace Directory" },
46
45
  "stateFile": { "label": "State File" },
47
46
  "receiptFile": { "label": "Receipt File" },
48
- "defaultsFile": { "label": "Defaults File" },
49
47
  "markerFile": { "label": "Install Marker File" },
50
48
  "logToDailyMemory": { "label": "Append summary to daily memory" },
51
49
  "notifyOnBootstrap": { "label": "Inject startup receipt into next bootstrap reply" },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ahyi/restart-continuity",
3
- "version": "0.3.1",
3
+ "version": "0.5.0",
4
4
  "description": "OpenClaw restart continuity plugin with resumable startup checks and receipt handoff",
5
5
  "type": "module",
6
6
  "license": "MIT",