@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 +257 -155
- package/dist/index.js +1204 -1090
- package/openclaw.plugin.json +1 -1
- package/package.json +6 -6
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:
|
|
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.
|
|
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.
|
|
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
|
|
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
|
-
|
|
15474
|
-
renameSync(tempPath, filePath);
|
|
15475
|
-
}
|
|
15476
|
-
catch (err) {
|
|
15482
|
+
for (let attempt = 0; attempt < ATOMIC_WRITE_MAX_RETRIES; attempt++) {
|
|
15477
15483
|
try {
|
|
15478
|
-
|
|
15484
|
+
renameSync(tempPath, filePath);
|
|
15485
|
+
return;
|
|
15479
15486
|
}
|
|
15480
|
-
catch {
|
|
15481
|
-
|
|
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
|
|
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*${
|
|
15840
|
-
endRe: new RegExp(`^<!--\\s*${
|
|
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}
|
|
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,
|
|
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
|
|
16392
|
-
|
|
16393
|
-
|
|
16394
|
-
|
|
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
|
|
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
|
-
|
|
16843
|
+
importMetaUrl: import.meta.url,
|
|
16742
16844
|
pluginPackage: '@karmaniverous/jeeves-server-openclaw',
|
|
16743
16845
|
componentName: 'server',
|
|
16744
16846
|
});
|