@karmaniverous/jeeves 0.1.5 → 0.2.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
@@ -16,7 +16,7 @@ That's it. I handle the rest.
16
16
 
17
17
  ## Who I Am
18
18
 
19
- My name is Jeeves.
19
+ My name is Jeeves.
20
20
 
21
21
  I add *identity* to OpenClaw: professional discipline, operational protocols, and a suite of services for data-wrangling, indexing, synthesis, and presentation.
22
22
 
@@ -60,19 +60,87 @@ I coordinate four service components. Each has its own repo, service, and OpenCl
60
60
  | [jeeves-runner](https://github.com/karmaniverous/jeeves-runner) | 1937 | Turing's paper in the *Proceedings* (1937) | Scheduled jobs, zero-LLM-cost scripts |
61
61
  | [jeeves-meta](https://github.com/karmaniverous/jeeves-meta) | 1938 | Shannon's switching circuits thesis (1938) | Three-step LLM synthesis |
62
62
 
63
- This package (`@karmaniverous/jeeves`) is the substrate they all share: managed workspace content, service discovery, config resolution, version-stamp convergence. It's a library and CLI. No daemon, no port, no tools registered with the gateway.
63
+ This package (`@karmaniverous/jeeves`) is the substrate they all share: managed workspace content, service discovery, config resolution, version-stamp convergence, and a Plugin SDK for building component plugins. It's a library and CLI. No daemon, no port, no tools registered with the gateway.
64
64
 
65
- ## For Platform Developers
65
+ ## Plugin SDK
66
66
 
67
- If you're building a component plugin, you implement one interface and call one factory:
67
+ The Plugin SDK (`src/plugin/`) provides canonical types and utilities for building OpenClaw plugins that integrate with the Jeeves platform.
68
+
69
+ ### Core Types
70
+
71
+ - **`PluginApi`** — the shape of the `api` object the OpenClaw gateway passes to plugins at registration time. Provides `config`, `resolvePath()`, and `registerTool()`.
72
+ - **`ToolResult`** — result shape returned by tool executions: an array of content blocks plus an optional `isError` flag.
73
+ - **`ToolDescriptor`** — tool definition for registration: `name`, `description`, `parameters` (JSON Schema), and an `execute` function.
74
+
75
+ ### Result Formatters
76
+
77
+ - **`ok(data)`** — wraps arbitrary data as a successful `ToolResult` with JSON-stringified content.
78
+ - **`fail(error)`** — wraps an error into a `ToolResult` with `isError: true`.
79
+ - **`connectionFail(error, baseUrl, pluginId)`** — detects `ECONNREFUSED`, `ENOTFOUND`, and `ETIMEDOUT` from `error.cause.code` and returns a user-friendly message referencing the plugin's `config.apiUrl` setting. Falls back to `fail()` for non-connection errors.
80
+
81
+ ### HTTP Helpers
82
+
83
+ - **`fetchJson(url, init?)`** — thin wrapper around `fetch` that throws on non-OK responses and returns parsed JSON.
84
+ - **`postJson(url, body)`** — POST JSON to a URL and return parsed response.
85
+
86
+ ### Resolution Helpers
87
+
88
+ - **`resolveWorkspacePath(api)`** — resolves the workspace root from the plugin API via a three-step chain: `api.config.agents.defaults.workspace` → `api.resolvePath('.')` → `process.cwd()`.
89
+ - **`resolvePluginSetting(api, pluginId, key, envVar, fallback)`** — resolves a plugin setting via: plugin config → environment variable → fallback value.
90
+
91
+ ### OpenClaw Config Utilities
92
+
93
+ - **`resolveOpenClawHome()`** — resolves the OpenClaw home directory: `OPENCLAW_CONFIG` env (dirname) → `OPENCLAW_HOME` env → `~/.openclaw`.
94
+ - **`resolveConfigPath(home)`** — resolves the OpenClaw config file path: `OPENCLAW_CONFIG` env → `{home}/openclaw.json`.
95
+ - **`patchConfig(config, pluginId, mode)`** — idempotent config patching for plugin install/uninstall. Manages `plugins.entries.{pluginId}` and `tools.alsoAllow`.
96
+
97
+ ## Config Query Handler
98
+
99
+ The `createConfigQueryHandler(getConfig)` factory produces a transport-agnostic handler for `GET /config` endpoints. It accepts a `getConfig` callback that returns the current config object.
100
+
101
+ - No `path` parameter → returns the full config document.
102
+ - Valid JSONPath expression → returns matching results with count (powered by `jsonpath-plus`).
103
+ - Invalid JSONPath → returns a 400 error.
104
+
105
+ Component services wire this into their HTTP server to expose config for diagnostic queries.
106
+
107
+ ## Managed Content System
108
+
109
+ The managed content system maintains SOUL.md, AGENTS.md, and TOOLS.md without destroying user-authored content.
110
+
111
+ ### Key Functions
112
+
113
+ - **`updateManagedSection(filePath, content, options)`** — writes managed content in either block mode (replaces entire managed block) or section mode (upserts a named H2 section within the block). Handles file locking, version-stamp convergence, cleanup detection, and atomic writes.
114
+ - **`removeManagedSection(filePath, options)`** — removes a specific section or the entire managed block. If the last section is removed, the entire block is removed.
115
+ - **`parseManaged(fileContent, markers)`** — parses a file into its managed block, version stamp, sections, and user content.
116
+ - **`atomicWrite(filePath, content)`** — writes via a temp file + rename to prevent partial writes.
117
+ - **`withFileLock(filePath, fn)`** — executes a callback while holding a file-level lock (2-minute stale threshold, 5 retries).
118
+
119
+ ### ManagedMarkers Type
120
+
121
+ ```typescript
122
+ interface ManagedMarkers {
123
+ begin: string; // BEGIN comment marker text
124
+ end: string; // END comment marker text
125
+ title?: string; // Optional H1 title prepended inside managed block
126
+ }
127
+ ```
128
+
129
+ Pre-defined marker sets: `TOOLS_MARKERS`, `SOUL_MARKERS`, `AGENTS_MARKERS`.
130
+
131
+ See the [Managed Content System](https://docs.karmanivero.us/jeeves/documents/Managed_Content_System.html) guide for the full deep-dive.
132
+
133
+ ## ComponentWriter and JeevesComponent
134
+
135
+ Component plugins implement the `JeevesComponent` interface and use `createComponentWriter()` to get a timer-based orchestrator:
68
136
 
69
137
  ```typescript
70
138
  import { init, createComponentWriter } from '@karmaniverous/jeeves';
71
139
  import type { JeevesComponent } from '@karmaniverous/jeeves';
72
140
 
73
141
  init({
74
- workspacePath: api.resolvePath('.'),
75
- configRoot: api.getConfig('configRoot'),
142
+ workspacePath: resolveWorkspacePath(api),
143
+ configRoot: resolvePluginSetting(api, pluginId, 'configRoot', 'JEEVES_CONFIG_ROOT', 'j:/config'),
76
144
  });
77
145
 
78
146
  const writer = createComponentWriter({
@@ -88,18 +156,29 @@ const writer = createComponentWriter({
88
156
  writer.start();
89
157
  ```
90
158
 
91
- The writer handles everything: your TOOLS.md section, platform content (SOUL/AGENTS/Platform), file locking, version stamps, cleanup detection.
159
+ On each cycle the writer calls `generateToolsContent()`, writes the component's TOOLS.md section, and runs `refreshPlatformContent()` to maintain SOUL.md, AGENTS.md, and the Platform section with live service health data.
160
+
161
+ The `createAsyncContentCache({ fetch, placeholder? })` utility bridges the sync `generateToolsContent` interface with async data sources — returns a sync `() => string` that serves cached content while refreshing in the background.
92
162
 
93
- See the [Building a Component Plugin](https://docs.karmanivero.us/jeeves/documents/guides_building-a-component-plugin.html) guide for the full walkthrough.
163
+ See the [Building a Component Plugin](https://docs.karmanivero.us/jeeves/documents/Building_a_Component_Plugin.html) guide for the full walkthrough.
164
+
165
+ ## Service Discovery
166
+
167
+ - **`getServiceUrl(serviceName, consumerName?)`** — resolves a service URL via: consumer config → core config → default port constants.
168
+ - **`probeService(serviceName, consumerName?, timeoutMs?)`** — probes `/status` then `/health` endpoints, returns a `ProbeResult` with health status and version.
169
+ - **`probeAllServices(consumerName?, timeoutMs?)`** — probes all known services (server, watcher, runner, meta).
170
+ - **`checkRegistryVersion(packageName, cacheDir, ttlSeconds?)`** — checks npm registry for the latest version with local file caching (default 1-hour TTL).
94
171
 
95
172
  ## CLI
96
173
 
97
174
  ```bash
98
- jeeves install # Bootstrap identity, protocols, platform content
99
- jeeves uninstall # Remove managed sections and artifacts
100
- jeeves status # Probe all service ports, report health
175
+ jeeves install # Seed identity, protocols, platform content; create core config
176
+ jeeves uninstall # Remove managed sections, templates, config schema
177
+ jeeves status # Probe all service ports, report health table
101
178
  ```
102
179
 
180
+ All three commands accept `--workspace <path>` and `--config-root <path>` options.
181
+
103
182
  ## Configuration
104
183
 
105
184
  Core config at `{configRoot}/jeeves-core/config.json`:
@@ -1,17 +1,7 @@
1
- {{#if versionInfo}}
2
- | Component | Service | Plugin | Core | Available |
3
- |-----------|---------|--------|------|-----------|
4
- {{#each versionInfo}}
5
- | **{{name}}** | {{#if serviceVersion}}{{serviceVersion}}{{else}}—{{/if}} | {{#if pluginVersion}}{{pluginVersion}}{{else}}—{{/if}} | {{coreVersion}} | {{#if availableVersion}}⬆ {{availableVersion}}{{else}}✓ current{{/if}} |
6
- {{/each}}
7
- {{/if}}
8
-
9
- ### Service Health
10
-
11
- | Service | Port | Status |
12
- |---------|------|--------|
1
+ | Component | Port | Status | Service | Plugin | Core |
2
+ |-----------|------|--------|---------|--------|------|
13
3
  {{#each services}}
14
- | {{name}} | {{port}} | {{#if healthy}}✅ Running{{#if version}} (v{{version}}){{/if}}{{else}}{{#if error}}⚠️ {{error}}{{else}}❌ Down{{/if}}{{/if}} |
4
+ | **{{name}}** | {{port}} | {{#if healthy}}✅ Running{{else}}{{#if error}}⚠️ {{error}}{{else}}❌ Down{{/if}}{{/if}} | {{#if version}}{{version}}{{#if availableServiceVersion}} (⬆ {{availableServiceVersion}}){{/if}}{{else}}—{{/if}} | {{#if pluginVersion}}{{pluginVersion}}{{#if availablePluginVersion}} (⬆ {{availablePluginVersion}}){{/if}}{{else}}—{{/if}} | {{../coreVersion}}{{#if ../availableCoreVersion}} (⬆ {{../availableCoreVersion}}){{/if}} |
15
5
  {{/each}}
16
6
 
17
7
  {{#if unhealthyServices}}