openclaw-sentinel-cli 1.1.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,MAAM,yBAAyB,CAAC;AAC5D,OAAO,EAAE,oBAAoB,EAAE,MAAM,+BAA+B,CAAC;AAErE,MAAM,OAAO,QAAQ;IACX,MAAM,CAAiB;IACxB,MAAM,CAAQ;IAErB,YAAY,MAAsF;QAChG,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;QAC7D,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,qBAAqB,CAAC;QACpE,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,eAAe,CAAC;QAC3G,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,IAAI,OAAO,CAAC,GAAG,CAAC,sBAAsB,CAAC;QAE/E,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,KAAK,CAAC,8CAA8C,CAAC,CAAC;QAClE,CAAC;QAED,IAAI,CAAC,MAAM,GAAG,IAAI,cAAc,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;QAE/D,IAAI,CAAC,MAAM,GAAG;YACZ,iBAAiB,CAAC,IAAI,CAAC,MAAM,CAAC;YAC9B,uBAAuB,CAAC,IAAI,CAAC,MAAM,CAAC;YACpC,iBAAiB,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,YAAY,EAAE,CAAC;YAChD,oBAAoB,CAAC,IAAI,CAAC,MAAM,CAAC;SAClC,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,24 @@
1
+ type SentinelPluginConfig = {
2
+ apiKey?: string;
3
+ baseUrl?: string;
4
+ agentId?: string;
5
+ workspaceDir?: string;
6
+ };
7
+ type ToolRegistration = {
8
+ name: string;
9
+ description: string;
10
+ annotations: {
11
+ readOnlyHint: boolean;
12
+ destructiveHint: boolean;
13
+ idempotentHint: boolean;
14
+ openWorldHint: boolean;
15
+ };
16
+ inputSchema: Record<string, unknown>;
17
+ execute: (params: Record<string, unknown>) => Promise<unknown>;
18
+ };
19
+ type PluginAPI = {
20
+ getConfig: <T = SentinelPluginConfig>() => T;
21
+ registerTool: (tool: ToolRegistration) => void;
22
+ };
23
+ export declare function register(api: PluginAPI): Promise<void>;
24
+ export {};
@@ -0,0 +1,144 @@
1
+ import { SentinelClient } from './client.js';
2
+ import { createBackupSkill } from './skills/backupBrain.js';
3
+ import { createHeartbeatSkill } from './skills/sentinelHeartbeat.js';
4
+ import { createMemorySkill } from './skills/remoteMemory.js';
5
+ import { createMemorySearchSkill } from './skills/searchMemory.js';
6
+ const toolSchemas = {
7
+ save_remote_memory: {
8
+ type: 'object',
9
+ properties: {
10
+ content: {
11
+ type: 'string',
12
+ description: 'The fact or data to remember',
13
+ },
14
+ tags: {
15
+ type: 'array',
16
+ items: { type: 'string' },
17
+ description: 'Keywords for categorization',
18
+ },
19
+ source: {
20
+ type: 'string',
21
+ enum: ['user', 'web', 'system', 'file'],
22
+ description: 'Optional memory source',
23
+ },
24
+ },
25
+ required: ['content'],
26
+ additionalProperties: false,
27
+ },
28
+ search_remote_memory: {
29
+ type: 'object',
30
+ properties: {
31
+ query: {
32
+ type: 'string',
33
+ minLength: 1,
34
+ description: 'Search phrase for memory retrieval',
35
+ },
36
+ limit: {
37
+ type: 'integer',
38
+ minimum: 1,
39
+ maximum: 20,
40
+ description: 'Maximum number of results to return (default 5)',
41
+ },
42
+ tags: {
43
+ type: 'array',
44
+ items: { type: 'string' },
45
+ description: 'Optional tag filters',
46
+ },
47
+ },
48
+ required: ['query'],
49
+ additionalProperties: false,
50
+ },
51
+ backup_brain: {
52
+ type: 'object',
53
+ properties: {
54
+ trigger: {
55
+ type: 'string',
56
+ enum: ['manual', 'scheduled'],
57
+ description: 'Reason for backup',
58
+ },
59
+ workspaceDir: {
60
+ type: 'string',
61
+ minLength: 1,
62
+ description: 'Optional explicit OpenClaw workspace directory containing AGENTS.md/IDENTITY.md/SOUL.md/TOOLS.md/USER.md',
63
+ },
64
+ },
65
+ required: ['trigger'],
66
+ additionalProperties: false,
67
+ },
68
+ sentinel_heartbeat: {
69
+ type: 'object',
70
+ properties: {},
71
+ additionalProperties: false,
72
+ },
73
+ };
74
+ export async function register(api) {
75
+ const configGetter = api.getConfig;
76
+ const config = typeof configGetter === 'function' ? (configGetter.call(api) ?? {}) : {};
77
+ const apiKey = config.apiKey ?? process.env.SENTINEL_API_KEY;
78
+ const baseUrl = config.baseUrl ?? process.env.SENTINEL_API_BASE_URL;
79
+ const agentId = config.agentId ?? process.env.SENTINEL_AGENT_ID ?? process.env.AGENT_ID ?? 'default-agent';
80
+ const workspaceDir = config.workspaceDir ?? process.env.OPENCLAW_WORKSPACE_DIR;
81
+ const notConfiguredMessage = "Sentinel API key is not configured. Set plugin config 'apiKey' or SENTINEL_API_KEY, then reload plugins.";
82
+ const client = apiKey ? new SentinelClient({ apiKey, baseUrl, agentId }) : null;
83
+ const skills = client
84
+ ? [
85
+ createMemorySkill(client),
86
+ createMemorySearchSkill(client),
87
+ createBackupSkill(client, { workspaceDir }),
88
+ createHeartbeatSkill(client),
89
+ ]
90
+ : [
91
+ {
92
+ name: 'save_remote_memory',
93
+ description: "Saves important information to Sentinel Cloud. Trigger this if the user says 'Remember that...' or provides critical config/preferences.",
94
+ execute: async () => notConfiguredMessage,
95
+ },
96
+ {
97
+ name: 'search_remote_memory',
98
+ description: 'Searches saved Sentinel memories by query and optional tags.',
99
+ execute: async () => notConfiguredMessage,
100
+ },
101
+ {
102
+ name: 'backup_brain',
103
+ description: 'Uploads critical agent identity files (IDENTITY.md, etc.) to the cloud.',
104
+ execute: async () => notConfiguredMessage,
105
+ },
106
+ {
107
+ name: 'sentinel_heartbeat',
108
+ description: 'Checks the status of the Sentinel cloud connection. Run this periodically.',
109
+ execute: async () => notConfiguredMessage,
110
+ },
111
+ ];
112
+ const registerToolFn = api.registerTool ??
113
+ api.tool?.register;
114
+ if (typeof registerToolFn !== 'function') {
115
+ throw new Error('Unsupported OpenClaw PluginAPI: registerTool is unavailable.');
116
+ }
117
+ for (const skill of skills) {
118
+ registerToolFn({
119
+ name: skill.name,
120
+ description: skill.description,
121
+ annotations: {
122
+ readOnlyHint: skill.name === 'sentinel_heartbeat' || skill.name === 'search_remote_memory',
123
+ destructiveHint: false,
124
+ idempotentHint: skill.name === 'sentinel_heartbeat',
125
+ openWorldHint: false,
126
+ },
127
+ inputSchema: toolSchemas[skill.name] ?? {
128
+ type: 'object',
129
+ properties: {},
130
+ additionalProperties: false,
131
+ },
132
+ execute: async (params) => {
133
+ try {
134
+ return await skill.execute(params ?? {});
135
+ }
136
+ catch (error) {
137
+ const message = error instanceof Error ? error.message : String(error);
138
+ return `Sentinel tool '${skill.name}' failed: ${message}. Verify SENTINEL_API_KEY, baseUrl, and gateway scope.`;
139
+ }
140
+ },
141
+ });
142
+ }
143
+ }
144
+ //# sourceMappingURL=openclaw.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openclaw.js","sourceRoot":"","sources":["../src/openclaw.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import { z } from 'zod';
2
+ import { SentinelClient } from '../client.js';
3
+ type BackupSkillOptions = {
4
+ workspaceDir?: string;
5
+ };
6
+ export declare const createBackupSkill: (sentinel: SentinelClient, options?: BackupSkillOptions) => {
7
+ name: string;
8
+ description: string;
9
+ parameters: z.ZodObject<{
10
+ trigger: z.ZodEnum<["manual", "scheduled"]>;
11
+ workspaceDir: z.ZodOptional<z.ZodString>;
12
+ }, "strip", z.ZodTypeAny, {
13
+ trigger: "manual" | "scheduled";
14
+ workspaceDir?: string | undefined;
15
+ }, {
16
+ trigger: "manual" | "scheduled";
17
+ workspaceDir?: string | undefined;
18
+ }>;
19
+ execute: (args: any) => Promise<string>;
20
+ };
21
+ export {};
@@ -0,0 +1,43 @@
1
+ import { z } from 'zod';
2
+ import * as fs from 'fs/promises';
3
+ import * as path from 'path';
4
+ import { resolveWorkspaceDir } from '../workspace.js';
5
+ export const createBackupSkill = (sentinel, options = {}) => ({
6
+ name: 'backup_brain',
7
+ description: 'Uploads critical agent identity files (IDENTITY.md, etc.) to the cloud.',
8
+ parameters: z.object({
9
+ trigger: z.enum(['manual', 'scheduled']).describe('Reason for backup'),
10
+ workspaceDir: z
11
+ .string()
12
+ .min(1)
13
+ .optional()
14
+ .describe('Optional explicit OpenClaw workspace directory containing required identity files.'),
15
+ }),
16
+ execute: async (args) => {
17
+ const filesToBackup = ['IDENTITY.md', 'AGENTS.md', 'SOUL.md', 'TOOLS.md', 'USER.md'];
18
+ const fileData = [];
19
+ const missingFiles = [];
20
+ const workspaceDir = resolveWorkspaceDir({ workspaceDir: args?.workspaceDir ?? options.workspaceDir });
21
+ for (const file of filesToBackup) {
22
+ try {
23
+ const content = await fs.readFile(path.join(workspaceDir, file), 'utf-8');
24
+ fileData.push({
25
+ name: file,
26
+ content,
27
+ size: `${Buffer.byteLength(content, 'utf-8')} B`,
28
+ });
29
+ }
30
+ catch {
31
+ missingFiles.push(file);
32
+ }
33
+ }
34
+ if (missingFiles.length > 0) {
35
+ return `Backup skipped. Missing required files: ${missingFiles.join(', ')}.`;
36
+ }
37
+ await sentinel.mutate('backups:create', {
38
+ files: fileData,
39
+ });
40
+ return `Backup successful. Uploaded ${fileData.length} files (${args.trigger}).`;
41
+ },
42
+ });
43
+ //# sourceMappingURL=backupBrain.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backupBrain.js","sourceRoot":"","sources":["../../src/skills/backupBrain.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,KAAK,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAMtD,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,QAAwB,EAAE,UAA8B,EAAE,EAAE,EAAE,CAAC,CAAC;IAChG,IAAI,EAAE,cAAc;IACpB,WAAW,EAAE,yEAAyE;IACtF,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC;QACnB,OAAO,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,QAAQ,EAAE,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,mBAAmB,CAAC;QACtE,YAAY,EAAE,CAAC;aACZ,MAAM,EAAE;aACR,GAAG,CAAC,CAAC,CAAC;aACN,QAAQ,EAAE;aACV,QAAQ,CAAC,oFAAoF,CAAC;KAClG,CAAC;IACF,OAAO,EAAE,KAAK,EAAE,IAAS,EAAE,EAAE;QAC3B,MAAM,aAAa,GAAG,CAAC,aAAa,EAAE,WAAW,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;QACrF,MAAM,QAAQ,GAA2D,EAAE,CAAC;QAC5E,MAAM,YAAY,GAAa,EAAE,CAAC;QAClC,MAAM,YAAY,GAAG,mBAAmB,CAAC,EAAE,YAAY,EAAE,IAAI,EAAE,YAAY,IAAI,OAAO,CAAC,YAAY,EAAE,CAAC,CAAC;QAEvG,KAAK,MAAM,IAAI,IAAI,aAAa,EAAE,CAAC;YACjC,IAAI,CAAC;gBACH,MAAM,OAAO,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,EAAE,OAAO,CAAC,CAAC;gBAC1E,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,IAAI;oBACV,OAAO;oBACP,IAAI,EAAE,GAAG,MAAM,CAAC,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI;iBACjD,CAAC,CAAC;YACL,CAAC;YAAC,MAAM,CAAC;gBACP,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAC1B,CAAC;QACH,CAAC;QAED,IAAI,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC5B,OAAO,2CAA2C,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;QAC/E,CAAC;QAED,MAAM,QAAQ,CAAC,MAAM,CAAC,gBAAgB,EAAE;YACtC,KAAK,EAAE,QAAQ;SAChB,CAAC,CAAC;QAEH,OAAO,+BAA+B,QAAQ,CAAC,MAAM,WAAW,IAAI,CAAC,OAAO,IAAI,CAAC;IACnF,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,58 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import * as os from 'node:os';
4
+ import * as path from 'node:path';
5
+ import * as fs from 'node:fs/promises';
6
+ import { createBackupSkill } from './backupBrain.js';
7
+ const REQUIRED_FILES = ['IDENTITY.md', 'AGENTS.md', 'SOUL.md', 'TOOLS.md', 'USER.md'];
8
+ class FakeSentinelClient {
9
+ calls = [];
10
+ async mutate(name, payload) {
11
+ this.calls.push({ name, payload });
12
+ }
13
+ }
14
+ async function writeRequiredFiles(workspaceDir, omit = []) {
15
+ for (const fileName of REQUIRED_FILES) {
16
+ if (omit.includes(fileName))
17
+ continue;
18
+ await fs.writeFile(path.join(workspaceDir, fileName), `content for ${fileName}\n`, 'utf-8');
19
+ }
20
+ }
21
+ test('backup_brain reads required files from configured workspace even when cwd is a subfolder', async () => {
22
+ const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), 'sentinel-backup-workspace-'));
23
+ const nestedDir = path.join(workspaceDir, 'projects', 'nested');
24
+ await fs.mkdir(nestedDir, { recursive: true });
25
+ await writeRequiredFiles(workspaceDir);
26
+ const previousCwd = process.cwd();
27
+ process.chdir(nestedDir);
28
+ try {
29
+ const sentinel = new FakeSentinelClient();
30
+ const skill = createBackupSkill(sentinel, { workspaceDir });
31
+ const result = await skill.execute({ trigger: 'manual' });
32
+ assert.match(result, /Backup successful\. Uploaded 5 files \(manual\)\./);
33
+ assert.equal(sentinel.calls.length, 1);
34
+ assert.equal(sentinel.calls[0]?.name, 'backups:create');
35
+ const payload = sentinel.calls[0]?.payload;
36
+ assert.equal(payload.files.length, 5);
37
+ assert.deepEqual(payload.files.map((file) => file.name).sort(), [...REQUIRED_FILES].sort());
38
+ }
39
+ finally {
40
+ process.chdir(previousCwd);
41
+ await fs.rm(workspaceDir, { recursive: true, force: true });
42
+ }
43
+ });
44
+ test('backup_brain skips upload when required files are missing in resolved workspace', async () => {
45
+ const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), 'sentinel-backup-missing-'));
46
+ await writeRequiredFiles(workspaceDir, ['USER.md']);
47
+ try {
48
+ const sentinel = new FakeSentinelClient();
49
+ const skill = createBackupSkill(sentinel, { workspaceDir });
50
+ const result = await skill.execute({ trigger: 'scheduled' });
51
+ assert.equal(result, 'Backup skipped. Missing required files: USER.md.');
52
+ assert.equal(sentinel.calls.length, 0);
53
+ }
54
+ finally {
55
+ await fs.rm(workspaceDir, { recursive: true, force: true });
56
+ }
57
+ });
58
+ //# sourceMappingURL=backupBrain.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backupBrain.test.js","sourceRoot":"","sources":["../../src/skills/backupBrain.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ import { z } from 'zod';
2
+ import { SentinelClient } from '../client.js';
3
+ export declare const createMemorySkill: (sentinel: SentinelClient) => {
4
+ name: string;
5
+ description: string;
6
+ parameters: z.ZodObject<{
7
+ content: z.ZodString;
8
+ tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
9
+ source: z.ZodOptional<z.ZodEnum<["user", "web", "system", "file"]>>;
10
+ }, "strip", z.ZodTypeAny, {
11
+ content: string;
12
+ tags?: string[] | undefined;
13
+ source?: "user" | "web" | "system" | "file" | undefined;
14
+ }, {
15
+ content: string;
16
+ tags?: string[] | undefined;
17
+ source?: "user" | "web" | "system" | "file" | undefined;
18
+ }>;
19
+ execute: (args: any) => Promise<string>;
20
+ };
@@ -0,0 +1,25 @@
1
+ import { z } from 'zod';
2
+ export const createMemorySkill = (sentinel) => ({
3
+ name: 'save_remote_memory',
4
+ description: 'Saves important information to Sentinel Cloud. ' +
5
+ "Trigger this if the user says 'Remember that...' or provides critical config/preferences.",
6
+ parameters: z.object({
7
+ content: z.string().describe('The fact or data to remember'),
8
+ tags: z.array(z.string()).optional().describe('Keywords for categorization'),
9
+ source: z.enum(['user', 'web', 'system', 'file']).optional(),
10
+ }),
11
+ execute: async (args) => {
12
+ try {
13
+ await sentinel.mutate('memories:create', {
14
+ content: args.content,
15
+ tags: Array.isArray(args.tags) ? args.tags : [],
16
+ source: args.source ?? 'user',
17
+ });
18
+ return 'Success: Memory saved to Sentinel Cloud.';
19
+ }
20
+ catch (e) {
21
+ return `Error saving memory: ${e.message}`;
22
+ }
23
+ },
24
+ });
25
+ //# sourceMappingURL=remoteMemory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"remoteMemory.js","sourceRoot":"","sources":["../../src/skills/remoteMemory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,QAAwB,EAAE,EAAE,CAAC,CAAC;IAC9D,IAAI,EAAE,oBAAoB;IAC1B,WAAW,EACT,iDAAiD;QACjD,2FAA2F;IAC7F,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC;QACnB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,8BAA8B,CAAC;QAC5D,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,6BAA6B,CAAC;QAC5E,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC,QAAQ,EAAE;KAC7D,CAAC;IACF,OAAO,EAAE,KAAK,EAAE,IAAS,EAAE,EAAE;QAC3B,IAAI,CAAC;YACH,MAAM,QAAQ,CAAC,MAAM,CAAC,iBAAiB,EAAE;gBACvC,OAAO,EAAE,IAAI,CAAC,OAAO;gBACrB,IAAI,EAAE,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE;gBAC/C,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,MAAM;aAC9B,CAAC,CAAC;YACH,OAAO,0CAA0C,CAAC;QACpD,CAAC;QAAC,OAAO,CAAM,EAAE,CAAC;YAChB,OAAO,wBAAwB,CAAC,CAAC,OAAO,EAAE,CAAC;QAC7C,CAAC;IACH,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,24 @@
1
+ import { z } from 'zod';
2
+ import { SentinelClient } from '../client.js';
3
+ export declare const createMemorySearchSkill: (sentinel: SentinelClient) => {
4
+ name: string;
5
+ description: string;
6
+ parameters: z.ZodObject<{
7
+ query: z.ZodString;
8
+ limit: z.ZodOptional<z.ZodNumber>;
9
+ tags: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
10
+ }, "strip", z.ZodTypeAny, {
11
+ query: string;
12
+ tags?: string[] | undefined;
13
+ limit?: number | undefined;
14
+ }, {
15
+ query: string;
16
+ tags?: string[] | undefined;
17
+ limit?: number | undefined;
18
+ }>;
19
+ execute: (input: unknown) => Promise<{
20
+ ok: boolean;
21
+ count: number;
22
+ results: Record<string, unknown>[];
23
+ }>;
24
+ };
@@ -0,0 +1,27 @@
1
+ import { z } from 'zod';
2
+ const SearchArgs = z.object({
3
+ query: z.string().min(1).describe('Search phrase for memory retrieval'),
4
+ limit: z.number().int().min(1).max(20).optional(),
5
+ tags: z.array(z.string()).optional(),
6
+ });
7
+ export const createMemorySearchSkill = (sentinel) => ({
8
+ name: 'search_remote_memory',
9
+ description: 'Searches saved Sentinel memories by query and optional tags.',
10
+ parameters: SearchArgs,
11
+ execute: async (input) => {
12
+ const args = SearchArgs.parse(input ?? {});
13
+ const response = (await sentinel.query('memories:search', {
14
+ query: args.query,
15
+ limit: args.limit ?? 5,
16
+ tags: args.tags,
17
+ includeArchived: false,
18
+ }));
19
+ const results = Array.isArray(response?.results) ? response.results : [];
20
+ return {
21
+ ok: true,
22
+ count: results.length,
23
+ results: results.slice(0, args.limit ?? 5),
24
+ };
25
+ },
26
+ });
27
+ //# sourceMappingURL=searchMemory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"searchMemory.js","sourceRoot":"","sources":["../../src/skills/searchMemory.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,MAAM,UAAU,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1B,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,oCAAoC,CAAC;IACvE,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,QAAQ,EAAE;IACjD,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;CACrC,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,QAAwB,EAAE,EAAE,CAAC,CAAC;IACpE,IAAI,EAAE,sBAAsB;IAC5B,WAAW,EAAE,8DAA8D;IAC3E,UAAU,EAAE,UAAU;IACtB,OAAO,EAAE,KAAK,EAAE,KAAc,EAAE,EAAE;QAChC,MAAM,IAAI,GAAG,UAAU,CAAC,KAAK,CAAC,KAAK,IAAI,EAAE,CAAC,CAAC;QAE3C,MAAM,QAAQ,GAAG,CAAC,MAAM,QAAQ,CAAC,KAAK,CAAC,iBAAiB,EAAE;YACxD,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,KAAK,EAAE,IAAI,CAAC,KAAK,IAAI,CAAC;YACtB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,eAAe,EAAE,KAAK;SACvB,CAAC,CAAiD,CAAC;QAEpD,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;QAEzE,OAAO;YACL,EAAE,EAAE,IAAI;YACR,KAAK,EAAE,OAAO,CAAC,MAAM;YACrB,OAAO,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,IAAI,CAAC,CAAC;SAC3C,CAAC;IACJ,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,8 @@
1
+ import { z } from 'zod';
2
+ import { SentinelClient } from '../client.js';
3
+ export declare const createHeartbeatSkill: (sentinel: SentinelClient) => {
4
+ name: string;
5
+ description: string;
6
+ parameters: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
7
+ execute: () => Promise<string>;
8
+ };
@@ -0,0 +1,64 @@
1
+ import { z } from 'zod';
2
+ const ONE_DAY_MS = 24 * 60 * 60 * 1000;
3
+ const lastSeenMemoryByAgent = new Map();
4
+ export const createHeartbeatSkill = (sentinel) => ({
5
+ name: 'sentinel_heartbeat',
6
+ description: 'Checks the status of the Sentinel cloud connection. Run this periodically.',
7
+ parameters: z.object({}),
8
+ execute: async () => {
9
+ const instructions = [];
10
+ let gatewayOk = false;
11
+ let needsBackup = false;
12
+ let hasNewMemories = false;
13
+ try {
14
+ await sentinel.query('health:get', {});
15
+ gatewayOk = true;
16
+ }
17
+ catch (error) {
18
+ const message = error instanceof Error ? error.message : String(error);
19
+ return `ALERT: Gateway health check failed (${message}).`;
20
+ }
21
+ try {
22
+ const backupResponse = (await sentinel.query('backups:list', {}));
23
+ const backups = Array.isArray(backupResponse.backups) ? backupResponse.backups : [];
24
+ const completed = backups.filter((entry) => entry.status === 'completed');
25
+ if (completed.length === 0) {
26
+ needsBackup = true;
27
+ }
28
+ else {
29
+ const latestTs = Math.max(...completed.map((entry) => typeof entry.createdAt === 'number' ? entry.createdAt : (entry._creationTime ?? 0)));
30
+ needsBackup = Date.now() - latestTs > ONE_DAY_MS;
31
+ }
32
+ }
33
+ catch {
34
+ needsBackup = true;
35
+ }
36
+ try {
37
+ const memoryResponse = (await sentinel.query('memories:search', {
38
+ query: '',
39
+ limit: 1,
40
+ includeArchived: false,
41
+ }));
42
+ const latestMemoryTs = memoryResponse.results?.[0]?._creationTime;
43
+ if (typeof latestMemoryTs === 'number') {
44
+ const lastSeen = lastSeenMemoryByAgent.get(sentinel.agentId) ?? 0;
45
+ hasNewMemories = latestMemoryTs > lastSeen;
46
+ lastSeenMemoryByAgent.set(sentinel.agentId, latestMemoryTs);
47
+ }
48
+ }
49
+ catch {
50
+ hasNewMemories = false;
51
+ }
52
+ if (needsBackup) {
53
+ instructions.push('Backup Required (Last backup > 24h ago).');
54
+ }
55
+ if (hasNewMemories) {
56
+ instructions.push('New Memories Available (User updated dashboard).');
57
+ }
58
+ if (instructions.length === 0) {
59
+ return gatewayOk ? 'System Nominal. Connection active.' : 'ALERT: Gateway unavailable.';
60
+ }
61
+ return `ALERT: ${instructions.join(' ')}`;
62
+ },
63
+ });
64
+ //# sourceMappingURL=sentinelHeartbeat.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sentinelHeartbeat.js","sourceRoot":"","sources":["../../src/skills/sentinelHeartbeat.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export type WorkspaceResolutionOptions = {
2
+ workspaceDir?: string;
3
+ };
4
+ export declare function resolveWorkspaceDir(options?: WorkspaceResolutionOptions): string;
5
+ export declare function resolveWorkspaceFilePath(fileName: string, options?: WorkspaceResolutionOptions): string;
@@ -0,0 +1,10 @@
1
+ import * as path from 'path';
2
+ export function resolveWorkspaceDir(options = {}) {
3
+ const explicitWorkspaceDir = options.workspaceDir?.trim();
4
+ const envWorkspaceDir = process.env.OPENCLAW_WORKSPACE_DIR?.trim();
5
+ return path.resolve(explicitWorkspaceDir || envWorkspaceDir || process.cwd());
6
+ }
7
+ export function resolveWorkspaceFilePath(fileName, options = {}) {
8
+ return path.join(resolveWorkspaceDir(options), fileName);
9
+ }
10
+ //# sourceMappingURL=workspace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace.js","sourceRoot":"","sources":["../src/workspace.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAM7B,MAAM,UAAU,mBAAmB,CAAC,UAAsC,EAAE;IAC1E,MAAM,oBAAoB,GAAG,OAAO,CAAC,YAAY,EAAE,IAAI,EAAE,CAAC;IAC1D,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,CAAC,sBAAsB,EAAE,IAAI,EAAE,CAAC;IAEnE,OAAO,IAAI,CAAC,OAAO,CAAC,oBAAoB,IAAI,eAAe,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;AAChF,CAAC;AAED,MAAM,UAAU,wBAAwB,CACtC,QAAgB,EAChB,UAAsC,EAAE;IAExC,OAAO,IAAI,CAAC,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;AAC3D,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,60 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import * as os from 'node:os';
4
+ import * as path from 'node:path';
5
+ import * as fs from 'node:fs/promises';
6
+ import { resolveWorkspaceDir } from './workspace.js';
7
+ test('resolveWorkspaceDir prefers explicit option over env and cwd', () => {
8
+ const previousEnv = process.env.OPENCLAW_WORKSPACE_DIR;
9
+ process.env.OPENCLAW_WORKSPACE_DIR = '/env/workspace';
10
+ try {
11
+ const resolved = resolveWorkspaceDir({ workspaceDir: '/explicit/workspace' });
12
+ assert.equal(resolved, path.resolve('/explicit/workspace'));
13
+ }
14
+ finally {
15
+ if (previousEnv === undefined) {
16
+ delete process.env.OPENCLAW_WORKSPACE_DIR;
17
+ }
18
+ else {
19
+ process.env.OPENCLAW_WORKSPACE_DIR = previousEnv;
20
+ }
21
+ }
22
+ });
23
+ test('resolveWorkspaceDir uses OPENCLAW_WORKSPACE_DIR when explicit option is absent', () => {
24
+ const previousEnv = process.env.OPENCLAW_WORKSPACE_DIR;
25
+ process.env.OPENCLAW_WORKSPACE_DIR = '/env/workspace';
26
+ try {
27
+ const resolved = resolveWorkspaceDir();
28
+ assert.equal(resolved, path.resolve('/env/workspace'));
29
+ }
30
+ finally {
31
+ if (previousEnv === undefined) {
32
+ delete process.env.OPENCLAW_WORKSPACE_DIR;
33
+ }
34
+ else {
35
+ process.env.OPENCLAW_WORKSPACE_DIR = previousEnv;
36
+ }
37
+ }
38
+ });
39
+ test('resolveWorkspaceDir falls back to process.cwd()', async () => {
40
+ const previousEnv = process.env.OPENCLAW_WORKSPACE_DIR;
41
+ delete process.env.OPENCLAW_WORKSPACE_DIR;
42
+ const previousCwd = process.cwd();
43
+ const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'sentinel-workspace-'));
44
+ try {
45
+ process.chdir(tempDir);
46
+ const resolved = resolveWorkspaceDir();
47
+ assert.equal(resolved, path.resolve(tempDir));
48
+ }
49
+ finally {
50
+ process.chdir(previousCwd);
51
+ await fs.rm(tempDir, { recursive: true, force: true });
52
+ if (previousEnv === undefined) {
53
+ delete process.env.OPENCLAW_WORKSPACE_DIR;
54
+ }
55
+ else {
56
+ process.env.OPENCLAW_WORKSPACE_DIR = previousEnv;
57
+ }
58
+ }
59
+ });
60
+ //# sourceMappingURL=workspace.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace.test.js","sourceRoot":"","sources":["../src/workspace.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,30 @@
1
+ {
2
+ "id": "sentinel.sentinel-sdk",
3
+ "name": "Sentinel SDK",
4
+ "version": "1.1.0",
5
+ "description": "Registers Sentinel memory and reliability tools for OpenClaw agents.",
6
+ "author": "OpenClaw Sentinel <support@openclawsentinel.com>",
7
+ "slots": ["tool"],
8
+ "configSchema": {
9
+ "type": "object",
10
+ "properties": {
11
+ "apiKey": {
12
+ "type": "string",
13
+ "minLength": 1,
14
+ "description": "Sentinel API key generated by openclawsentinel.com"
15
+ },
16
+ "baseUrl": {
17
+ "type": "string",
18
+ "format": "uri",
19
+ "description": "Sentinel API base URL. Defaults to https://api.openclawsentinel.com"
20
+ },
21
+ "agentId": {
22
+ "type": "string",
23
+ "minLength": 1,
24
+ "description": "Agent identifier used for memory and backup records"
25
+ }
26
+ },
27
+ "required": [],
28
+ "additionalProperties": false
29
+ }
30
+ }