@coffeexdev/openclaw-sentinel 0.4.1 → 0.4.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/README.md CHANGED
@@ -132,7 +132,7 @@ It **does not** execute user-authored code from watcher definitions.
132
132
  ## Features
133
133
 
134
134
  - Tool registration: `sentinel_control`
135
- - actions: `create`, `enable`, `disable`, `remove`, `status`, `list`
135
+ - actions: `create` (`add`), `enable`, `disable`, `remove` (`delete`), `status` (`get`), `list`
136
136
  - Strict schema validation (TypeBox, strict object checks) + code-like field/value rejection
137
137
  - Strategies:
138
138
  - `http-poll`
package/dist/tool.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import { jsonResult } from "openclaw/plugin-sdk";
2
2
  import { Value } from "@sinclair/typebox/value";
3
3
  import { SentinelToolSchema } from "./toolSchema.js";
4
+ import { TemplateValueSchema } from "./templateValueSchema.js";
4
5
  function validateParams(params) {
5
6
  const candidate = (params ?? {});
6
- if (!Value.Check(SentinelToolSchema, candidate)) {
7
- const first = [...Value.Errors(SentinelToolSchema, candidate)][0];
7
+ if (!Value.Check(SentinelToolSchema, [TemplateValueSchema], candidate)) {
8
+ const first = [...Value.Errors(SentinelToolSchema, [TemplateValueSchema], candidate)][0];
8
9
  const where = first?.path || "(root)";
9
10
  const why = first?.message || "Invalid parameters";
10
11
  throw new Error(`Invalid sentinel_control parameters at ${where}: ${why}`);
@@ -35,17 +36,20 @@ export function registerSentinelControl(registerTool, manager) {
35
36
  const payload = validateParams(params);
36
37
  switch (payload.action) {
37
38
  case "create":
39
+ case "add":
38
40
  return jsonResult(await manager.create(payload.watcher, {
39
41
  deliveryTargets: inferDefaultDeliveryTargets(ctx),
40
42
  }));
41
43
  case "enable":
42
- return jsonResult(await manager.enable(payload.id ?? ""));
44
+ return jsonResult(await manager.enable(payload.id));
43
45
  case "disable":
44
- return jsonResult(await manager.disable(payload.id ?? ""));
46
+ return jsonResult(await manager.disable(payload.id));
45
47
  case "remove":
46
- return jsonResult(await manager.remove(payload.id ?? ""));
48
+ case "delete":
49
+ return jsonResult(await manager.remove(payload.id));
47
50
  case "status":
48
- return jsonResult(manager.status(payload.id ?? ""));
51
+ case "get":
52
+ return jsonResult(manager.status(payload.id));
49
53
  case "list":
50
54
  return jsonResult(manager.list());
51
55
  }
@@ -1,7 +1,6 @@
1
- export declare const SentinelToolSchema: import("@sinclair/typebox").TObject<{
2
- action: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"create">, import("@sinclair/typebox").TLiteral<"enable">, import("@sinclair/typebox").TLiteral<"disable">, import("@sinclair/typebox").TLiteral<"remove">, import("@sinclair/typebox").TLiteral<"status">, import("@sinclair/typebox").TLiteral<"list">]>;
3
- id: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
4
- watcher: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TObject<{
1
+ export declare const SentinelToolSchema: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TObject<{
2
+ action: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"create">, import("@sinclair/typebox").TLiteral<"add">]>;
3
+ watcher: import("@sinclair/typebox").TObject<{
5
4
  id: import("@sinclair/typebox").TString;
6
5
  skillId: import("@sinclair/typebox").TString;
7
6
  enabled: import("@sinclair/typebox").TBoolean;
@@ -21,9 +20,9 @@ export declare const SentinelToolSchema: import("@sinclair/typebox").TObject<{
21
20
  fire: import("@sinclair/typebox").TObject<{
22
21
  webhookPath: import("@sinclair/typebox").TString;
23
22
  eventName: import("@sinclair/typebox").TString;
24
- payloadTemplate: import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, any>;
23
+ payloadTemplate: import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TRef<any>>;
25
24
  intent: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
26
- contextTemplate: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, any>>;
25
+ contextTemplate: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TRef<any>>>;
27
26
  priority: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"low">, import("@sinclair/typebox").TLiteral<"normal">, import("@sinclair/typebox").TLiteral<"high">, import("@sinclair/typebox").TLiteral<"critical">]>>;
28
27
  deadlineTemplate: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
29
28
  dedupeKeyTemplate: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
@@ -40,5 +39,10 @@ export declare const SentinelToolSchema: import("@sinclair/typebox").TObject<{
40
39
  accountId: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
41
40
  }>>>;
42
41
  metadata: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TString>>;
43
- }>>;
44
- }>;
42
+ }>;
43
+ }>, import("@sinclair/typebox").TObject<{
44
+ action: import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"enable">, import("@sinclair/typebox").TLiteral<"disable">, import("@sinclair/typebox").TLiteral<"remove">, import("@sinclair/typebox").TLiteral<"delete">, import("@sinclair/typebox").TLiteral<"status">, import("@sinclair/typebox").TLiteral<"get">]>;
45
+ id: import("@sinclair/typebox").TString;
46
+ }>, import("@sinclair/typebox").TObject<{
47
+ action: import("@sinclair/typebox").TLiteral<"list">;
48
+ }>]>;
@@ -1,5 +1,6 @@
1
1
  import { Type } from "@sinclair/typebox";
2
2
  import { TemplateValueSchema } from "./templateValueSchema.js";
3
+ const TemplateValueRefSchema = Type.Ref(TemplateValueSchema);
3
4
  const ConditionSchema = Type.Object({
4
5
  path: Type.String({ description: "JSONPath expression to evaluate against the response" }),
5
6
  op: Type.Union([
@@ -24,11 +25,11 @@ const FireConfigSchema = Type.Object({
24
25
  description: "Path appended to localDispatchBase for webhook delivery",
25
26
  }),
26
27
  eventName: Type.String({ description: "Event name included in the dispatched payload" }),
27
- payloadTemplate: Type.Record(Type.String(), TemplateValueSchema, {
28
+ payloadTemplate: Type.Record(Type.String(), TemplateValueRefSchema, {
28
29
  description: "Key-value template for the webhook payload. Supports ${...} interpolation from matched response data.",
29
30
  }),
30
31
  intent: Type.Optional(Type.String({ description: "Generic callback intent for downstream agent routing" })),
31
- contextTemplate: Type.Optional(Type.Record(Type.String(), TemplateValueSchema, {
32
+ contextTemplate: Type.Optional(Type.Record(Type.String(), TemplateValueRefSchema, {
32
33
  description: "Structured callback context template. Supports ${...} interpolation from matched response data.",
33
34
  })),
34
35
  priority: Type.Optional(Type.Union([Type.Literal("low"), Type.Literal("normal"), Type.Literal("high"), Type.Literal("critical")], { description: "Callback urgency hint" })),
@@ -79,15 +80,28 @@ const WatcherSchema = Type.Object({
79
80
  })),
80
81
  metadata: Type.Optional(Type.Record(Type.String(), Type.String(), { description: "Arbitrary key-value metadata" })),
81
82
  }, { description: "Full watcher definition" });
82
- export const SentinelToolSchema = Type.Object({
83
+ const CreateActionSchema = Type.Object({
84
+ action: Type.Union([Type.Literal("create"), Type.Literal("add")], {
85
+ description: "Create action (alias: add)",
86
+ }),
87
+ watcher: WatcherSchema,
88
+ }, { additionalProperties: false });
89
+ const IdActionSchema = Type.Object({
83
90
  action: Type.Union([
84
- Type.Literal("create"),
85
91
  Type.Literal("enable"),
86
92
  Type.Literal("disable"),
87
93
  Type.Literal("remove"),
94
+ Type.Literal("delete"),
88
95
  Type.Literal("status"),
89
- Type.Literal("list"),
90
- ], { description: "The action to perform" }),
91
- id: Type.Optional(Type.String({ description: "Watcher ID (required for enable/disable/remove/status)" })),
92
- watcher: Type.Optional(WatcherSchema),
96
+ Type.Literal("get"),
97
+ ], { description: "ID-targeting action aliases: delete/remove and get/status" }),
98
+ id: Type.String({ description: "Watcher ID for action target" }),
93
99
  }, { additionalProperties: false });
100
+ const ListActionSchema = Type.Object({
101
+ action: Type.Literal("list", { description: "List all watchers" }),
102
+ }, { additionalProperties: false });
103
+ export const SentinelToolSchema = Type.Union([CreateActionSchema, IdActionSchema, ListActionSchema], {
104
+ $defs: {
105
+ templateValue: TemplateValueSchema,
106
+ },
107
+ });
@@ -19,9 +19,9 @@ export declare const WatcherSchema: import("@sinclair/typebox").TObject<{
19
19
  fire: import("@sinclair/typebox").TObject<{
20
20
  webhookPath: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
21
21
  eventName: import("@sinclair/typebox").TString;
22
- payloadTemplate: import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, any>;
22
+ payloadTemplate: import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TRef<any>>;
23
23
  intent: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
24
- contextTemplate: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, any>>;
24
+ contextTemplate: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TRecord<import("@sinclair/typebox").TString, import("@sinclair/typebox").TRef<any>>>;
25
25
  priority: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TUnion<[import("@sinclair/typebox").TLiteral<"low">, import("@sinclair/typebox").TLiteral<"normal">, import("@sinclair/typebox").TLiteral<"high">, import("@sinclair/typebox").TLiteral<"critical">]>>;
26
26
  deadlineTemplate: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
27
27
  dedupeKeyTemplate: import("@sinclair/typebox").TOptional<import("@sinclair/typebox").TString>;
package/dist/validator.js CHANGED
@@ -2,6 +2,7 @@ import { Type } from "@sinclair/typebox";
2
2
  import { Value } from "@sinclair/typebox/value";
3
3
  import { TemplateValueSchema } from "./templateValueSchema.js";
4
4
  import { DEFAULT_SENTINEL_WEBHOOK_PATH } from "./types.js";
5
+ const TemplateValueRefSchema = Type.Ref(TemplateValueSchema);
5
6
  const codeyKeyPattern = /(script|code|eval|handler|function|import|require)/i;
6
7
  const codeyValuePattern = /(=>|\bfunction\b|\bimport\s+|\brequire\s*\(|\beval\s*\()/i;
7
8
  const ConditionSchema = Type.Object({
@@ -42,9 +43,9 @@ export const WatcherSchema = Type.Object({
42
43
  fire: Type.Object({
43
44
  webhookPath: Type.Optional(Type.String({ pattern: "^/" })),
44
45
  eventName: Type.String({ minLength: 1 }),
45
- payloadTemplate: Type.Record(Type.String(), TemplateValueSchema),
46
+ payloadTemplate: Type.Record(Type.String(), TemplateValueRefSchema),
46
47
  intent: Type.Optional(Type.String({ minLength: 1 })),
47
- contextTemplate: Type.Optional(Type.Record(Type.String(), TemplateValueSchema)),
48
+ contextTemplate: Type.Optional(Type.Record(Type.String(), TemplateValueRefSchema)),
48
49
  priority: Type.Optional(Type.Union([
49
50
  Type.Literal("low"),
50
51
  Type.Literal("normal"),
@@ -66,7 +67,12 @@ export const WatcherSchema = Type.Object({
66
67
  accountId: Type.Optional(Type.String({ minLength: 1 })),
67
68
  }, { additionalProperties: false }), { minItems: 1 })),
68
69
  metadata: Type.Optional(Type.Record(Type.String(), Type.String())),
69
- }, { additionalProperties: false });
70
+ }, {
71
+ additionalProperties: false,
72
+ $defs: {
73
+ templateValue: TemplateValueSchema,
74
+ },
75
+ });
70
76
  function scanNoCodeLike(input, parentKey = "") {
71
77
  if (input === null || input === undefined)
72
78
  return;
@@ -91,8 +97,8 @@ function scanNoCodeLike(input, parentKey = "") {
91
97
  }
92
98
  export function validateWatcherDefinition(input) {
93
99
  scanNoCodeLike(input);
94
- if (!Value.Check(WatcherSchema, input)) {
95
- const first = [...Value.Errors(WatcherSchema, input)][0];
100
+ if (!Value.Check(WatcherSchema, [TemplateValueSchema], input)) {
101
+ const first = [...Value.Errors(WatcherSchema, [TemplateValueSchema], input)][0];
96
102
  const where = first?.path || "(root)";
97
103
  const why = first?.message || "Invalid watcher definition";
98
104
  throw new Error(`Invalid watcher definition at ${where}: ${why}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coffeexdev/openclaw-sentinel",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "Secure declarative gateway-native watcher plugin for OpenClaw",
5
5
  "keywords": [
6
6
  "openclaw",
@@ -38,6 +38,7 @@
38
38
  "@changesets/cli": "^2.29.7",
39
39
  "@types/node": "^24.0.0",
40
40
  "@types/ws": "^8.5.13",
41
+ "ajv": "^8.17.1",
41
42
  "husky": "^9.1.7",
42
43
  "lint-staged": "^16.3.2",
43
44
  "openclaw": "latest",