chore: convert plugin to ESM and migrate to current openclaw plugin SDK

ESM conversion:
- plugin/package.json: add "type": "module".
- plugin/tsconfig.json: switch module/moduleResolution to "nodenext"; bump
  target to ES2022.
- All relative imports across plugin/ now carry .js extensions as required
  by Node ESM (nodenext module resolution).
- plugin/index.ts: replace `require('./calendar/schedule-cache')` with a
  proper top-level import; switch `from 'os'` to `from 'node:os'`.

Plugin SDK convention update:
- Wrap default export with definePluginEntry({ id, name, description,
  register }) per the current openclaw plugin authoring contract.
- Modernize plugin/openclaw.plugin.json: drop entry/version, add
  activation.onStartup so gateway_start fires for this plugin at boot,
  declare contracts.tools listing the five harborforge_* tools.
- Add a local plugin/openclaw-sdk.d.ts with ambient declarations for the
  focused subpaths (openclaw/plugin-sdk/plugin-entry,
  openclaw/plugin-sdk/core). We deliberately do NOT add openclaw as an
  npm devDependency: the installer's `npm install --omit=dev` step trips
  over openclaw's own (deeply nested) dependency graph when listed via
  file:.../openclaw, and the runtime contract is provided by the gateway
  loader anyway.
- The local PluginAPI interface is preserved (broader than the standard
  OpenClawPluginApi — it surfaces `version`/`runtime`/`spawn`); the
  register function is cast at the definePluginEntry boundary.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
operator
2026-05-08 08:28:58 +00:00
parent 957bcbb4a8
commit 64a9c431bf
9 changed files with 50 additions and 30 deletions

View File

