@karmaniverous/jeeves-server-openclaw 0.7.0 → 0.7.2

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/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
- import path, { join, dirname, resolve } from 'node:path';
3
- import { fileURLToPath } from 'node:url';
4
2
  import fs, { existsSync, copyFileSync, writeFileSync, readFileSync, rmSync, mkdirSync, readdirSync, renameSync, unlinkSync } from 'node:fs';
3
+ import path, { join, dirname, resolve, basename } from 'node:path';
4
+ import { randomUUID } from 'node:crypto';
5
5
  import require$$0$4 from 'path';
6
6
  import require$$0$3 from 'fs';
7
7
  import require$$0$1 from 'constants';
@@ -13,9 +13,9 @@ import 'vm';
13
13
  import require$$0$5 from 'node:events';
14
14
  import require$$1 from 'node:child_process';
15
15
  import process$2 from 'node:process';
16
- import 'node:fs/promises';
17
16
  import { homedir } from 'node:os';
18
- import 'node:crypto';
17
+ import { fileURLToPath } from 'node:url';
18
+ import 'node:fs/promises';
19
19
 
20
20
  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
21
21
 
@@ -15403,14 +15403,14 @@ const SECTION_ORDER = [
15403
15403
  * Core library version, inlined at build time.
15404
15404
  *
15405
15405
  * @remarks
15406
- * The `0.5.0` placeholder is replaced by
15406
+ * The `0.5.3` placeholder is replaced by
15407
15407
  * `@rollup/plugin-replace` during the build with the actual version
15408
15408
  * from `package.json`. This ensures the correct version survives
15409
15409
  * when consumers bundle core into their own dist (where runtime
15410
15410
  * `import.meta.url`-based resolution would find the wrong package.json).
15411
15411
  */
15412
15412
  /** The core library version from package.json (inlined at build time). */
15413
- const CORE_VERSION = '0.5.0';
15413
+ const CORE_VERSION = '0.5.3';
15414
15414
 
15415
15415
  /**
15416
15416
  * Workspace and config root initialization.
@@ -15460,27 +15460,46 @@ const STALE_LOCK_MS = 120_000;
15460
15460
  const DEFAULT_CORE_VERSION = CORE_VERSION;
15461
15461
  /** Lock retry options. */
15462
15462
  const LOCK_RETRIES = { retries: 5, minTimeout: 100, maxTimeout: 1000 };
15463
+ /** Maximum rename retry attempts on EPERM. */
15464
+ const ATOMIC_WRITE_MAX_RETRIES = 3;
15465
+ /** Delay between EPERM retries in milliseconds. */
15466
+ const ATOMIC_WRITE_RETRY_DELAY_MS = 100;
15463
15467
  /**
15464
15468
  * Write content to a file atomically via a temp file + rename.
15465
15469
  *
15470
+ * @remarks
15471
+ * Retries the rename up to three times on EPERM (Windows file-handle
15472
+ * contention) with a 100 ms synchronous delay between attempts.
15473
+ *
15466
15474
  * @param filePath - Absolute path to the target file.
15467
15475
  * @param content - Content to write.
15468
15476
  */
15469
15477
  function atomicWrite(filePath, content) {
15470
15478
  const dir = dirname(filePath);
15471
- const tempPath = join(dir, `.${String(Date.now())}.tmp`);
15479
+ const base = basename(filePath, '.md');
15480
+ const tempPath = join(dir, `.${base}.${String(Date.now())}.${randomUUID().slice(0, 8)}.tmp`);
15472
15481
  writeFileSync(tempPath, content, 'utf-8');
15473
- try {
15474
- renameSync(tempPath, filePath);
15475
- }
15476
- catch (err) {
15482
+ for (let attempt = 0; attempt < ATOMIC_WRITE_MAX_RETRIES; attempt++) {
15477
15483
  try {
15478
- unlinkSync(tempPath);
15484
+ renameSync(tempPath, filePath);
15485
+ return;
15479
15486
  }
15480
- catch {
15481
- /* best-effort cleanup */
15487
+ catch (err) {
15488
+ const isEperm = err instanceof Error &&
15489
+ 'code' in err &&
15490
+ err.code === 'EPERM';
15491
+ if (!isEperm || attempt === ATOMIC_WRITE_MAX_RETRIES - 1) {
15492
+ try {
15493
+ unlinkSync(tempPath);
15494
+ }
15495
+ catch {
15496
+ /* best-effort cleanup */
15497
+ }
15498
+ throw err;
15499
+ }
15500
+ // Synchronous sleep before retry (acceptable in atomic write context)
15501
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ATOMIC_WRITE_RETRY_DELAY_MS);
15482
15502
  }
15483
- throw err;
15484
15503
  }
15485
15504
  }
15486
15505
  /**
@@ -15745,7 +15764,7 @@ function parseHeartbeat(fileContent) {
15745
15764
  const userContent = fileContent.slice(0, headingIndex).trim();
15746
15765
  const sectionContent = fileContent.slice(headingIndex + HEARTBEAT_HEADING.length);
15747
15766
  const entries = [];
15748
- const h2Re = /^## (jeeves-\S+?|MEMORY\.md)(?:: declined)?$/gm;
15767
+ const h2Re = /^## (jeeves-\S+?|\S+\.md)(?:: declined)?$/gm;
15749
15768
  let match;
15750
15769
  const h2Positions = [];
15751
15770
  while ((match = h2Re.exec(sectionContent)) !== null) {
@@ -15826,6 +15845,15 @@ function sortSectionsByOrder(sections) {
15826
15845
  * sections within the block, and returns the structured result plus
15827
15846
  * user content outside the markers.
15828
15847
  */
15848
+ /**
15849
+ * Escape a string for safe use as a literal in a RegExp pattern.
15850
+ *
15851
+ * @param str - The string to escape.
15852
+ * @returns The escaped string.
15853
+ */
15854
+ function escapeForRegex(str) {
15855
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
15856
+ }
15829
15857
  /**
15830
15858
  * Build regex patterns for the given markers.
15831
15859
  *
@@ -15833,11 +15861,9 @@ function sortSectionsByOrder(sections) {
15833
15861
  * @returns Object with begin and end regex patterns.
15834
15862
  */
15835
15863
  function buildMarkerPatterns(markers) {
15836
- const escapedBegin = markers.begin.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
15837
- const escapedEnd = markers.end.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
15838
15864
  return {
15839
- beginRe: new RegExp(`^<!--\\s*${escapedBegin}(?:\\s*\\|[^>]*)?\\s*(?:—[^>]*)?\\s*-->\\s*$`, 'm'),
15840
- endRe: new RegExp(`^<!--\\s*${escapedEnd}\\s*-->\\s*$`, 'm'),
15865
+ beginRe: new RegExp(`^<!--\\s*${escapeForRegex(markers.begin)}(?:\\s*\\|[^>]*)?\\s*(?:—[^>]*)?\\s*-->\\s*$`, 'm'),
15866
+ endRe: new RegExp(`^<!--\\s*${escapeForRegex(markers.end)}\\s*-->\\s*$`, 'm'),
15841
15867
  };
15842
15868
  }
15843
15869
  /**
@@ -16162,6 +16188,23 @@ Review is human/agent-mediated — core does not auto-delete.
16162
16188
  Memory hygiene is checked on every \`ComponentWriter\` cycle alongside component health. When budget or staleness thresholds are breached, a \`## MEMORY.md\` alert appears in HEARTBEAT.md under \`# Jeeves Platform Status\`. The alert includes character count, budget usage percentage, and any stale section names. When memory is healthy, the heading is absent — no alert content, no LLM cost on heartbeat polls.
16163
16189
 
16164
16190
  The \`## MEMORY.md\` heading follows the same declined/active lifecycle as component headings (\`## jeeves-{name}\`). Users can decline memory alerts by changing the heading to \`## MEMORY.md: declined\`.
16191
+
16192
+ ## Workspace File Size Monitoring
16193
+
16194
+ OpenClaw applies a ~20,000-char injection limit to all workspace bootstrap files (AGENTS.md, SOUL.md, TOOLS.md, USER.md, MEMORY.md). Files exceeding the limit are silently truncated.
16195
+
16196
+ Core monitors all five files on every \`ComponentWriter\` cycle:
16197
+ - Warning at 80% of budget (fixed threshold; not configurable via \`jeeves.config.json\`)
16198
+ - Over-budget alert when charCount exceeds the budget
16199
+ - Missing files are silently skipped
16200
+
16201
+ ### HEARTBEAT Integration
16202
+
16203
+ When a workspace file exceeds the warning threshold, a \`## {filename}\` alert appears in HEARTBEAT.md (e.g., \`## AGENTS.md\`). The alert includes:
16204
+ - Character count, budget, and usage percentage
16205
+ - Trimming guidance in priority order: (1) move domain-specific content to a local skill, (2) extract reference material to companion files with a pointer, (3) summarize verbose instructions, (4) remove stale content
16206
+
16207
+ Each file heading follows the same declined/active lifecycle as component headings. Users can decline alerts by changing the heading to \`## {filename}: declined\` (e.g., \`## AGENTS.md: declined\`).
16165
16208
  `;
16166
16209
 
16167
16210
  /**
@@ -16186,6 +16229,148 @@ function seedSkill(workspacePath) {
16186
16229
  writeFileSync(skillPath, skillContent, 'utf-8');
16187
16230
  }
16188
16231
 
16232
+ /**
16233
+ * Zod schema for the Jeeves component descriptor.
16234
+ *
16235
+ * @remarks
16236
+ * The descriptor replaces the v0.4.0 `JeevesComponent` interface with a
16237
+ * Zod-first approach. The TypeScript type is inferred via `z.infer<>`.
16238
+ * Validates at parse time: prime interval, callable functions.
16239
+ */
16240
+ /**
16241
+ * Check whether a number is prime.
16242
+ *
16243
+ * @param n - Number to check.
16244
+ * @returns `true` if n is prime.
16245
+ */
16246
+ function isPrime(n) {
16247
+ if (n < 2)
16248
+ return false;
16249
+ if (n === 2)
16250
+ return true;
16251
+ if (n % 2 === 0)
16252
+ return false;
16253
+ for (let i = 3; i * i <= n; i += 2) {
16254
+ if (n % i === 0)
16255
+ return false;
16256
+ }
16257
+ return true;
16258
+ }
16259
+ /**
16260
+ * Zod schema for the Jeeves component descriptor.
16261
+ *
16262
+ * @remarks
16263
+ * Single source of truth for what a component must provide.
16264
+ * Factories consume this descriptor to produce CLI commands,
16265
+ * plugin tools, and HTTP handlers.
16266
+ */
16267
+ object({
16268
+ /** Component name (e.g., 'watcher', 'runner', 'server', 'meta'). */
16269
+ name: string().min(1, 'name must be a non-empty string'),
16270
+ /** Component version (from package.json). */
16271
+ version: string().min(1, 'version must be a non-empty string'),
16272
+ /** npm package name for the service. */
16273
+ servicePackage: string().min(1),
16274
+ /** npm package name for the plugin. */
16275
+ pluginPackage: string().min(1),
16276
+ /** System service name. Defaults to `jeeves-${name}` when not provided. */
16277
+ serviceName: string().min(1).optional(),
16278
+ /** Default port for the service's HTTP API. */
16279
+ defaultPort: number().int().positive(),
16280
+ /** Zod schema for validating config files. */
16281
+ configSchema: custom((val) => val !== null &&
16282
+ typeof val === 'object' &&
16283
+ typeof val.parse === 'function', { message: 'configSchema must be a Zod schema' }),
16284
+ /** Config file name (e.g., 'jeeves-watcher.config.json'). */
16285
+ configFileName: string().min(1),
16286
+ /** Returns a default config object for `init`. */
16287
+ initTemplate: _function({
16288
+ input: [],
16289
+ output: record(string(), unknown()),
16290
+ }),
16291
+ /**
16292
+ * Service-side callback after config apply. Receives the merged,
16293
+ * validated config (not the raw patch). Optional — if omitted,
16294
+ * write-only (service picks up changes on restart).
16295
+ */
16296
+ onConfigApply: _function({
16297
+ input: [record(string(), unknown())],
16298
+ output: promise(_void()),
16299
+ })
16300
+ .optional(),
16301
+ /**
16302
+ * Custom merge function for config apply. Receives the existing config
16303
+ * and the patch, returns the merged result. Optional — if omitted,
16304
+ * the default deep-merge (object-recursive, array-replacing) is used.
16305
+ *
16306
+ * Use this to implement domain-specific merge strategies such as
16307
+ * name-based array merging for inference rules.
16308
+ */
16309
+ customMerge: _function({
16310
+ input: [
16311
+ record(string(), unknown()),
16312
+ record(string(), unknown()),
16313
+ ],
16314
+ output: record(string(), unknown()),
16315
+ })
16316
+ .optional(),
16317
+ /**
16318
+ * Returns command + args for launching the service process.
16319
+ * Consumed by `service install`.
16320
+ */
16321
+ startCommand: _function({
16322
+ input: [string()],
16323
+ output: array(string()),
16324
+ }),
16325
+ /** In-process service entry point for the CLI `start` command. */
16326
+ run: _function({
16327
+ input: [string()],
16328
+ output: promise(_void()),
16329
+ }),
16330
+ /** TOOLS.md section name (e.g., 'Watcher'). */
16331
+ sectionId: string().min(1, 'sectionId must be a non-empty string'),
16332
+ /** Refresh interval in seconds (must be a prime number). */
16333
+ refreshIntervalSeconds: number().int().positive().refine(isPrime, {
16334
+ message: 'refreshIntervalSeconds must be a prime number',
16335
+ }),
16336
+ /** Produce the component's TOOLS.md section content. */
16337
+ generateToolsContent: _function({ input: [], output: string() }),
16338
+ /** Component dependencies for HEARTBEAT alert suppression. */
16339
+ dependencies: object({
16340
+ /** Components that must be healthy for this component to function. */
16341
+ hard: array(string()),
16342
+ /** Components that improve behavior but are not strictly required. */
16343
+ soft: array(string()),
16344
+ })
16345
+ .optional(),
16346
+ /** Extension point: add custom CLI commands to the service CLI. */
16347
+ customCliCommands: _function({ input: [custom()], output: _void() })
16348
+ .optional(),
16349
+ /** Extension point: return additional plugin tool descriptors. */
16350
+ customPluginTools: _function({ input: [custom()], output: array(unknown()) })
16351
+ .optional(),
16352
+ });
16353
+
16354
+ /**
16355
+ * Resolve the package root directory from a module's `import.meta.url`.
16356
+ *
16357
+ * @module
16358
+ */
16359
+ /**
16360
+ * Get the nearest package root directory relative to the calling module URL.
16361
+ *
16362
+ * @param importMetaUrl - The `import.meta.url` of the calling module.
16363
+ * @returns The absolute package root path, or `undefined` on any error.
16364
+ */
16365
+ function getPackageRoot(importMetaUrl) {
16366
+ try {
16367
+ return packageDirectorySync({ cwd: fileURLToPath(importMetaUrl) });
16368
+ }
16369
+ catch {
16370
+ return undefined;
16371
+ }
16372
+ }
16373
+
16189
16374
  /**
16190
16375
  * OpenClaw configuration helpers for plugin CLI installers.
16191
16376
  *
@@ -16262,16 +16447,18 @@ function patchAllowList(parent, key, label, pluginId, mode) {
16262
16447
  * Patch an OpenClaw config for plugin install or uninstall.
16263
16448
  *
16264
16449
  * @remarks
16265
- * Manages `plugins.entries.{pluginId}` and `tools.alsoAllow`.
16450
+ * Manages `plugins.entries.{pluginId}`, `plugins.installs.{pluginId}`,
16451
+ * and `tools.alsoAllow`.
16266
16452
  * Idempotent: adding twice produces no duplicates; removing when absent
16267
16453
  * produces no errors.
16268
16454
  *
16269
16455
  * @param config - The parsed OpenClaw config object (mutated in place).
16270
16456
  * @param pluginId - The plugin identifier.
16271
16457
  * @param mode - Whether to add or remove the plugin.
16458
+ * @param installRecord - Install provenance record (required when mode is 'add').
16272
16459
  * @returns Array of log messages describing changes made.
16273
16460
  */
16274
- function patchConfig(config, pluginId, mode) {
16461
+ function patchConfig(config, pluginId, mode, installRecord) {
16275
16462
  const messages = [];
16276
16463
  // Ensure plugins section
16277
16464
  if (!config.plugins || typeof config.plugins !== 'object') {
@@ -16293,6 +16480,24 @@ function patchConfig(config, pluginId, mode) {
16293
16480
  Reflect.deleteProperty(entries, pluginId);
16294
16481
  messages.push(`Removed "${pluginId}" from plugins.entries`);
16295
16482
  }
16483
+ // plugins.installs
16484
+ if (!plugins.installs || typeof plugins.installs !== 'object') {
16485
+ plugins.installs = {};
16486
+ }
16487
+ const installs = plugins.installs;
16488
+ if (mode === 'add' && installRecord) {
16489
+ installs[pluginId] = {
16490
+ source: 'path',
16491
+ installPath: installRecord.installPath,
16492
+ version: installRecord.version,
16493
+ installedAt: installRecord.installedAt ?? new Date().toISOString(),
16494
+ };
16495
+ messages.push(`Wrote install record for "${pluginId}" to plugins.installs`);
16496
+ }
16497
+ else if (mode === 'remove' && pluginId in installs) {
16498
+ Reflect.deleteProperty(installs, pluginId);
16499
+ messages.push(`Removed install record for "${pluginId}" from plugins.installs`);
16500
+ }
16296
16501
  // tools.alsoAllow
16297
16502
  if (!config.tools || typeof config.tools !== 'object') {
16298
16503
  config.tools = {};
@@ -16369,8 +16574,13 @@ function readJsonFile(filePath) {
16369
16574
  * @returns A Commander program ready for `.parse()`.
16370
16575
  */
16371
16576
  function createPluginCli(options) {
16372
- const { pluginId, distDir, pluginPackage, configRoot = 'j:/config', } = options;
16577
+ const { pluginId, importMetaUrl, pluginPackage, configRoot = 'j:/config', } = options;
16373
16578
  const componentName = options.componentName ?? deriveComponentName(pluginId);
16579
+ const pkgRoot = getPackageRoot(importMetaUrl);
16580
+ if (!pkgRoot) {
16581
+ throw new Error(`Unable to resolve package root for plugin CLI: ${pluginPackage}`);
16582
+ }
16583
+ const distDir = join(pkgRoot, 'dist');
16374
16584
  const program = new Command()
16375
16585
  .name(pluginPackage)
16376
16586
  .description(`Jeeves ${componentName} plugin installer`);
@@ -16385,23 +16595,38 @@ function createPluginCli(options) {
16385
16595
  const configPath = resolveConfigPath(openClawHome);
16386
16596
  // 1. Copy dist to extensions
16387
16597
  const extensionsDir = join(openClawHome, 'extensions', pluginId);
16598
+ if (!existsSync(distDir)) {
16599
+ throw new Error(`Plugin dist directory not found: ${distDir}. Ensure the plugin is built before installing.`);
16600
+ }
16388
16601
  console.log(`Copying dist to ${extensionsDir}...`);
16389
16602
  copyDistFiles(distDir, extensionsDir);
16390
16603
  // Copy package.json and openclaw.plugin.json from package root
16391
- const pkgRoot = packageDirectorySync({ cwd: distDir });
16392
- if (pkgRoot) {
16393
- for (const file of ['package.json', 'openclaw.plugin.json']) {
16394
- const src = join(pkgRoot, file);
16395
- if (existsSync(src)) {
16396
- copyFileSync(src, join(extensionsDir, file));
16397
- }
16604
+ for (const file of ['package.json', 'openclaw.plugin.json']) {
16605
+ const src = join(pkgRoot, file);
16606
+ if (existsSync(src)) {
16607
+ copyFileSync(src, join(extensionsDir, file));
16398
16608
  }
16399
16609
  }
16400
16610
  console.log(' ✓ Dist files copied');
16401
16611
  // 2. Patch openclaw.json
16402
16612
  console.log('Patching OpenClaw config...');
16403
16613
  const config = readJsonFile(configPath);
16404
- const messages = patchConfig(config, pluginId, 'add');
16614
+ const pkgJsonPathForVersion = join(extensionsDir, 'package.json');
16615
+ let pluginVersionForRecord;
16616
+ try {
16617
+ const pkgJsonForRecord = readJsonFile(pkgJsonPathForVersion);
16618
+ pluginVersionForRecord =
16619
+ typeof pkgJsonForRecord.version === 'string'
16620
+ ? pkgJsonForRecord.version
16621
+ : undefined;
16622
+ }
16623
+ catch {
16624
+ // best-effort: version may not be available yet
16625
+ }
16626
+ const messages = patchConfig(config, pluginId, 'add', {
16627
+ installPath: extensionsDir,
16628
+ version: pluginVersionForRecord,
16629
+ });
16405
16630
  // 3. Memory slot claim
16406
16631
  if (opts.memory) {
16407
16632
  if (!config.agents || typeof config.agents !== 'object') {
@@ -16549,128 +16774,6 @@ function createPluginCli(options) {
16549
16774
  return program;
16550
16775
  }
16551
16776
 
16552
- /**
16553
- * Zod schema for the Jeeves component descriptor.
16554
- *
16555
- * @remarks
16556
- * The descriptor replaces the v0.4.0 `JeevesComponent` interface with a
16557
- * Zod-first approach. The TypeScript type is inferred via `z.infer<>`.
16558
- * Validates at parse time: prime interval, callable functions.
16559
- */
16560
- /**
16561
- * Check whether a number is prime.
16562
- *
16563
- * @param n - Number to check.
16564
- * @returns `true` if n is prime.
16565
- */
16566
- function isPrime(n) {
16567
- if (n < 2)
16568
- return false;
16569
- if (n === 2)
16570
- return true;
16571
- if (n % 2 === 0)
16572
- return false;
16573
- for (let i = 3; i * i <= n; i += 2) {
16574
- if (n % i === 0)
16575
- return false;
16576
- }
16577
- return true;
16578
- }
16579
- /**
16580
- * Zod schema for the Jeeves component descriptor.
16581
- *
16582
- * @remarks
16583
- * Single source of truth for what a component must provide.
16584
- * Factories consume this descriptor to produce CLI commands,
16585
- * plugin tools, and HTTP handlers.
16586
- */
16587
- object({
16588
- /** Component name (e.g., 'watcher', 'runner', 'server', 'meta'). */
16589
- name: string().min(1, 'name must be a non-empty string'),
16590
- /** Component version (from package.json). */
16591
- version: string().min(1, 'version must be a non-empty string'),
16592
- /** npm package name for the service. */
16593
- servicePackage: string().min(1),
16594
- /** npm package name for the plugin. */
16595
- pluginPackage: string().min(1),
16596
- /** System service name. Defaults to `jeeves-${name}` when not provided. */
16597
- serviceName: string().min(1).optional(),
16598
- /** Default port for the service's HTTP API. */
16599
- defaultPort: number().int().positive(),
16600
- /** Zod schema for validating config files. */
16601
- configSchema: custom((val) => val !== null &&
16602
- typeof val === 'object' &&
16603
- typeof val.parse === 'function', { message: 'configSchema must be a Zod schema' }),
16604
- /** Config file name (e.g., 'jeeves-watcher.config.json'). */
16605
- configFileName: string().min(1),
16606
- /** Returns a default config object for `init`. */
16607
- initTemplate: _function({
16608
- input: [],
16609
- output: record(string(), unknown()),
16610
- }),
16611
- /**
16612
- * Service-side callback after config apply. Receives the merged,
16613
- * validated config (not the raw patch). Optional — if omitted,
16614
- * write-only (service picks up changes on restart).
16615
- */
16616
- onConfigApply: _function({
16617
- input: [record(string(), unknown())],
16618
- output: promise(_void()),
16619
- })
16620
- .optional(),
16621
- /**
16622
- * Custom merge function for config apply. Receives the existing config
16623
- * and the patch, returns the merged result. Optional — if omitted,
16624
- * the default deep-merge (object-recursive, array-replacing) is used.
16625
- *
16626
- * Use this to implement domain-specific merge strategies such as
16627
- * name-based array merging for inference rules.
16628
- */
16629
- customMerge: _function({
16630
- input: [
16631
- record(string(), unknown()),
16632
- record(string(), unknown()),
16633
- ],
16634
- output: record(string(), unknown()),
16635
- })
16636
- .optional(),
16637
- /**
16638
- * Returns command + args for launching the service process.
16639
- * Consumed by `service install`.
16640
- */
16641
- startCommand: _function({
16642
- input: [string()],
16643
- output: array(string()),
16644
- }),
16645
- /** In-process service entry point for the CLI `start` command. */
16646
- run: _function({
16647
- input: [string()],
16648
- output: promise(_void()),
16649
- }),
16650
- /** TOOLS.md section name (e.g., 'Watcher'). */
16651
- sectionId: string().min(1, 'sectionId must be a non-empty string'),
16652
- /** Refresh interval in seconds (must be a prime number). */
16653
- refreshIntervalSeconds: number().int().positive().refine(isPrime, {
16654
- message: 'refreshIntervalSeconds must be a prime number',
16655
- }),
16656
- /** Produce the component's TOOLS.md section content. */
16657
- generateToolsContent: _function({ input: [], output: string() }),
16658
- /** Component dependencies for HEARTBEAT alert suppression. */
16659
- dependencies: object({
16660
- /** Components that must be healthy for this component to function. */
16661
- hard: array(string()),
16662
- /** Components that improve behavior but are not strictly required. */
16663
- soft: array(string()),
16664
- })
16665
- .optional(),
16666
- /** Extension point: add custom CLI commands to the service CLI. */
16667
- customCliCommands: _function({ input: [custom()], output: _void() })
16668
- .optional(),
16669
- /** Extension point: return additional plugin tool descriptors. */
16670
- customPluginTools: _function({ input: [custom()], output: array(unknown()) })
16671
- .optional(),
16672
- });
16673
-
16674
16777
  /**
16675
16778
  * Core configuration schema and resolution.
16676
16779
  *
@@ -16733,12 +16836,11 @@ const PLUGIN_ID = 'jeeves-server-openclaw';
16733
16836
  *
16734
16837
  * @packageDocumentation
16735
16838
  */
16736
- const distDir = resolve(dirname(fileURLToPath(import.meta.url)));
16737
16839
  // Type assertion: core's bundled .d.ts doesn't fully resolve the Command
16738
16840
  // return type for eslint, but the runtime value is a Commander instance.
16739
16841
  const program = createPluginCli({
16740
16842
  pluginId: PLUGIN_ID,
16741
- distDir,
16843
+ importMetaUrl: import.meta.url,
16742
16844
  pluginPackage: '@karmaniverous/jeeves-server-openclaw',
16743
16845
  componentName: 'server',
16744
16846
  });