@omnixal/openclaw-nats-plugin 0.2.0 → 0.2.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/cli/setup.ts +27 -0
- package/dashboard/bun.lock +253 -0
- package/dashboard/components.json +16 -0
- package/dashboard/index.html +22 -0
- package/dashboard/package.json +24 -0
- package/dashboard/src/App.svelte +107 -0
- package/dashboard/src/app.css +232 -0
- package/dashboard/src/lib/ConfigPanel.svelte +35 -0
- package/dashboard/src/lib/CronPanel.svelte +255 -0
- package/dashboard/src/lib/HealthCards.svelte +68 -0
- package/dashboard/src/lib/MetricsPanel.svelte +60 -0
- package/dashboard/src/lib/PendingTable.svelte +73 -0
- package/dashboard/src/lib/RoutesPanel.svelte +178 -0
- package/dashboard/src/lib/ThemeToggle.svelte +54 -0
- package/dashboard/src/lib/api.ts +141 -0
- package/dashboard/src/lib/components/ui/badge/badge.svelte +50 -0
- package/dashboard/src/lib/components/ui/badge/index.ts +2 -0
- package/dashboard/src/lib/components/ui/button/button.svelte +82 -0
- package/dashboard/src/lib/components/ui/button/index.ts +17 -0
- package/dashboard/src/lib/components/ui/card/card-action.svelte +20 -0
- package/dashboard/src/lib/components/ui/card/card-content.svelte +15 -0
- package/dashboard/src/lib/components/ui/card/card-description.svelte +20 -0
- package/dashboard/src/lib/components/ui/card/card-footer.svelte +20 -0
- package/dashboard/src/lib/components/ui/card/card-header.svelte +23 -0
- package/dashboard/src/lib/components/ui/card/card-title.svelte +20 -0
- package/dashboard/src/lib/components/ui/card/card.svelte +23 -0
- package/dashboard/src/lib/components/ui/card/index.ts +25 -0
- package/dashboard/src/lib/components/ui/table/index.ts +28 -0
- package/dashboard/src/lib/components/ui/table/table-body.svelte +20 -0
- package/dashboard/src/lib/components/ui/table/table-caption.svelte +20 -0
- package/dashboard/src/lib/components/ui/table/table-cell.svelte +23 -0
- package/dashboard/src/lib/components/ui/table/table-footer.svelte +20 -0
- package/dashboard/src/lib/components/ui/table/table-head.svelte +23 -0
- package/dashboard/src/lib/components/ui/table/table-header.svelte +20 -0
- package/dashboard/src/lib/components/ui/table/table-row.svelte +23 -0
- package/dashboard/src/lib/components/ui/table/table.svelte +22 -0
- package/dashboard/src/lib/utils.ts +29 -0
- package/dashboard/src/main.ts +7 -0
- package/dashboard/tsconfig.json +19 -0
- package/dashboard/vite.config.ts +30 -0
- package/package.json +6 -4
- package/plugins/nats-context-engine/http-handler.ts +1 -1
- package/plugins/nats-context-engine/index.ts +59 -0
- package/sidecar/bun.lock +8 -6
- package/sidecar/package.json +4 -4
- package/sidecar/src/app.module.ts +9 -2
- package/sidecar/src/consumer/consumer.controller.ts +4 -0
- package/sidecar/src/consumer/consumer.module.ts +2 -1
- package/sidecar/src/db/migrations/0004_familiar_zaladane.sql +17 -0
- package/sidecar/src/db/migrations/meta/0004_snapshot.json +306 -0
- package/sidecar/src/db/migrations/meta/_journal.json +7 -0
- package/sidecar/src/db/schema.ts +20 -0
- package/sidecar/src/health/health.service.ts +1 -1
- package/sidecar/src/index.ts +6 -0
- package/sidecar/src/metrics/metrics.controller.ts +16 -0
- package/sidecar/src/metrics/metrics.module.ts +10 -0
- package/sidecar/src/metrics/metrics.service.ts +64 -0
- package/sidecar/src/publisher/publisher.module.ts +2 -0
- package/sidecar/src/publisher/publisher.service.ts +6 -1
- package/sidecar/src/router/router.controller.ts +20 -12
- package/sidecar/src/router/router.repository.ts +39 -7
- package/sidecar/src/router/router.service.ts +10 -2
- package/sidecar/src/scheduler/scheduler.controller.ts +68 -0
- package/sidecar/src/scheduler/scheduler.module.ts +13 -0
- package/sidecar/src/scheduler/scheduler.repository.ts +64 -0
- package/sidecar/src/scheduler/scheduler.service.ts +138 -0
- package/sidecar/src/validation/schemas.ts +18 -0
- package/skills/nats-events/SKILL.md +41 -28
- package/dashboard/dist/assets/index--UFIkwvP.js +0 -2
- package/dashboard/dist/assets/index-CafgidIc.css +0 -2
- package/dashboard/dist/index.html +0 -13
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
3
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="card-header"
|
|
16
|
+
class={cn(
|
|
17
|
+
"@container/card-header grid auto-rows-min grid-rows-[auto_auto] items-start gap-1.5 px-6 has-data-[slot=card-action]:grid-cols-[1fr_auto] [.border-b]:pb-6",
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
{...restProps}
|
|
21
|
+
>
|
|
22
|
+
{@render children?.()}
|
|
23
|
+
</div>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="card-title"
|
|
16
|
+
class={cn("leading-none font-semibold", className)}
|
|
17
|
+
{...restProps}
|
|
18
|
+
>
|
|
19
|
+
{@render children?.()}
|
|
20
|
+
</div>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
3
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="card"
|
|
16
|
+
class={cn(
|
|
17
|
+
"bg-card text-card-foreground flex flex-col gap-6 rounded-xl border py-6 shadow-sm",
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
{...restProps}
|
|
21
|
+
>
|
|
22
|
+
{@render children?.()}
|
|
23
|
+
</div>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import Root from "./card.svelte";
|
|
2
|
+
import Content from "./card-content.svelte";
|
|
3
|
+
import Description from "./card-description.svelte";
|
|
4
|
+
import Footer from "./card-footer.svelte";
|
|
5
|
+
import Header from "./card-header.svelte";
|
|
6
|
+
import Title from "./card-title.svelte";
|
|
7
|
+
import Action from "./card-action.svelte";
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
Root,
|
|
11
|
+
Content,
|
|
12
|
+
Description,
|
|
13
|
+
Footer,
|
|
14
|
+
Header,
|
|
15
|
+
Title,
|
|
16
|
+
Action,
|
|
17
|
+
//
|
|
18
|
+
Root as Card,
|
|
19
|
+
Content as CardContent,
|
|
20
|
+
Description as CardDescription,
|
|
21
|
+
Footer as CardFooter,
|
|
22
|
+
Header as CardHeader,
|
|
23
|
+
Title as CardTitle,
|
|
24
|
+
Action as CardAction,
|
|
25
|
+
};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import Root from "./table.svelte";
|
|
2
|
+
import Body from "./table-body.svelte";
|
|
3
|
+
import Caption from "./table-caption.svelte";
|
|
4
|
+
import Cell from "./table-cell.svelte";
|
|
5
|
+
import Footer from "./table-footer.svelte";
|
|
6
|
+
import Head from "./table-head.svelte";
|
|
7
|
+
import Header from "./table-header.svelte";
|
|
8
|
+
import Row from "./table-row.svelte";
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
Root,
|
|
12
|
+
Body,
|
|
13
|
+
Caption,
|
|
14
|
+
Cell,
|
|
15
|
+
Footer,
|
|
16
|
+
Head,
|
|
17
|
+
Header,
|
|
18
|
+
Row,
|
|
19
|
+
//
|
|
20
|
+
Root as Table,
|
|
21
|
+
Body as TableBody,
|
|
22
|
+
Caption as TableCaption,
|
|
23
|
+
Cell as TableCell,
|
|
24
|
+
Footer as TableFooter,
|
|
25
|
+
Head as TableHead,
|
|
26
|
+
Header as TableHeader,
|
|
27
|
+
Row as TableRow,
|
|
28
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
3
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLTableSectionElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<tbody
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="table-body"
|
|
16
|
+
class={cn("[&_tr:last-child]:border-0", className)}
|
|
17
|
+
{...restProps}
|
|
18
|
+
>
|
|
19
|
+
{@render children?.()}
|
|
20
|
+
</tbody>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
3
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<caption
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="table-caption"
|
|
16
|
+
class={cn("text-muted-foreground mt-4 text-sm", className)}
|
|
17
|
+
{...restProps}
|
|
18
|
+
>
|
|
19
|
+
{@render children?.()}
|
|
20
|
+
</caption>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
3
|
+
import type { HTMLTdAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLTdAttributes> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<td
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="table-cell"
|
|
16
|
+
class={cn(
|
|
17
|
+
"bg-clip-padding p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pe-0",
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
{...restProps}
|
|
21
|
+
>
|
|
22
|
+
{@render children?.()}
|
|
23
|
+
</td>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
3
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLTableSectionElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<tfoot
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="table-footer"
|
|
16
|
+
class={cn("bg-muted/50 border-t font-medium [&>tr]:last:border-b-0", className)}
|
|
17
|
+
{...restProps}
|
|
18
|
+
>
|
|
19
|
+
{@render children?.()}
|
|
20
|
+
</tfoot>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
3
|
+
import type { HTMLThAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLThAttributes> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<th
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="table-head"
|
|
16
|
+
class={cn(
|
|
17
|
+
"text-foreground h-10 bg-clip-padding px-2 text-start align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pe-0",
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
{...restProps}
|
|
21
|
+
>
|
|
22
|
+
{@render children?.()}
|
|
23
|
+
</th>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
3
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLTableSectionElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<thead
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="table-header"
|
|
16
|
+
class={cn("[&_tr]:border-b", className)}
|
|
17
|
+
{...restProps}
|
|
18
|
+
>
|
|
19
|
+
{@render children?.()}
|
|
20
|
+
</thead>
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
3
|
+
import type { HTMLAttributes } from "svelte/elements";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLAttributes<HTMLTableRowElement>> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<tr
|
|
14
|
+
bind:this={ref}
|
|
15
|
+
data-slot="table-row"
|
|
16
|
+
class={cn(
|
|
17
|
+
"hover:[&,&>svelte-css-wrapper]:[&>th,td]:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors",
|
|
18
|
+
className
|
|
19
|
+
)}
|
|
20
|
+
{...restProps}
|
|
21
|
+
>
|
|
22
|
+
{@render children?.()}
|
|
23
|
+
</tr>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { HTMLTableAttributes } from "svelte/elements";
|
|
3
|
+
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
4
|
+
|
|
5
|
+
let {
|
|
6
|
+
ref = $bindable(null),
|
|
7
|
+
class: className,
|
|
8
|
+
children,
|
|
9
|
+
...restProps
|
|
10
|
+
}: WithElementRef<HTMLTableAttributes> = $props();
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<div data-slot="table-container" class="relative w-full overflow-x-auto">
|
|
14
|
+
<table
|
|
15
|
+
bind:this={ref}
|
|
16
|
+
data-slot="table"
|
|
17
|
+
class={cn("w-full caption-bottom text-sm", className)}
|
|
18
|
+
{...restProps}
|
|
19
|
+
>
|
|
20
|
+
{@render children?.()}
|
|
21
|
+
</table>
|
|
22
|
+
</div>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { clsx, type ClassValue } from "clsx";
|
|
2
|
+
import { twMerge } from "tailwind-merge";
|
|
3
|
+
|
|
4
|
+
export function cn(...inputs: ClassValue[]) {
|
|
5
|
+
return twMerge(clsx(inputs));
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
9
|
+
export type WithoutChild<T> = T extends { child?: any } ? Omit<T, "child"> : T;
|
|
10
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
11
|
+
export type WithoutChildren<T> = T extends { children?: any } ? Omit<T, "children"> : T;
|
|
12
|
+
export type WithoutChildrenOrChild<T> = WithoutChildren<WithoutChild<T>>;
|
|
13
|
+
export type WithElementRef<T, U extends HTMLElement = HTMLElement> = T & { ref?: U | null };
|
|
14
|
+
|
|
15
|
+
export function relativeAge(ts: number | null): string {
|
|
16
|
+
if (!ts) return '\u2014';
|
|
17
|
+
const seconds = Math.floor((Date.now() - ts) / 1000);
|
|
18
|
+
if (seconds < 60) return `${seconds}s ago`;
|
|
19
|
+
if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;
|
|
20
|
+
return `${Math.floor(seconds / 3600)}h ago`;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function formatDuration(ms: number | null): string {
|
|
24
|
+
if (ms === null) return '\u2014';
|
|
25
|
+
const seconds = Math.floor(ms / 1000);
|
|
26
|
+
if (seconds < 60) return `${seconds}s`;
|
|
27
|
+
if (seconds < 3600) return `${Math.floor(seconds / 60)}m`;
|
|
28
|
+
return `${Math.floor(seconds / 3600)}h`;
|
|
29
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ESNext",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
"forceConsistentCasingInFileNames": true,
|
|
10
|
+
"resolveJsonModule": true,
|
|
11
|
+
"isolatedModules": true,
|
|
12
|
+
"baseUrl": ".",
|
|
13
|
+
"paths": {
|
|
14
|
+
"$lib": ["./src/lib"],
|
|
15
|
+
"$lib/*": ["./src/lib/*"]
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"include": ["src/**/*"]
|
|
19
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { defineConfig } from 'vite';
|
|
2
|
+
import { svelte } from '@sveltejs/vite-plugin-svelte';
|
|
3
|
+
import tailwindcss from '@tailwindcss/vite';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
|
|
6
|
+
const BACKEND_PORT = process.env.BACKEND_PORT || 3104;
|
|
7
|
+
|
|
8
|
+
export default defineConfig({
|
|
9
|
+
plugins: [svelte(), tailwindcss()],
|
|
10
|
+
base: '/nats-dashboard/',
|
|
11
|
+
resolve: {
|
|
12
|
+
alias: {
|
|
13
|
+
$lib: path.resolve('./src/lib'),
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
server: {
|
|
17
|
+
proxy: {
|
|
18
|
+
'/nats-dashboard/api': {
|
|
19
|
+
target: `http://localhost:${BACKEND_PORT}`,
|
|
20
|
+
rewrite: (path) => path.replace('/nats-dashboard/api', '/api'),
|
|
21
|
+
changeOrigin: true,
|
|
22
|
+
headers: { Authorization: 'Bearer dev-nats-plugin-key' },
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
build: {
|
|
27
|
+
outDir: 'dist',
|
|
28
|
+
emptyDirFirst: true,
|
|
29
|
+
},
|
|
30
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@omnixal/openclaw-nats-plugin",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
4
4
|
"description": "NATS JetStream event-driven plugin for OpenClaw",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"nats-plugin": "./bin/cli.ts"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
|
-
"build:dashboard": "cd dashboard && bun run build",
|
|
13
|
-
"
|
|
12
|
+
"build:dashboard": "cd dashboard && bun install --frozen-lockfile && bun run build",
|
|
13
|
+
"nats": "docker run -d --name nats -p 4222:4222 nats:2.10-alpine -js"
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
16
16
|
"index.ts",
|
|
@@ -21,7 +21,9 @@
|
|
|
21
21
|
"sidecar/",
|
|
22
22
|
"skills/",
|
|
23
23
|
"docker/",
|
|
24
|
-
"dashboard/
|
|
24
|
+
"dashboard/",
|
|
25
|
+
"!dashboard/node_modules",
|
|
26
|
+
"!dashboard/dist",
|
|
25
27
|
"openclaw.plugin.json",
|
|
26
28
|
"PLUGIN.md",
|
|
27
29
|
"!**/*.test.ts",
|
|
@@ -172,6 +172,65 @@ export default function (api: any) {
|
|
|
172
172
|
},
|
|
173
173
|
});
|
|
174
174
|
|
|
175
|
+
// ── Cron Scheduler Tools ────────────────────────────────────────────
|
|
176
|
+
|
|
177
|
+
api.registerTool({
|
|
178
|
+
name: 'nats_cron_add',
|
|
179
|
+
description: 'Create or update a scheduled cron job that publishes a NATS event on a schedule. No LLM wake — fires directly.',
|
|
180
|
+
parameters: {
|
|
181
|
+
type: 'object',
|
|
182
|
+
properties: {
|
|
183
|
+
name: { type: 'string', description: 'Unique job name (e.g., daily-report, hourly-check)' },
|
|
184
|
+
cron: { type: 'string', description: 'Cron expression (e.g., "0 9 * * *" for daily at 9am)' },
|
|
185
|
+
subject: { type: 'string', description: 'NATS subject to publish (must start with agent.events.)' },
|
|
186
|
+
payload: { type: 'object', description: 'Event payload data' },
|
|
187
|
+
timezone: { type: 'string', description: 'Timezone (default: UTC). e.g., Europe/Moscow' },
|
|
188
|
+
},
|
|
189
|
+
required: ['name', 'cron', 'subject'],
|
|
190
|
+
},
|
|
191
|
+
async execute(_id: string, params: any) {
|
|
192
|
+
const result = await sidecarFetch('/api/cron', {
|
|
193
|
+
method: 'POST',
|
|
194
|
+
body: JSON.stringify({
|
|
195
|
+
name: params.name,
|
|
196
|
+
cron: params.cron,
|
|
197
|
+
subject: params.subject,
|
|
198
|
+
payload: params.payload ?? {},
|
|
199
|
+
timezone: params.timezone,
|
|
200
|
+
}),
|
|
201
|
+
});
|
|
202
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
203
|
+
},
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
api.registerTool({
|
|
207
|
+
name: 'nats_cron_remove',
|
|
208
|
+
description: 'Remove a scheduled cron job by name.',
|
|
209
|
+
parameters: {
|
|
210
|
+
type: 'object',
|
|
211
|
+
properties: {
|
|
212
|
+
name: { type: 'string', description: 'Job name to remove' },
|
|
213
|
+
},
|
|
214
|
+
required: ['name'],
|
|
215
|
+
},
|
|
216
|
+
async execute(_id: string, params: any) {
|
|
217
|
+
const result = await sidecarFetch(`/api/cron/${encodeURIComponent(params.name)}`, {
|
|
218
|
+
method: 'DELETE',
|
|
219
|
+
});
|
|
220
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
221
|
+
},
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
api.registerTool({
|
|
225
|
+
name: 'nats_cron_list',
|
|
226
|
+
description: 'List all scheduled cron jobs with their next run time and status.',
|
|
227
|
+
parameters: { type: 'object', properties: {} },
|
|
228
|
+
async execute() {
|
|
229
|
+
const result = await sidecarFetch('/api/cron');
|
|
230
|
+
return { content: [{ type: 'text', text: JSON.stringify(result) }] };
|
|
231
|
+
},
|
|
232
|
+
});
|
|
233
|
+
|
|
175
234
|
// ── Dashboard UI ─────────────────────────────────────────────────
|
|
176
235
|
|
|
177
236
|
api.registerHttpRoute({
|
package/sidecar/bun.lock
CHANGED
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
"": {
|
|
6
6
|
"name": "@ai-entrepreneur/nats-sidecar",
|
|
7
7
|
"dependencies": {
|
|
8
|
-
"@onebun/core": "^0.2.
|
|
8
|
+
"@onebun/core": "^0.2.14",
|
|
9
9
|
"@onebun/drizzle": "^0.2.4",
|
|
10
|
-
"@onebun/envs": "^0.2.
|
|
10
|
+
"@onebun/envs": "^0.2.2",
|
|
11
11
|
"@onebun/logger": "^0.2.1",
|
|
12
|
-
"@onebun/nats": "^0.2.
|
|
12
|
+
"@onebun/nats": "^0.2.6",
|
|
13
13
|
"arktype": "^2.2.0",
|
|
14
14
|
"ulid": "^2.3.0",
|
|
15
15
|
},
|
|
@@ -104,17 +104,17 @@
|
|
|
104
104
|
|
|
105
105
|
"@nats-io/transport-node": ["@nats-io/transport-node@3.3.1", "", { "dependencies": { "@nats-io/nats-core": "3.3.1", "@nats-io/nkeys": "2.0.3", "@nats-io/nuid": "2.0.3" } }, "sha512-GBvY0VcvyQEILgy5bjpqU1GpDYmSF06bW59I7cewZuNGS9u3AoV/gf+a+3ep45T/Z+UC661atq/b7x+QV12w+Q=="],
|
|
106
106
|
|
|
107
|
-
"@onebun/core": ["@onebun/core@0.2.
|
|
107
|
+
"@onebun/core": ["@onebun/core@0.2.14", "", { "dependencies": { "@onebun/envs": "^0.2.2", "@onebun/logger": "^0.2.1", "@onebun/metrics": "^0.2.2", "@onebun/requests": "^0.2.1", "@onebun/trace": "^0.2.1", "arktype": "^2.0.0", "effect": "^3.13.10" }, "peerDependencies": { "testcontainers": ">=10.0.0" }, "optionalPeers": ["testcontainers"] }, "sha512-uClu4Oez18y6BldubdE6R/I02Brrk5eXCoxxPatgRJ9qYRM+jKSTNmeZVoqqo7ai/rMYM9lJ1WuF9d7p6/RtDA=="],
|
|
108
108
|
|
|
109
109
|
"@onebun/drizzle": ["@onebun/drizzle@0.2.4", "", { "dependencies": { "@onebun/envs": "^0.2.1", "@onebun/logger": "^0.2.1", "arktype": "^2.0.0", "drizzle-arktype": "^0.1.3", "drizzle-kit": "^0.31.6", "drizzle-orm": "^0.44.7", "effect": "^3.13.10" }, "peerDependencies": { "@onebun/core": ">=0.2.0" }, "bin": { "onebun-drizzle": "bin/drizzle-kit.ts" } }, "sha512-LbkW2hU9pTKZU/rlrHNdwhI4jYoMl+v22c3G2zc0L0aW77nW7ZCfp5YqOJYufWJbfOTSWEnNOVZQXMueYhBxsA=="],
|
|
110
110
|
|
|
111
|
-
"@onebun/envs": ["@onebun/envs@0.2.
|
|
111
|
+
"@onebun/envs": ["@onebun/envs@0.2.2", "", { "dependencies": { "effect": "^3.13.10" } }, "sha512-WIjc1LpGnecYArSWsZhheyUSYJlo+iz9SA7ZfIXQnt1vkLd7ILCmVCtODBvqG9Mh86CMmromf1lCyRkjNZyLoA=="],
|
|
112
112
|
|
|
113
113
|
"@onebun/logger": ["@onebun/logger@0.2.1", "", { "dependencies": { "effect": "^3.13.10" } }, "sha512-u/zirsUSGBfbjVv274qqIHG5jzPBWY3vl8HzM6hjzsMCCpExgstkQiP1eP9rF1isIzhetwmyfBpYYc9eYsbrrw=="],
|
|
114
114
|
|
|
115
115
|
"@onebun/metrics": ["@onebun/metrics@0.2.2", "", { "dependencies": { "@onebun/requests": "^0.2.1", "effect": "^3.13.10", "prom-client": "^15.1.3" } }, "sha512-8oN74MZeaWyyPHi5H3pZyY0V3cM8ORupHe2fR0gGYsZqHyM4S9UJStV29rNQlvhyesHzL7p5x3Ux6n/SRYBszw=="],
|
|
116
116
|
|
|
117
|
-
"@onebun/nats": ["@onebun/nats@0.2.
|
|
117
|
+
"@onebun/nats": ["@onebun/nats@0.2.6", "", { "dependencies": { "@nats-io/jetstream": "^3.0.0-31", "@nats-io/transport-node": "^3.0.0-31", "effect": "^3.13.10" }, "peerDependencies": { "@onebun/core": ">=0.2.0" } }, "sha512-b7b0PUu0eGDLPbstOVjaCW0GCKsBzQb8qyYQmjKPu6RCsm02qvUU8HWDytoy01mYnQxWpI93qVogGiQKd6Ps4A=="],
|
|
118
118
|
|
|
119
119
|
"@onebun/requests": ["@onebun/requests@0.2.1", "", { "dependencies": { "effect": "^3.13.10" } }, "sha512-Wit+o3zRiuZOM7O0nAJ0rpFVLgkypJ1UR5uuHi0IZuiCvGmxv+Vus2+QqHoCL141L7SPO1Xlywt8dVqqu4NP7w=="],
|
|
120
120
|
|
|
@@ -446,6 +446,8 @@
|
|
|
446
446
|
|
|
447
447
|
"@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="],
|
|
448
448
|
|
|
449
|
+
"@onebun/drizzle/@onebun/envs": ["@onebun/envs@0.2.1", "", { "dependencies": { "effect": "^3.13.10" } }, "sha512-kiXJcA4ct194+aNJK8zkrVuaAgPPVpTkcW8tJU9XN9KOh8003lENOOuUZUcieMCxdMWUgo08lp9UgiwawLan+Q=="],
|
|
450
|
+
|
|
449
451
|
"bcrypt-pbkdf/tweetnacl": ["tweetnacl@0.14.5", "", {}, "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA=="],
|
|
450
452
|
|
|
451
453
|
"bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
|
package/sidecar/package.json
CHANGED
|
@@ -11,14 +11,14 @@
|
|
|
11
11
|
"typecheck": "bunx tsc --noEmit",
|
|
12
12
|
"db:generate": "bunx onebun-drizzle generate",
|
|
13
13
|
"db:push": "bunx onebun-drizzle push",
|
|
14
|
-
"db:studio": "bunx onebun-drizzle studio"
|
|
14
|
+
"db:studio": "bunx onebun-drizzle studio",
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@onebun/core": "^0.2.
|
|
17
|
+
"@onebun/core": "^0.2.14",
|
|
18
18
|
"@onebun/drizzle": "^0.2.4",
|
|
19
|
-
"@onebun/envs": "^0.2.
|
|
19
|
+
"@onebun/envs": "^0.2.2",
|
|
20
20
|
"@onebun/logger": "^0.2.1",
|
|
21
|
-
"@onebun/nats": "^0.2.
|
|
21
|
+
"@onebun/nats": "^0.2.6",
|
|
22
22
|
"arktype": "^2.2.0",
|
|
23
23
|
"ulid": "^2.3.0"
|
|
24
24
|
},
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { Module } from '@onebun/core';
|
|
1
|
+
import { getConfig, Module } from '@onebun/core';
|
|
2
2
|
import { DrizzleModule, DatabaseType } from '@onebun/drizzle';
|
|
3
|
+
import { envSchema, type AppConfig } from './config';
|
|
3
4
|
import { DedupModule } from './dedup/dedup.module';
|
|
4
5
|
import { PublisherModule } from './publisher/publisher.module';
|
|
5
6
|
import { PreHandlersModule } from './pre-handlers/pre-handlers.module';
|
|
@@ -8,6 +9,10 @@ import { ConsumerModule } from './consumer/consumer.module';
|
|
|
8
9
|
import { PendingModule } from './pending/pending.module';
|
|
9
10
|
import { HealthModule } from './health/health.module';
|
|
10
11
|
import { RouterModule } from './router/router.module';
|
|
12
|
+
import { SchedulerModule } from './scheduler/scheduler.module';
|
|
13
|
+
import { MetricsModule } from './metrics/metrics.module';
|
|
14
|
+
|
|
15
|
+
const config = getConfig<AppConfig>(envSchema);
|
|
11
16
|
|
|
12
17
|
@Module({
|
|
13
18
|
imports: [
|
|
@@ -15,7 +20,7 @@ import { RouterModule } from './router/router.module';
|
|
|
15
20
|
connection: {
|
|
16
21
|
type: DatabaseType.SQLITE,
|
|
17
22
|
options: {
|
|
18
|
-
url:
|
|
23
|
+
url: config.get('database.url'),
|
|
19
24
|
},
|
|
20
25
|
},
|
|
21
26
|
migrationsFolder: './src/db/migrations',
|
|
@@ -28,6 +33,8 @@ import { RouterModule } from './router/router.module';
|
|
|
28
33
|
PendingModule,
|
|
29
34
|
HealthModule,
|
|
30
35
|
RouterModule,
|
|
36
|
+
SchedulerModule,
|
|
37
|
+
MetricsModule,
|
|
31
38
|
],
|
|
32
39
|
})
|
|
33
40
|
export class AppModule {}
|
|
@@ -3,6 +3,7 @@ import { PipelineService } from '../pre-handlers/pipeline.service';
|
|
|
3
3
|
import { GatewayClientService } from '../gateway/gateway-client.service';
|
|
4
4
|
import { PendingService } from '../pending/pending.service';
|
|
5
5
|
import { RouterService } from '../router/router.service';
|
|
6
|
+
import { MetricsService } from '../metrics/metrics.service';
|
|
6
7
|
import type { NatsEventEnvelope } from '../publisher/envelope';
|
|
7
8
|
|
|
8
9
|
@Controller('/consumer')
|
|
@@ -12,6 +13,7 @@ export class ConsumerController extends BaseController {
|
|
|
12
13
|
private gatewayClient: GatewayClientService,
|
|
13
14
|
private pendingService: PendingService,
|
|
14
15
|
private routerService: RouterService,
|
|
16
|
+
private metrics: MetricsService,
|
|
15
17
|
) {
|
|
16
18
|
super();
|
|
17
19
|
}
|
|
@@ -57,6 +59,8 @@ export class ConsumerController extends BaseController {
|
|
|
57
59
|
priority: (ctx.enrichments['priority'] as number) ?? envelope.meta?.priority ?? 5,
|
|
58
60
|
},
|
|
59
61
|
});
|
|
62
|
+
await this.routerService.recordDelivery(route.id, envelope.subject);
|
|
63
|
+
this.metrics.recordConsume(envelope.subject);
|
|
60
64
|
}
|
|
61
65
|
await message.ack();
|
|
62
66
|
} else {
|
|
@@ -3,9 +3,10 @@ import { ConsumerController } from './consumer.controller';
|
|
|
3
3
|
import { PreHandlersModule } from '../pre-handlers/pre-handlers.module';
|
|
4
4
|
import { PendingModule } from '../pending/pending.module';
|
|
5
5
|
import { RouterModule } from '../router/router.module';
|
|
6
|
+
import { MetricsModule } from '../metrics/metrics.module';
|
|
6
7
|
|
|
7
8
|
@Module({
|
|
8
|
-
imports: [PreHandlersModule, PendingModule, RouterModule],
|
|
9
|
+
imports: [PreHandlersModule, PendingModule, RouterModule, MetricsModule],
|
|
9
10
|
controllers: [ConsumerController],
|
|
10
11
|
})
|
|
11
12
|
export class ConsumerModule {}
|