@senpi/runtime 1.1.0-dev.runtime-phase-2.20260512102629 → 1.1.0-dev.runtime-phase-2.20260514134437

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.
Files changed (57) hide show
  1. package/dist/cli/senpi-cli-config.d.ts +15 -1
  2. package/dist/cli/senpi-cli-config.d.ts.map +1 -1
  3. package/dist/cli/senpi-cli-config.js +19 -2
  4. package/dist/cli/senpi-cli-config.js.map +1 -1
  5. package/dist/cli/senpi-commands.d.ts.map +1 -1
  6. package/dist/cli/senpi-commands.js +96 -0
  7. package/dist/cli/senpi-commands.js.map +1 -1
  8. package/dist/index.d.ts +21 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +84 -1
  11. package/dist/index.js.map +1 -1
  12. package/dist/runtime/auto-update/coordinator.d.ts +13 -0
  13. package/dist/runtime/auto-update/coordinator.d.ts.map +1 -1
  14. package/dist/runtime/auto-update/coordinator.js +14 -0
  15. package/dist/runtime/auto-update/coordinator.js.map +1 -1
  16. package/dist/skills-manager/bootstrap.d.ts +26 -0
  17. package/dist/skills-manager/bootstrap.d.ts.map +1 -0
  18. package/dist/skills-manager/bootstrap.js +86 -0
  19. package/dist/skills-manager/bootstrap.js.map +1 -0
  20. package/dist/skills-manager/constants.d.ts +31 -0
  21. package/dist/skills-manager/constants.d.ts.map +1 -0
  22. package/dist/skills-manager/constants.js +40 -0
  23. package/dist/skills-manager/constants.js.map +1 -0
  24. package/dist/skills-manager/coordinator.d.ts +336 -0
  25. package/dist/skills-manager/coordinator.d.ts.map +1 -0
  26. package/dist/skills-manager/coordinator.js +931 -0
  27. package/dist/skills-manager/coordinator.js.map +1 -0
  28. package/dist/skills-manager/fetcher.d.ts +28 -0
  29. package/dist/skills-manager/fetcher.d.ts.map +1 -0
  30. package/dist/skills-manager/fetcher.js +92 -0
  31. package/dist/skills-manager/fetcher.js.map +1 -0
  32. package/dist/skills-manager/index.d.ts +5 -0
  33. package/dist/skills-manager/index.d.ts.map +1 -0
  34. package/dist/skills-manager/index.js +4 -0
  35. package/dist/skills-manager/index.js.map +1 -0
  36. package/dist/skills-manager/state-store.d.ts +57 -0
  37. package/dist/skills-manager/state-store.d.ts.map +1 -0
  38. package/dist/skills-manager/state-store.js +129 -0
  39. package/dist/skills-manager/state-store.js.map +1 -0
  40. package/dist/skills-manager/types.d.ts +165 -0
  41. package/dist/skills-manager/types.d.ts.map +1 -0
  42. package/dist/skills-manager/types.js +2 -0
  43. package/dist/skills-manager/types.js.map +1 -0
  44. package/dist/skills-manager/utils.d.ts +63 -0
  45. package/dist/skills-manager/utils.d.ts.map +1 -0
  46. package/dist/skills-manager/utils.js +119 -0
  47. package/dist/skills-manager/utils.js.map +1 -0
  48. package/dist/skills-manager/version-manager.d.ts +146 -0
  49. package/dist/skills-manager/version-manager.d.ts.map +1 -0
  50. package/dist/skills-manager/version-manager.js +299 -0
  51. package/dist/skills-manager/version-manager.js.map +1 -0
  52. package/dist/skills-manager/writer.d.ts +28 -0
  53. package/dist/skills-manager/writer.d.ts.map +1 -0
  54. package/dist/skills-manager/writer.js +101 -0
  55. package/dist/skills-manager/writer.js.map +1 -0
  56. package/openclaw.plugin.json +27 -0
  57. package/package.json +3 -1
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Installs a skill from a cloned repo directory into the target skills
3
+ * directory using a crash-safe swap flow.
4
+ */
5
+ import type { LoggerLike } from "./types.js";
6
+ /**
7
+ * Copy a skill's subdirectory from `cloneDir` into `skillsDir/<skillName>/`,
8
+ * replacing the existing copy via a crash-safe swap.
9
+ *
10
+ * Strategy:
11
+ * 1. Validate source directory exists in the clone.
12
+ * 2. Write to a `.tmp-<skillName>` staging path inside `skillsDir`.
13
+ * 3. Move existing target to a hidden backup path (if present).
14
+ * 4. Move staging → target (atomic on same fs).
15
+ * 5. Delete the backup only after the new target is in place.
16
+ *
17
+ * This avoids destroying the previous skill copy before the new one is ready.
18
+ * There is still a brief window where `targetPath` may be absent, but the old
19
+ * content remains recoverable from the backup path until install succeeds.
20
+ *
21
+ * The `.tmp-` prefix hides the staging directory from skill discovery, which
22
+ * only reads immediate subdirectories of `skillsDir`.
23
+ *
24
+ * @throws When the skill subdirectory is absent in the clone.
25
+ * @throws When the staging copy fails.
26
+ */
27
+ export declare function installSkill(cloneDir: string, skillName: string, skillsDir: string, logger?: LoggerLike): Promise<void>;
28
+ //# sourceMappingURL=writer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writer.d.ts","sourceRoot":"","sources":["../../src/skills-manager/writer.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAOH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,YAAY,CAChC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,UAAU,GAClB,OAAO,CAAC,IAAI,CAAC,CA6Ff"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Installs a skill from a cloned repo directory into the target skills
3
+ * directory using a crash-safe swap flow.
4
+ */
5
+ import * as fs from "node:fs/promises";
6
+ import { randomUUID } from "node:crypto";
7
+ import { join } from "node:path";
8
+ import { ALLOWED_SKILLS } from "./constants.js";
9
+ import { formatErr } from "./utils.js";
10
+ /**
11
+ * Copy a skill's subdirectory from `cloneDir` into `skillsDir/<skillName>/`,
12
+ * replacing the existing copy via a crash-safe swap.
13
+ *
14
+ * Strategy:
15
+ * 1. Validate source directory exists in the clone.
16
+ * 2. Write to a `.tmp-<skillName>` staging path inside `skillsDir`.
17
+ * 3. Move existing target to a hidden backup path (if present).
18
+ * 4. Move staging → target (atomic on same fs).
19
+ * 5. Delete the backup only after the new target is in place.
20
+ *
21
+ * This avoids destroying the previous skill copy before the new one is ready.
22
+ * There is still a brief window where `targetPath` may be absent, but the old
23
+ * content remains recoverable from the backup path until install succeeds.
24
+ *
25
+ * The `.tmp-` prefix hides the staging directory from skill discovery, which
26
+ * only reads immediate subdirectories of `skillsDir`.
27
+ *
28
+ * @throws When the skill subdirectory is absent in the clone.
29
+ * @throws When the staging copy fails.
30
+ */
31
+ export async function installSkill(cloneDir, skillName, skillsDir, logger) {
32
+ if (!ALLOWED_SKILLS.includes(skillName)) {
33
+ throw new Error(`Skill "${skillName}" is not in the allowed skills list: ${ALLOWED_SKILLS.join(", ")}`);
34
+ }
35
+ const sourcePath = join(cloneDir, skillName);
36
+ const targetPath = join(skillsDir, skillName);
37
+ const stagingPath = join(skillsDir, `.tmp-${skillName}`);
38
+ const backupPath = join(skillsDir, `.bak-${skillName}-${randomUUID()}`);
39
+ logger?.info(`[senpi-skills-manager] installing skill | skill=${skillName} from=${sourcePath} to=${targetPath}`);
40
+ // Validate source exists and is a directory
41
+ try {
42
+ const info = await fs.stat(sourcePath);
43
+ if (!info.isDirectory()) {
44
+ throw new Error(`Expected a directory at "${sourcePath}" for skill "${skillName}" but found a file`);
45
+ }
46
+ }
47
+ catch (err) {
48
+ if (err.code === "ENOENT") {
49
+ throw new Error(`Skill "${skillName}" not found in cloned repo at "${sourcePath}"`);
50
+ }
51
+ throw err;
52
+ }
53
+ // Ensure skillsDir exists
54
+ await fs.mkdir(skillsDir, { recursive: true });
55
+ // Remove any leftover staging dir from a previous failed install
56
+ await fs.rm(stagingPath, { recursive: true, force: true });
57
+ logger?.info(`[senpi-skills-manager] cleared leftover staging dir | path=${stagingPath}`);
58
+ // Copy source → staging
59
+ logger?.info(`[senpi-skills-manager] copying skill to staging | from=${sourcePath} to=${stagingPath}`);
60
+ await fs.cp(sourcePath, stagingPath, { recursive: true });
61
+ // Move current target out of the way first so old data is recoverable on failure.
62
+ let movedTargetToBackup = false;
63
+ try {
64
+ await fs.rename(targetPath, backupPath);
65
+ movedTargetToBackup = true;
66
+ logger?.info(`[senpi-skills-manager] backed up existing install | from=${targetPath} to=${backupPath}`);
67
+ }
68
+ catch (err) {
69
+ if (err.code !== "ENOENT") {
70
+ throw err;
71
+ }
72
+ logger?.info(`[senpi-skills-manager] no existing install to back up | skill=${skillName}`);
73
+ }
74
+ try {
75
+ logger?.info(`[senpi-skills-manager] swapping staging to target | from=${stagingPath} to=${targetPath}`);
76
+ await fs.rename(stagingPath, targetPath);
77
+ }
78
+ catch (err) {
79
+ logger?.warn(`[senpi-skills-manager] swap failed, rolling back | skill=${skillName} err=${formatErr(err)}`);
80
+ // Remove any partial target before restoring old contents.
81
+ await fs.rm(targetPath, { recursive: true, force: true });
82
+ if (movedTargetToBackup) {
83
+ await fs.rename(backupPath, targetPath).catch((restoreErr) => {
84
+ logger?.warn(`[senpi-skills-manager] failed to restore backup after swap failure | skill=${skillName} backup=${backupPath} err=${formatErr(restoreErr)}`);
85
+ });
86
+ }
87
+ throw err;
88
+ }
89
+ finally {
90
+ // On successful rename this path no longer exists; rm is still safe with force=true.
91
+ await fs.rm(stagingPath, { recursive: true, force: true });
92
+ }
93
+ // Backup cleanup is best-effort once the new target is in place.
94
+ if (movedTargetToBackup) {
95
+ await fs.rm(backupPath, { recursive: true, force: true }).catch((err) => {
96
+ logger?.warn(`[senpi-skills-manager] failed to remove backup dir | path=${backupPath} err=${formatErr(err)}`);
97
+ });
98
+ }
99
+ logger?.info(`[senpi-skills-manager] skill installed successfully | skill=${skillName} path=${targetPath}`);
100
+ }
101
+ //# sourceMappingURL=writer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"writer.js","sourceRoot":"","sources":["../../src/skills-manager/writer.ts"],"names":[],"mappings":"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"}
@@ -71,6 +71,33 @@
71
71
  }
