feat: implement OpenClaw integration

Ports the OMX OpenClaw module into oh-my-openagent as a first-class integration.
This integration allows forwarding internal events (session lifecycle, tool execution) to external gateways (HTTP or command-based).

- Added `src/openclaw` directory with implementation:
  - `dispatcher.ts`: Handles HTTP/Command dispatching with interpolation
  - `types.ts`: TypeScript definitions
  - `client.ts`: Main entry point `wakeOpenClaw`
  - `index.ts`: Public API
- Added `src/config/schema/openclaw.ts` for Zod schema validation
- Updated `src/config/schema/oh-my-opencode-config.ts` to include `openclaw` config
- Added `src/hooks/openclaw-sender/index.ts` to listen for events
- Registered the hook in `src/plugin/hooks/create-session-hooks.ts`
- Added unit tests in `src/openclaw/__tests__`

Events handled:
- `session-start` (via `session.created`)
- `session-end` (via `session.deleted`)
- `session-idle` (via `session.idle`)
- `ask-user-question` (via `tool.execute.before` for `ask_user_question`)
- `stop` (via `tool.execute.before` for `stop-continuation` command)
This commit is contained in:
YeonGyu-Kim
2026-03-16 17:21:56 +09:00
parent 84fb1113f1
commit 03b346ba51
14 changed files with 1006 additions and 0 deletions
+1
View File
@@ -51,6 +51,7 @@ export const HookNameSchema = z.enum([
"anthropic-effort",
"hashline-read-enhancer",
"read-image-resizer",
"openclaw-sender",
])
export type HookName = z.infer<typeof HookNameSchema>
@@ -12,6 +12,7 @@ import { BuiltinCommandNameSchema } from "./commands"
import { ExperimentalConfigSchema } from "./experimental"
import { GitMasterConfigSchema } from "./git-master"
import { NotificationConfigSchema } from "./notification"
import { OpenClawConfigSchema } from "./openclaw"
import { RalphLoopConfigSchema } from "./ralph-loop"
import { RuntimeFallbackConfigSchema } from "./runtime-fallback"
import { SkillsConfigSchema } from "./skills"
@@ -55,6 +56,7 @@ export const OhMyOpenCodeConfigSchema = z.object({
runtime_fallback: z.union([z.boolean(), RuntimeFallbackConfigSchema]).optional(),
background_task: BackgroundTaskConfigSchema.optional(),
notification: NotificationConfigSchema.optional(),
openclaw: OpenClawConfigSchema.optional(),
babysitting: BabysittingConfigSchema.optional(),
git_master: GitMasterConfigSchema.optional(),
browser_automation_engine: BrowserAutomationConfigSchema.optional(),
+51
View File
@@ -0,0 +1,51 @@
import { z } from "zod";
export const OpenClawHookEventSchema = z.enum([
"session-start",
"session-end",
"session-idle",
"ask-user-question",
"stop",
]);
export const OpenClawHttpGatewayConfigSchema = z.object({
type: z.literal("http").optional(),
url: z.string(), // Allow looser URL validation as it might contain placeholders
headers: z.record(z.string(), z.string()).optional(),
method: z.enum(["POST", "PUT"]).optional(),
timeout: z.number().optional(),
});
export const OpenClawCommandGatewayConfigSchema = z.object({
type: z.literal("command"),
command: z.string(),
timeout: z.number().optional(),
});
export const OpenClawGatewayConfigSchema = z.union([
OpenClawHttpGatewayConfigSchema,
OpenClawCommandGatewayConfigSchema,
]);
export const OpenClawHookMappingSchema = z.object({
gateway: z.string(),
instruction: z.string(),
enabled: z.boolean(),
});
export const OpenClawConfigSchema = z.object({
enabled: z.boolean(),
gateways: z.record(z.string(), OpenClawGatewayConfigSchema),
hooks: z
.object({
"session-start": OpenClawHookMappingSchema.optional(),
"session-end": OpenClawHookMappingSchema.optional(),
"session-idle": OpenClawHookMappingSchema.optional(),
"ask-user-question": OpenClawHookMappingSchema.optional(),
stop: OpenClawHookMappingSchema.optional(),
})
.strict()
.optional(),
});
export type OpenClawConfig = z.infer<typeof OpenClawConfigSchema>;