@karmaniverous/jeeves-meta-openclaw 0.13.3 → 0.15.0-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,16 +1,14 @@
1
1
  # @karmaniverous/jeeves-meta-openclaw
2
2
 
3
- OpenClaw plugin for [jeeves-meta](../service/). A thin HTTP client that registers interactive tools and uses [`@karmaniverous/jeeves`](https://github.com/karmaniverous/jeeves) core for managed TOOLS.md content writing and platform maintenance.
3
+ OpenClaw plugin for [jeeves-meta](https://github.com/karmaniverous/jeeves-meta/tree/main/packages/service). A standard OpenClaw plugin and thin HTTP client that registers interactive tools on top of the [`@karmaniverous/jeeves`](https://github.com/karmaniverous/jeeves) core SDK. It writes no workspace files: static platform content is rendered by `jeeves install`, and live state is served by the tools.
4
4
 
5
5
  ## Features
6
6
 
7
7
  - **Twelve tools** — standard: `meta_status`, `meta_config`, `meta_config_apply`, `meta_service`; custom: `meta_list`, `meta_detail`, `meta_trigger`, `meta_preview`, `meta_seed`, `meta_unlock`, `meta_queue` (list/clear/abort), `meta_update`
8
8
  - **MetaServiceClient** — typed HTTP client delegating all operations to the running service
9
- - **TOOLS.md injection** — periodic refresh of entity stats, phase-state summary, failed-phase alerts, and next-phase indicator via `ComponentWriter` from `@karmaniverous/jeeves` (73-second prime interval)
10
9
  - **Phase-state awareness** — tools expose per-meta `_phaseState`, `owedPhase`, and phase-state summary from the service's phase-state machine
11
- - **Cleanup escalation** — passes `gatewayUrl` into `ComponentWriter` so managed-content cleanup can request a gateway session when needed
12
- - **Dependency health** — shows warnings when watcher/gateway are degraded
13
- - **Consumer skill** — `SKILL.md` for agent integration
10
+ - **Lazy config** — registers with no config; `configRoot` is resolved when a tool needs it
11
+ - **Consumer skill** — `jeeves-meta` skill shipped through the plugin manifest
14
12
 
15
13
  ## Plugin Lifecycle
16
14
 
@@ -18,24 +16,28 @@ OpenClaw plugin for [jeeves-meta](../service/). A thin HTTP client that register
18
16
 
19
17
  ## Install
20
18
 
19
+ Install with the Jeeves CLI, which runs the OpenClaw install and writes the plugin config:
20
+
21
21
  ```bash
22
- npm install @karmaniverous/jeeves-meta-openclaw
22
+ jeeves install meta --config-root j:/config
23
23
  ```
24
24
 
25
- Then run the CLI installer to register with the OpenClaw gateway:
25
+ Or install it as a standard OpenClaw plugin and configure it yourself:
26
26
 
27
27
  ```bash
28
- npx @karmaniverous/jeeves-meta-openclaw install
28
+ openclaw plugins install npm:@karmaniverous/jeeves-meta-openclaw@<version> --pin --accept-capabilities
29
29
  ```
30
30
 
31
+ Restart the gateway afterwards. There is no plugin-specific installer (the `npx @karmaniverous/jeeves-meta-openclaw install` CLI was removed in favour of `jeeves install`).
32
+
31
33
  ## Configuration
32
34
 
33
- The plugin resolves settings via a three-step fallback chain: plugin config → environment variable → default.
35
+ The plugin resolves settings via a fallback chain: plugin config → environment variable → default.
34
36
 
35
37
  | Setting | Plugin Config Key | Env Var | Default |
36
- |---------|-------------------|---------|---------|
38
+ | --- | --- | --- | --- |
37
39
  | Service URL | `apiUrl` | `JEEVES_META_URL` | `http://127.0.0.1:1938` |
38
- | Config Root | `configRoot` | `JEEVES_CONFIG_ROOT` | `j:/config` |
40
+ | Config Root | `configRoot` | `JEEVES_CONFIG_ROOT` | _none_ |
39
41
 
40
42
  ```json
41
43
  {
@@ -53,14 +55,21 @@ The plugin resolves settings via a three-step fallback chain: plugin config →
53
55
  }
54
56
  ```
55
57
 
56
- The `configRoot` setting tells `@karmaniverous/jeeves` core where to find the platform config directory. Core derives `{configRoot}/jeeves-meta/` for component-specific configuration.
58
+ The `configRoot` setting tells `@karmaniverous/jeeves` core where to find the platform config directory. Core derives `{configRoot}/jeeves-meta/` for component-specific configuration. It has no default: `jeeves install --config-root <path>` writes it.
59
+
60
+ `configRoot` is resolved **lazily**. Registration always succeeds without it (with a running gateway, `openclaw plugins install` activates the plugin before `jeeves install` writes its config), logging one warning:
61
+
62
+ ```text
63
+ [jeeves-meta] configRoot not configured yet — meta_service install will be unavailable until it is set in plugin config or JEEVES_CONFIG_ROOT
64
+ ```
65
+
66
+ Only `meta_service` with `action: "install"` reads it (core resolves the service config path from it); invoked without it, that call returns an error naming both ways to set it. Core `init()` runs the first time `configRoot` resolves. The other `meta_service` actions (`status`, `start`, `stop`, `restart`, `uninstall`) address the OS service by name, and every other tool only talks HTTP to the service at `apiUrl`, so all of them work without it.
57
67
 
58
68
  ## Documentation
59
69
 
60
70
  - **[Plugin Setup](guides/plugin-setup.md)** — installation, config, lifecycle
61
71
  - **[Tools Reference](guides/tools-reference.md)** — 12 tools: standard (meta_status, meta_config, meta_config_apply, meta_service) + custom (meta_list, meta_detail, meta_trigger, meta_preview, meta_seed, meta_unlock, meta_queue, meta_update)
62
72
  - **[Virtual Rules](guides/virtual-rules.md)** — watcher inference rules
63
- - **[TOOLS.md Injection](guides/tools-injection.md)** — dynamic prompt generation
64
73
 
65
74
  ## License
66
75
 
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
- import { resolvePluginSetting, resolveOptionalPluginSetting, fetchJson, postJson, ok, connectionFail, createPluginToolset, resolveWorkspacePath, init, loadWorkspaceConfig, WORKSPACE_CONFIG_DEFAULTS, createAsyncContentCache, jeevesComponentDescriptorSchema, getPackageVersion, createComponentWriter } from '@karmaniverous/jeeves';
2
- import { phaseStatuses, phaseNames, getEndpoint, META_COMPONENT } from '@karmaniverous/jeeves-meta-core';
1
+ import { resolvePluginSetting, resolveOptionalPluginSetting, fail, init, resolveWorkspacePath, getErrorMessage, fetchJson, postJson, ok, connectionFail, createPluginToolset, jeevesComponentDescriptorSchema, getPackageVersion } from '@karmaniverous/jeeves';
2
+ import { getEndpoint, META_COMPONENT } from '@karmaniverous/jeeves-meta-core';
3
+ import { z } from 'zod';
3
4
 
4
5
  /**
5
6
  * Shared constants for the jeeves-meta OpenClaw plugin.
@@ -14,184 +15,97 @@ const PLUGIN_ID = 'jeeves-meta-openclaw';
14
15
  *
15
16
  * @module helpers
16
17
  */
18
+ /** Environment variable consulted when plugin config has no `configRoot`. */
19
+ const CONFIG_ROOT_ENV = 'JEEVES_CONFIG_ROOT';
20
+ /** Error returned by tools that need `configRoot` when it is not set. */
21
+ const CONFIG_ROOT_MISSING_MESSAGE = `configRoot not configured — set it in plugin config (plugins.entries.${PLUGIN_ID}.config.configRoot) or via ${CONFIG_ROOT_ENV}`;
17
22
  /** Resolve the meta service URL. */
18
23
  function getServiceUrl(api) {
19
24
  return resolvePluginSetting(api, PLUGIN_ID, 'apiUrl', 'JEEVES_META_URL', 'http://127.0.0.1:1938');
20
25
  }
21
- /** Resolve the platform config root. */
22
- function getConfigRoot(api) {
23
- const value = resolveOptionalPluginSetting(api, PLUGIN_ID, 'configRoot', 'JEEVES_CONFIG_ROOT');
24
- if (!value) {
25
- throw new Error('configRoot not configured — set it in plugin config or via JEEVES_CONFIG_ROOT env var');
26
- }
27
- return value;
28
- }
29
-
30
26
  /**
31
- * Generate the Meta menu content for TOOLS.md injection.
27
+ * Resolve the platform config root without throwing.
32
28
  *
33
- * Queries the jeeves-meta service for entity stats and produces
34
- * a Markdown section suitable for agent system prompt injection.
29
+ * Order: the plugin's own config (`api.pluginConfig`), then
30
+ * `plugins.entries.<id>.config`, then the `JEEVES_CONFIG_ROOT` env var.
35
31
  *
36
- * @module promptInjection
32
+ * @returns The config root, or `undefined` when none is set.
37
33
  */
38
- /** Phase statuses that require per-phase breakdown (everything except fresh). */
39
- const nonFreshStatuses = phaseStatuses.filter((s) => s !== 'fresh');
34
+ function resolveConfigRoot(api) {
35
+ const own = api.pluginConfig?.['configRoot'];
36
+ if (typeof own === 'string' && own)
37
+ return own;
38
+ return (resolveOptionalPluginSetting(api, PLUGIN_ID, 'configRoot', CONFIG_ROOT_ENV) || undefined);
39
+ }
40
+
40
41
  /**
41
- * Format a duration in seconds as a compound human-readable interval.
42
+ * Lazy `configRoot` resolution for the jeeves-meta plugin.
42
43
  *
43
- * Produces at most two units: `2d 3h`, `1h 35m`, `45m`, etc.
44
- * Returns `'never synthesized'` for non-finite input.
44
+ * With a running gateway, `openclaw plugins install` activates the plugin
45
+ * before `jeeves install` writes its config, so `register()` must not
46
+ * depend on `configRoot`. The gate resolves it on first use, initializes
47
+ * core once it is known, and lets tools that need it fail cleanly.
45
48
  *
46
- * @param seconds - Duration in seconds.
47
- * @returns Formatted interval string.
49
+ * @module configRoot
48
50
  */
49
- function formatAge(seconds) {
50
- if (!isFinite(seconds))
51
- return 'never synthesized';
52
- const d = Math.floor(seconds / 86400);
53
- const h = Math.floor((seconds % 86400) / 3600);
54
- const m = Math.floor((seconds % 3600) / 60);
55
- if (d > 0 && h > 0)
56
- return `${d.toString()}d ${h.toString()}h`;
57
- if (d > 0)
58
- return `${d.toString()}d`;
59
- if (h > 0 && m > 0)
60
- return `${h.toString()}h ${m.toString()}m`;
61
- if (h > 0)
62
- return `${h.toString()}h`;
63
- return `${Math.max(m, 1).toString()}m`;
64
- }
51
+ /** Warning logged once at registration when `configRoot` is unset. */
52
+ const CONFIG_ROOT_WARNING = `[jeeves-meta] configRoot not configured yet — meta_service install will be unavailable until it is set in plugin config or ${CONFIG_ROOT_ENV}`;
65
53
  /**
66
- * Generate the Meta menu Markdown for TOOLS.md.
54
+ * Create a gate over the plugin API.
67
55
  *
68
- * @param client - MetaServiceClient instance.
69
- * @returns Markdown string for the Meta section.
56
+ * Nothing is resolved or initialized until {@link ConfigRootGate.ensure}
57
+ * is called, so config written after registration is picked up.
70
58
  */
71
- async function generateMetaMenu(client) {
72
- const [status, metas] = await Promise.all([
73
- client.status(),
74
- client.listMetas(),
75
- ]);
76
- if (metas.summary.total === 0) {
77
- return [
78
- '> **ACTION REQUIRED: No synthesis entities found.**',
79
- '> The service is running but no `.meta/` directories were discovered.',
80
- '>',
81
- "> **Read the `jeeves-meta` skill's Bootstrapping section** for guidance",
82
- '> on creating `.meta/` directories.',
83
- ].join('\n');
84
- }
85
- const { summary } = metas;
86
- // Find stalest age
87
- let stalestAge = 0;
88
- for (const item of metas.metas) {
89
- const s = item.stalenessSeconds !== null ? item.stalenessSeconds : Infinity;
90
- if (s > stalestAge)
91
- stalestAge = s;
92
- }
93
- const stalestDisplay = summary.stalestPath
94
- ? summary.stalestPath + ' (' + formatAge(stalestAge) + ')'
95
- : 'n/a';
96
- const lastSynthDisplay = summary.lastSynthesizedAt
97
- ? (summary.lastSynthesizedPath ?? '') +
98
- ' (' +
99
- summary.lastSynthesizedAt +
100
- ')'
101
- : 'n/a';
102
- // Service status + dependency health
103
- // The core SDK's createStatusHandler nests getHealth() under `health`.
104
- const { dependencies } = status.health;
105
- const depLines = [];
106
- if (dependencies.watcher.status === 'indexing') {
107
- depLines.push('> ⏳ **Watcher indexing**: Initial filesystem scan in progress. Synthesis will resume when complete.');
108
- }
109
- else if (dependencies.watcher.status !== 'ok' &&
110
- dependencies.watcher.status !== 'indexing') {
111
- depLines.push('> ⚠️ **Watcher**: ' + dependencies.watcher.status);
112
- }
113
- if (dependencies.watcher.rulesRegistered === false &&
114
- dependencies.watcher.status === 'ok') {
115
- depLines.push('> ⚠️ **Watcher rules not registered**: Meta files may not render properly in search/server.');
116
- }
117
- if (dependencies.gateway.status !== 'ok') {
118
- depLines.push('> ⚠️ **Gateway**: ' + dependencies.gateway.status);
119
- }
120
- // Phase-state summary from /status health
121
- const phaseLines = [];
122
- const phaseSummary = status.health.phaseStateSummary;
123
- if (phaseSummary) {
124
- // Fresh counts are aggregated; non-fresh show <count> <state> <phase>
125
- let freshTotal = 0;
126
- for (const phase of phaseNames) {
127
- freshTotal += phaseSummary[phase].fresh;
128
- }
129
- const parts = [];
130
- if (freshTotal > 0)
131
- parts.push(freshTotal.toString() + ' fresh');
132
- for (const phase of phaseNames) {
133
- for (const state of nonFreshStatuses) {
134
- const count = phaseSummary[phase][state];
135
- if (count > 0)
136
- parts.push(`${count.toString()} ${state} ${phase}`);
59
+ function createConfigRootGate(api) {
60
+ let resolved;
61
+ return {
62
+ ensure: () => {
63
+ if (resolved)
64
+ return { ok: true, configRoot: resolved };
65
+ const configRoot = resolveConfigRoot(api);
66
+ if (!configRoot)
67
+ return { ok: false, error: CONFIG_ROOT_MISSING_MESSAGE };
68
+ try {
69
+ init({ workspacePath: resolveWorkspacePath(api), configRoot });
137
70
  }
138
- }
139
- if (parts.length > 0) {
140
- phaseLines.push('Phases: ' + parts.join(', '));
141
- }
142
- // Failed-phase alert
143
- const failedParts = [];
144
- for (const item of metas.metas) {
145
- const ps = item.phaseState;
146
- if (!ps)
147
- continue;
148
- for (const phase of phaseNames) {
149
- if (ps[phase] === 'failed') {
150
- const p = item.path;
151
- failedParts.push(p + ' (' + phase + ')');
152
- }
71
+ catch (error) {
72
+ return {
73
+ ok: false,
74
+ error: `jeeves core init failed: ${getErrorMessage(error)}`,
75
+ };
153
76
  }
154
- }
155
- if (failedParts.length > 0) {
156
- phaseLines.push('⚠ Failed: ' +
157
- failedParts.slice(0, 10).join(', ') +
158
- (failedParts.length > 10
159
- ? ' (+' + (failedParts.length - 10).toString() + ' more)'
160
- : ''));
161
- }
162
- }
163
- // Next-phase indicator from /status health
164
- const nextPhase = status.health.nextPhase;
165
- if (nextPhase) {
166
- phaseLines.push('Next: ' +
167
- nextPhase.path +
168
- ' → ' +
169
- nextPhase.phase +
170
- ' (band ' +
171
- nextPhase.band.toString() +
172
- ', staleness ' +
173
- formatAge(nextPhase.staleness) +
174
- ')');
175
- }
176
- return [
177
- 'The jeeves-meta synthesis engine manages ' +
178
- summary.total.toString() +
179
- ' meta entities.',
180
- '',
181
- '### Entity Summary',
182
- '| Metric | Value |',
183
- '|--------|-------|',
184
- '| Total | ' + summary.total.toString() + ' |',
185
- '| Stale | ' + summary.stale.toString() + ' |',
186
- '| Errors | ' + summary.errors.toString() + ' |',
187
- '| Never synthesized | ' + summary.neverSynthesized.toString() + ' |',
188
- '| Stalest | ' + stalestDisplay + ' |',
189
- '| Last synthesized | ' + lastSynthDisplay + ' |',
190
- ...(phaseLines.length > 0 ? ['', '### Phase State', ...phaseLines] : []),
191
- ...(depLines.length > 0 ? ['', '### Dependencies', ...depLines] : []),
192
- '',
193
- 'Read the `jeeves-meta` skill for usage guidance, configuration, and troubleshooting.',
194
- ].join('\n');
77
+ resolved = configRoot;
78
+ return { ok: true, configRoot };
79
+ },
80
+ };
81
+ }
82
+ /** Log {@link CONFIG_ROOT_WARNING} once if `configRoot` cannot be resolved now. */
83
+ function warnIfConfigRootMissing(api) {
84
+ if (resolveConfigRoot(api))
85
+ return;
86
+ const warn = api.logger?.warn ??
87
+ ((m) => {
88
+ console.warn(m);
89
+ });
90
+ warn(CONFIG_ROOT_WARNING);
91
+ }
92
+ /**
93
+ * Wrap a tool so calls that read `configRoot` return a clear error until
94
+ * it resolves. Calls for which `needsConfigRoot` is false pass straight
95
+ * through without touching the gate.
96
+ */
97
+ function requireConfigRoot(tool, gate, needsConfigRoot = () => true) {
98
+ return {
99
+ ...tool,
100
+ execute: (id, params) => {
101
+ if (!needsConfigRoot(params))
102
+ return tool.execute(id, params);
103
+ const check = gate.ensure();
104
+ if (!check.ok)
105
+ return Promise.resolve(fail(check.error));
106
+ return tool.execute(id, params);
107
+ },
108
+ };
195
109
  }
196
110
 
197
111
  /**
@@ -580,12 +494,30 @@ function buildMetaUpdateTool(client, baseUrl) {
580
494
  *
581
495
  * @module tools
582
496
  */
497
+ /**
498
+ * Tool calls that read `configRoot`, keyed by tool name.
499
+ *
500
+ * Only `meta_service` with `action: 'install'` reads it: core's service
501
+ * manager resolves the component config path via `getComponentConfigDir()`.
502
+ * The other `meta_service` actions drive the OS service manager by name,
503
+ * and every other tool only talks HTTP to the meta service at `apiUrl`,
504
+ * so none of them are gated.
505
+ */
506
+ const CONFIG_ROOT_TOOLS = new Map([
507
+ ['meta_service', (params) => params.action === 'install'],
508
+ ]);
583
509
  /** Register all meta_* tools (standard + custom). */
584
- function registerMetaTools(api, client, descriptor) {
510
+ function registerMetaTools(api, client, descriptor, gate) {
585
511
  const baseUrl = client.getBaseUrl();
586
- // Standard tools from factory: meta_status, meta_config, meta_config_apply, meta_service
587
- for (const tool of createPluginToolset(descriptor)) {
588
- api.registerTool(tool);
512
+ // Standard tools from factory: meta_status, meta_config, meta_config_apply, meta_service.
513
+ // `apiUrl` is resolved on every call with the same resolution as the
514
+ // custom tools, so the standard HTTP tools hit the configured service.
515
+ const toolset = createPluginToolset(descriptor, {
516
+ apiUrl: () => getServiceUrl(api),
517
+ });
518
+ for (const tool of toolset) {
519
+ const needsConfigRoot = CONFIG_ROOT_TOOLS.get(tool.name);
520
+ api.registerTool(needsConfigRoot ? requireConfigRoot(tool, gate, needsConfigRoot) : tool);
589
521
  }
590
522
  // Custom domain-specific tools
591
523
  for (const tool of buildCustomTools(client, baseUrl)) {
@@ -593,59 +525,46 @@ function registerMetaTools(api, client, descriptor) {
593
525
  }
594
526
  }
595
527
 
528
+ /**
529
+ * Zod schema for the jeeves-meta OpenClaw plugin configuration.
530
+ *
531
+ * This is the source of truth for the plugin configSchema.
532
+ * The build-time script `scripts/generate-plugin-schema.mjs` derives
533
+ * the JSON Schema in `openclaw.plugin.json` from this definition.
534
+ *
535
+ * @module pluginConfigSchema
536
+ */
537
+ /** Zod schema for the jeeves-meta OpenClaw plugin configuration. */
538
+ const pluginConfigSchema = z.object({
539
+ apiUrl: z
540
+ .string()
541
+ .default('http://127.0.0.1:1938')
542
+ .describe('URL of the jeeves-meta HTTP service. Falls back to JEEVES_META_URL env var.'),
543
+ configRoot: z
544
+ .string()
545
+ .optional()
546
+ .describe('Absolute path to the platform config root (e.g. j:/config). Used by @karmaniverous/jeeves core for config directory resolution.'),
547
+ });
548
+
596
549
  /**
597
550
  * OpenClaw plugin for jeeves-meta.
598
551
  *
599
552
  * Thin HTTP client — all operations delegate to the jeeves-meta service.
600
- * Uses `@karmaniverous/jeeves` core for TOOLS.md and platform content.
553
+ * A standard OpenClaw plugin: it writes no workspace files and is installed
554
+ * by `jeeves install` (or `openclaw plugins install`).
601
555
  *
602
556
  * @packageDocumentation
603
557
  */
604
- /** Register all jeeves-meta tools with the OpenClaw plugin API. */
605
- function register(api) {
606
- const client = new MetaServiceClient({ apiUrl: getServiceUrl(api) });
607
- const workspacePath = resolveWorkspacePath(api);
608
- init({
609
- workspacePath,
610
- configRoot: getConfigRoot(api),
611
- });
612
- const gatewayUrl = loadWorkspaceConfig(workspacePath)?.core?.gatewayUrl ??
613
- WORKSPACE_CONFIG_DEFAULTS.core.gatewayUrl;
614
- const placeholder = 'The jeeves-meta synthesis engine is initializing...\n\n' +
615
- 'Read the `jeeves-meta` skill for usage guidance, configuration, and troubleshooting.';
616
- let consecutive503s = 0;
617
- const getContent = createAsyncContentCache({
618
- fetch: async () => {
619
- const content = await generateMetaMenu(client);
620
- consecutive503s = 0;
621
- return content;
622
- },
623
- placeholder,
624
- onError: (error) => {
625
- const msg = error instanceof Error ? error.message : String(error);
626
- if (/HTTP 503\b/i.test(msg) || /scan.in.progress/i.test(msg)) {
627
- consecutive503s++;
628
- if (consecutive503s === 1) {
629
- console.warn('[jeeves-meta] Watcher scan still in progress — will retry on next refresh cycle.');
630
- }
631
- else {
632
- console.debug(`[jeeves-meta] Watcher scan still in progress (attempt ${String(consecutive503s)}) — suppressing repeated warnings.`);
633
- }
634
- return;
635
- }
636
- consecutive503s = 0;
637
- console.warn('[jeeves-meta] Content fetch failed:', msg);
638
- },
639
- });
640
- const descriptor = jeevesComponentDescriptorSchema.parse({
558
+ /** Build the plugin-side component descriptor used by the standard toolset. */
559
+ function buildDescriptor() {
560
+ return jeevesComponentDescriptorSchema.parse({
641
561
  name: META_COMPONENT.name,
642
562
  version: getPackageVersion(import.meta.url),
643
563
  servicePackage: META_COMPONENT.servicePackage,
644
564
  pluginPackage: META_COMPONENT.pluginPackage,
645
565
  defaultPort: META_COMPONENT.defaultPort,
646
- // The runtime Zod custom validator only checks for a .parse() method.
647
- // Use unknown cast to bridge the Zod v4 (service) → v3 (core SDK) type gap.
648
- configSchema: { parse: (v) => v },
566
+ // The plugin never validates service config; the service descriptor does.
567
+ configSchema: z.unknown(),
649
568
  configFileName: 'config.json',
650
569
  initTemplate: () => ({}),
651
570
  startCommand: (configPath) => [
@@ -655,19 +574,22 @@ function register(api) {
655
574
  '-c',
656
575
  configPath,
657
576
  ],
658
- // Plugin-side descriptor is only used by ComponentWriter for managed
659
- // content. The real run callback lives in the service descriptor.
577
+ // The real run callback lives in the service descriptor.
660
578
  run: () => {
661
579
  return Promise.reject(new Error('run() is not available on the plugin-side descriptor'));
662
580
  },
663
- sectionId: 'Meta',
664
- refreshIntervalSeconds: 73,
665
- generateToolsContent: getContent,
666
- dependencies: { hard: ['watcher'], soft: [] },
667
581
  });
668
- registerMetaTools(api, client, descriptor);
669
- const writer = createComponentWriter(descriptor, { gatewayUrl });
670
- writer.start();
582
+ }
583
+ /**
584
+ * Register all jeeves-meta tools with the OpenClaw plugin API.
585
+ *
586
+ * Always succeeds, even with no plugin config: `configRoot` is resolved
587
+ * lazily when a tool that needs it runs.
588
+ */
589
+ function register(api) {
590
+ const client = new MetaServiceClient({ apiUrl: getServiceUrl(api) });
591
+ warnIfConfigRootMissing(api);
592
+ registerMetaTools(api, client, buildDescriptor(), createConfigRootGate(api));
671
593
  }
672
594
 
673
- export { register as default };
595
+ export { register as default, pluginConfigSchema };
@@ -1,10 +1,9 @@
1
1
  /**
2
2
  * Rollup configuration for the OpenClaw plugin package.
3
- * Two entry points: plugin (ESM + declarations) and CLI (ESM executable).
3
+ * Single entry point: the plugin (ESM + declarations).
4
4
  *
5
- * Runtime dependencies (`@karmaniverous/jeeves`, `@karmaniverous/jeeves-meta`)
6
- * are externalized — the OpenClaw host environment provides them.
7
- * CLI bundles everything for standalone execution.
5
+ * Runtime dependencies (`@karmaniverous/jeeves`, `@karmaniverous/jeeves-meta-core`,
6
+ * `zod`) are externalized and resolved from the plugin's installed dependencies.
8
7
  */
9
8
  import type { RollupOptions } from 'rollup';
10
9
  declare const _default: RollupOptions[];