72
72
  }
73
73
  },
74
+ "skillsManager": {
75
+ "type": "object",
76
+ "additionalProperties": false,
77
+ "description": "Skills manager configuration. Checks the senpi-skills repo for updates to declared skills after each plugin auto-update tick and applies patch/minor updates automatically (major updates are notify-only). Cadence is inherited from `autoUpdate.pollIntervalMinutes`; this block has no independent poll interval. Omit this block to use defaults.",
78
+ "default": {
79
+ "enabled": true,
80
+ "skills": ["senpi-trading-runtime"]
81
+ },
82
+ "properties": {
83
+ "enabled": {
84
+ "type": "boolean",
85
+ "description": "Enable or disable the skills manager (default: true)"
86
+ },
87
+ "skillsDir": {
88
+ "oneOf": [
89
+ { "type": "string" },
90
+ { "type": "array", "items": { "type": "string", "minLength": 1 }, "minItems": 1 }
91
+ ],
92
+ "description": "Directory (or directories) where managed skills are installed. Accepts a single string for one dir, or an array for a search path. With multiple, the first is the *primary* (writes go here on bootstrap/install); the rest are read-only mirrors scanned during version checks and discovery. Default: /data/.openclaw/skills. Env override: SENPI_SKILLS_DIR (comma-separated for multi-dir; use the JSON array form for paths containing commas)."
93
+ },
94
+ "skills": {
95
+ "type": "array",
96
+ "items": { "type": "string" },
97
+ "description": "Explicit skill names to manage. Omitting the field falls back to the canonical default [\"senpi-trading-runtime\"]; passing an explicit empty array [] switches to discovery mode (scan subdirs under skillsDir)."
98
+ }
99
+ }
100
+ },
74
101
  "api": {
75
102
  "type": "object",
76
103
  "additionalProperties": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@senpi/runtime",
3
- "version": "1.1.0-dev.runtime-phase-2.20260512102629",
3
+ "version": "1.1.0-dev.runtime-phase-2.20260514134437",
4
4
  "description": "Senpi runtime plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -33,6 +33,7 @@
33
33
  "@anthropic-ai/sdk": "^0.39.0",
34
34
  "@modelcontextprotocol/sdk": "^1.0.0",
35
35
  "@sinclair/typebox": "^0.34.0",
36
+ "async-retry": "^1.3.3",
36
37
  "dd-trace": "^5.99.1",
37
38
  "dotenv": "^16.4.5",
38
39
  "fastify": "^5.8.5",
@@ -44,6 +45,7 @@
44
45
  "openclaw": ">=2026.2.0"
45
46
  },
46
47
  "devDependencies": {
48
+ "@types/async-retry": "^1.4.9",
47
49
  "@types/node": "^22.0.0",
48
50
  "tsx": "^4.21.0",
49
51
  "typescript": "^5.7.0",