@@ -29,7 +29,7 @@ import {
CalendarSlotResponse,
SlotAgentUpdate,
SlotStatus,
} from './types';
} from './types.js';
export interface CalendarBridgeConfig {
/** HarborForge backend base URL (e.g. "https://monitor.hangman-lab.top") */
@@ -292,7 +292,7 @@ export class CalendarBridgeClient {
// ---------------------------------------------------------------------------
import { hostname } from 'os';
import { getPluginConfig } from '../core/config';
import { getPluginConfig } from '../core/config.js';
export interface CalendarPluginConfig {
/** Backend URL for calendar API (overrides monitor backendUrl) */

View File

@@ -13,7 +13,7 @@
* • AgentWakeContext — context passed to agent when waking
*
* Usage in plugin/index.ts:
* import { createCalendarBridgeClient, createCalendarScheduler } from './calendar';
* import { createCalendarBridgeClient, createCalendarScheduler } from './calendar.js';
*
* const agentId = process.env.AGENT_ID || 'unknown';
* const calendar = createCalendarBridgeClient(api, 'https://monitor.hangman-lab.top', agentId);
@@ -28,7 +28,7 @@
* scheduler.start();
*/
export * from './types';
export * from './calendar-bridge';
export * from './scheduler';
export * from './schedule-cache';
export * from './types.js';
export * from './calendar-bridge.js';
export * from './scheduler.js';
export * from './schedule-cache.js';

View File

@@ -18,7 +18,7 @@
import { writeFileSync, readFileSync, existsSync, mkdirSync } from 'fs';
import { join, dirname } from 'path';
import { CalendarBridgeClient } from './calendar-bridge';
import { CalendarBridgeClient } from './calendar-bridge.js';
import {
CalendarSlotResponse,
SlotStatus,
@@ -26,7 +26,7 @@ import {
SlotAgentUpdate,
CalendarEventDataJob,
CalendarEventDataSystemEvent,
} from './types';
} from './types.js';
export interface CalendarSchedulerConfig {
/** Calendar bridge client for backend communication */

View File

@@ -1,6 +1,6 @@
import { hostname } from 'os';
import { getPluginConfig } from '../core/config';
import { startManagedMonitor } from '../core/managed-monitor';
import { getPluginConfig } from '../core/config.js';
import { startManagedMonitor } from '../core/managed-monitor.js';
export function registerGatewayStartHook(api: any, deps: {
logger: any;

View File

@@ -1,4 +1,4 @@
import { stopManagedMonitor } from '../core/managed-monitor';
import { stopManagedMonitor } from '../core/managed-monitor.js';
export function registerGatewayStopHook(api: any, deps: {
logger: any;

View File

@@ -11,18 +11,20 @@
* served directly by the plugin when Monitor queries via the
* local monitor_port communication path.
*/
import { hostname, freemem, totalmem, uptime, loadavg, platform } from 'os';
import { getPluginConfig } from './core/config';
import { MonitorBridgeClient, type OpenClawMeta } from './core/monitor-bridge';
import type { OpenClawAgentInfo } from './core/openclaw-agents';
import { registerGatewayStartHook } from './hooks/gateway-start';
import { registerGatewayStopHook } from './hooks/gateway-stop';
import { hostname, freemem, totalmem, uptime, loadavg, platform } from 'node:os';
import { definePluginEntry } from 'openclaw/plugin-sdk/plugin-entry';
import { MultiAgentScheduleCache } from './calendar/schedule-cache.js';
import { getPluginConfig } from './core/config.js';
import { MonitorBridgeClient, type OpenClawMeta } from './core/monitor-bridge.js';
import type { OpenClawAgentInfo } from './core/openclaw-agents.js';
import { registerGatewayStartHook } from './hooks/gateway-start.js';
import { registerGatewayStopHook } from './hooks/gateway-stop.js';
import {
createCalendarBridgeClient,
createCalendarScheduler,
CalendarScheduler,
AgentWakeContext,
} from './calendar';
} from './calendar/index.js';
interface PluginAPI {
logger: {
@@ -53,10 +55,7 @@ interface PluginAPI {
getAgentStatus?: () => Promise<{ status: string } | null>;
}
export default {
id: 'harbor-forge',
name: 'HarborForge',
register(api: PluginAPI) {
function register(api: PluginAPI): void {
const logger = api.logger || {
info: (...args: any[]) => console.log('[HarborForge]', ...args),
error: (...args: any[]) => console.error('[HarborForge]', ...args),
@@ -290,7 +289,6 @@ export default {
);
// Multi-agent sync + check loop
const { MultiAgentScheduleCache } = require('./calendar/schedule-cache') as typeof import('./calendar/schedule-cache');
const scheduleCache = new MultiAgentScheduleCache();
const SYNC_INTERVAL_MS = 300_000; // 5 min
@@ -595,5 +593,16 @@ export default {
}));
logger.info('HarborForge plugin registered (id: harbor-forge)');
},
};
}
// HarborForge's local PluginAPI is broader than the standard OpenClawPluginApi
// (it surfaces optional `version`/`runtime`/`spawn` accessors that older
// OpenClaw builds exposed). The cast at the definePluginEntry boundary
// acknowledges that gap — the runtime api object is whatever the gateway
// passes us, and each access is guarded with optional chaining / fallbacks.
export default definePluginEntry({
id: 'harbor-forge',
name: 'HarborForge',
description: 'HarborForge plugin for OpenClaw - project management, monitoring, and CLI integration',
register: register as (api: any) => void,
});

View File

@@ -1,9 +1,19 @@
{
"id": "harbor-forge",
"name": "HarborForge",
"version": "0.2.0",
"description": "HarborForge plugin for OpenClaw - project management, monitoring, and CLI integration",
"entry": "./dist/index.js",
"activation": {
"onStartup": true
},
"contracts": {
"tools": [
"harborforge_status",
"harborforge_telemetry",
"harborforge_monitor_telemetry",
"harborforge_calendar_status",
"harborforge_calendar_complete"
]
},
"configSchema": {
"type": "object",
"additionalProperties": false,

View File

@@ -2,6 +2,7 @@
"name": "harbor-forge-plugin",
"version": "0.2.0",
"description": "OpenClaw plugin for HarborForge monitor bridge and CLI integration",
"type": "module",
"main": "dist/index.js",
"scripts": {
"clean": "rm -rf dist",

View File

@@ -1,8 +1,8 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "CommonJS",
"moduleResolution": "node",
"module": "nodenext",
"moduleResolution": "nodenext",
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,