@kvasar/openclaw-storyblok-plugin 0.1.13 → 0.1.14
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/index.ts +247 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +4 -3
- package/src/__tests__/README.md +115 -0
- package/src/__tests__/openclaw.plugin.json +30 -0
- package/src/__tests__/package-lock.json +1555 -0
- package/src/__tests__/package.json +22 -0
- package/src/__tests__/storyblok.test.ts +55 -0
- package/src/__tests__/tsconfig.json +19 -0
- package/src/client.ts +176 -0
- package/src/config.ts +35 -0
- package/dist/index.d.ts +0 -11
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -192
- package/dist/index.js.map +0 -1
- package/dist/src/client.d.ts +0 -61
- package/dist/src/client.d.ts.map +0 -1
- package/dist/src/client.js +0 -155
- package/dist/src/client.js.map +0 -1
- package/dist/src/config.d.ts +0 -9
- package/dist/src/config.d.ts.map +0 -1
- package/dist/src/config.js +0 -32
- package/dist/src/config.js.map +0 -1
package/index.ts
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* index.ts — OpenClaw Storyblok Plugin
|
|
3
|
+
*
|
|
4
|
+
* Tools for interacting with Storyblok Management API and Delivery API.
|
|
5
|
+
* Authentication:
|
|
6
|
+
* - managementToken required for write operations
|
|
7
|
+
* - previewToken optional for read operations
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { Type } from "@sinclair/typebox";
|
|
11
|
+
import { definePluginEntry } from "openclaw/plugin-sdk/plugin-entry";
|
|
12
|
+
|
|
13
|
+
import { StoryblokClient, type StoryblokConfig } from "./src/client.js";
|
|
14
|
+
import { redactTokens } from "./src/config.js";
|
|
15
|
+
|
|
16
|
+
interface StoryblokPluginConfig {
|
|
17
|
+
baseUrl?: string;
|
|
18
|
+
spaceId?: string;
|
|
19
|
+
managementToken?: string;
|
|
20
|
+
previewToken?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface OpenClawPluginApi {
|
|
24
|
+
pluginConfig?: unknown;
|
|
25
|
+
config?: unknown;
|
|
26
|
+
registerTool(tool: {
|
|
27
|
+
name: string;
|
|
28
|
+
description: string;
|
|
29
|
+
parameters: unknown;
|
|
30
|
+
execute: (_id?: string, params?: any) => Promise<unknown> | unknown;
|
|
31
|
+
}): void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function ok(data: unknown) {
|
|
35
|
+
return {
|
|
36
|
+
content: [
|
|
37
|
+
{
|
|
38
|
+
type: "text",
|
|
39
|
+
text: JSON.stringify(data, null, 2),
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
metadata: { storyblok: true },
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function fail(error: unknown) {
|
|
47
|
+
return {
|
|
48
|
+
content: [
|
|
49
|
+
{
|
|
50
|
+
type: "text",
|
|
51
|
+
text: `❌ Storyblok: ${String(error)}`,
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
isError: true,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export default definePluginEntry({
|
|
59
|
+
id: "openclaw-storyblok",
|
|
60
|
+
name: "Storyblok Integration",
|
|
61
|
+
description:
|
|
62
|
+
"Interact with Storyblok CMS: manage stories, components, and space information.",
|
|
63
|
+
|
|
64
|
+
register(api: OpenClawPluginApi) {
|
|
65
|
+
function resolveClient() {
|
|
66
|
+
const rawCfg = (api.pluginConfig ?? api.config ?? {}) as StoryblokPluginConfig;
|
|
67
|
+
|
|
68
|
+
const cfg: StoryblokPluginConfig = {
|
|
69
|
+
baseUrl: rawCfg.baseUrl ?? "https://api.storyblok.com",
|
|
70
|
+
spaceId: rawCfg.spaceId,
|
|
71
|
+
managementToken: rawCfg.managementToken,
|
|
72
|
+
previewToken: rawCfg.previewToken,
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const clientCfg: StoryblokConfig = {
|
|
76
|
+
baseUrl: cfg.baseUrl!,
|
|
77
|
+
spaceId: cfg.spaceId ?? "",
|
|
78
|
+
managementToken: cfg.managementToken ?? "",
|
|
79
|
+
previewToken: cfg.previewToken,
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
client: new StoryblokClient(clientCfg),
|
|
84
|
+
cfg,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function executeSafely<T>(
|
|
89
|
+
handler: (client: StoryblokClient, params?: T) => Promise<unknown>,
|
|
90
|
+
params?: T
|
|
91
|
+
) {
|
|
92
|
+
const { client, cfg } = resolveClient();
|
|
93
|
+
|
|
94
|
+
try {
|
|
95
|
+
return ok(await handler(client, params));
|
|
96
|
+
} catch (error) {
|
|
97
|
+
return fail(
|
|
98
|
+
redactTokens(String((error as any)?.message ?? error), cfg)
|
|
99
|
+
);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
api.registerTool({
|
|
104
|
+
name: "storyblok_get_space",
|
|
105
|
+
description: "Retrieve Storyblok space details.",
|
|
106
|
+
parameters: Type.Object({}),
|
|
107
|
+
execute() {
|
|
108
|
+
return executeSafely((client) => client.getSpace());
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
api.registerTool({
|
|
113
|
+
name: "storyblok_get_story",
|
|
114
|
+
description: "Retrieve a story by ID, UUID, or slug.",
|
|
115
|
+
parameters: Type.Object({
|
|
116
|
+
identifier: Type.String({
|
|
117
|
+
description: "Story ID, UUID, or slug",
|
|
118
|
+
}),
|
|
119
|
+
version: Type.Optional(
|
|
120
|
+
Type.String({
|
|
121
|
+
description: "'draft' or 'published'",
|
|
122
|
+
})
|
|
123
|
+
),
|
|
124
|
+
language: Type.Optional(Type.String()),
|
|
125
|
+
svg_render: Type.Optional(Type.Boolean()),
|
|
126
|
+
}),
|
|
127
|
+
execute(_id, params) {
|
|
128
|
+
return executeSafely((client, p: any) =>
|
|
129
|
+
client.getStory(p.identifier, {
|
|
130
|
+
version: p.version,
|
|
131
|
+
language: p.language,
|
|
132
|
+
svg_render: p.svg_render,
|
|
133
|
+
}),
|
|
134
|
+
params
|
|
135
|
+
);
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
api.registerTool({
|
|
140
|
+
name: "storyblok_list_stories",
|
|
141
|
+
description: "List stories with optional filters.",
|
|
142
|
+
parameters: Type.Object({
|
|
143
|
+
folder_id: Type.Optional(Type.Number()),
|
|
144
|
+
parent_id: Type.Optional(Type.Number()),
|
|
145
|
+
status: Type.Optional(Type.String()),
|
|
146
|
+
tag: Type.Optional(Type.String()),
|
|
147
|
+
per_page: Type.Optional(Type.Number()),
|
|
148
|
+
page: Type.Optional(Type.Number()),
|
|
149
|
+
sort_by: Type.Optional(Type.String()),
|
|
150
|
+
direction: Type.Optional(Type.String()),
|
|
151
|
+
}),
|
|
152
|
+
execute(_id, params) {
|
|
153
|
+
return executeSafely((client, p) => client.listStories(p), params);
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
api.registerTool({
|
|
158
|
+
name: "storyblok_create_story",
|
|
159
|
+
description: "Create a new story.",
|
|
160
|
+
parameters: Type.Object({
|
|
161
|
+
title: Type.String(),
|
|
162
|
+
slug: Type.Optional(Type.String()),
|
|
163
|
+
folder_id: Type.Optional(Type.Number()),
|
|
164
|
+
parent_id: Type.Optional(Type.Number()),
|
|
165
|
+
content: Type.Optional(Type.Record(Type.String(), Type.Any())),
|
|
166
|
+
tags: Type.Optional(Type.Array(Type.String())),
|
|
167
|
+
is_folder: Type.Optional(Type.Boolean()),
|
|
168
|
+
language: Type.Optional(Type.String()),
|
|
169
|
+
}),
|
|
170
|
+
execute(_id, params) {
|
|
171
|
+
return executeSafely((client, p) => client.createStory(p), params);
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
api.registerTool({
|
|
176
|
+
name: "storyblok_update_story",
|
|
177
|
+
description: "Update an existing story.",
|
|
178
|
+
parameters: Type.Object({
|
|
179
|
+
story_id: Type.String(),
|
|
180
|
+
title: Type.Optional(Type.String()),
|
|
181
|
+
slug: Type.Optional(Type.String()),
|
|
182
|
+
content: Type.Optional(Type.Record(Type.String(), Type.Any())),
|
|
183
|
+
parent_id: Type.Optional(Type.Number()),
|
|
184
|
+
tags: Type.Optional(Type.Array(Type.String())),
|
|
185
|
+
language: Type.Optional(Type.String()),
|
|
186
|
+
version: Type.Optional(Type.String()),
|
|
187
|
+
}),
|
|
188
|
+
execute(_id, params) {
|
|
189
|
+
return executeSafely((client, p: any) => {
|
|
190
|
+
const { story_id, ...update } = p;
|
|
191
|
+
return client.updateStory(story_id, update);
|
|
192
|
+
}, params);
|
|
193
|
+
},
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
api.registerTool({
|
|
197
|
+
name: "storyblok_publish_story",
|
|
198
|
+
description: "Publish a story.",
|
|
199
|
+
parameters: Type.Object({
|
|
200
|
+
story_id: Type.String(),
|
|
201
|
+
version: Type.Optional(Type.String()),
|
|
202
|
+
language: Type.Optional(Type.String()),
|
|
203
|
+
publish_notes: Type.Optional(Type.String()),
|
|
204
|
+
}),
|
|
205
|
+
execute(_id, params) {
|
|
206
|
+
return executeSafely((client, p: any) =>
|
|
207
|
+
client.publishStory(p.story_id, {
|
|
208
|
+
version: p.version,
|
|
209
|
+
language: p.language,
|
|
210
|
+
publish_notes: p.publish_notes,
|
|
211
|
+
}),
|
|
212
|
+
params
|
|
213
|
+
);
|
|
214
|
+
},
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
api.registerTool({
|
|
218
|
+
name: "storyblok_unpublish_story",
|
|
219
|
+
description: "Unpublish a story.",
|
|
220
|
+
parameters: Type.Object({
|
|
221
|
+
story_id: Type.String(),
|
|
222
|
+
language: Type.Optional(Type.String()),
|
|
223
|
+
}),
|
|
224
|
+
execute(_id, params) {
|
|
225
|
+
return executeSafely((client, p: any) =>
|
|
226
|
+
client.unpublishStory(p.story_id, p.language),
|
|
227
|
+
params
|
|
228
|
+
);
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
api.registerTool({
|
|
233
|
+
name: "storyblok_get_components",
|
|
234
|
+
description: "Get component schemas.",
|
|
235
|
+
parameters: Type.Object({
|
|
236
|
+
version: Type.Optional(Type.String()),
|
|
237
|
+
language: Type.Optional(Type.String()),
|
|
238
|
+
}),
|
|
239
|
+
execute(_id, params) {
|
|
240
|
+
return executeSafely((client, p: any) =>
|
|
241
|
+
client.getComponents(p.version, p.language),
|
|
242
|
+
params
|
|
243
|
+
);
|
|
244
|
+
},
|
|
245
|
+
});
|
|
246
|
+
},
|
|
247
|
+
});
|
package/openclaw.plugin.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": "openclaw-storyblok",
|
|
3
3
|
"name": "Storyblok Integration",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.14",
|
|
5
5
|
"description": "Provides tools to interact with Storyblok CMS via Management API and Delivery API. Supports stories, components, and space management.",
|
|
6
6
|
"activation": {
|
|
7
7
|
"onStartup": false
|
package/package.json
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kvasar/openclaw-storyblok-plugin",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.14",
|
|
4
4
|
"description": "OpenClaw plugin — interact with Storyblok CMS via Management API and Delivery API",
|
|
5
5
|
"type": "module",
|
|
6
|
-
"main": "./
|
|
6
|
+
"main": "./index.ts",
|
|
7
7
|
"files": [
|
|
8
|
-
"
|
|
8
|
+
"index.ts",
|
|
9
|
+
"src/",
|
|
9
10
|
"openclaw.plugin.json",
|
|
10
11
|
"skills/",
|
|
11
12
|
"tsconfig.json",
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Storyblok Plugin (thin client) — OpenClaw
|
|
2
|
+
|
|
3
|
+
Thin‑client OpenClaw plugin that calls an external REST service to generate Storyblok pages via AI.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
### 1. Clone the plugin repository
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
git clone git@bitbucket.org:scaledagilequa/openclaw-storyblok-plugin.git
|
|
11
|
+
cd openclaw-storyblok-plugin
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
### 2. Install dependencies
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
npm install
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
### 3. Build the plugin
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
npm run build
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
### 4. Configure OpenClaw
|
|
27
|
+
|
|
28
|
+
Add the plugin to your OpenClaw configuration (`~/.openclaw/config.yaml` or equivalent):
|
|
29
|
+
|
|
30
|
+
```yaml
|
|
31
|
+
plugins:
|
|
32
|
+
- id: storyblok
|
|
33
|
+
config:
|
|
34
|
+
serviceUrl: "http://localhost:8000" # base URL of your Storyblok AI service
|
|
35
|
+
apiKey: "optional-key" # optional API key for the service
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### 5. Restart OpenClaw
|
|
39
|
+
|
|
40
|
+
Restart OpenClaw (or reload plugins) to activate the new plugin.
|
|
41
|
+
|
|
42
|
+
## Plugin Configuration
|
|
43
|
+
|
|
44
|
+
| Key | Type | Required | Default | Description |
|
|
45
|
+
|-----|------|----------|---------|-------------|
|
|
46
|
+
| `serviceUrl` | string | Yes | `http://localhost:8000` | Base URL of the Storyblok AI service (must be reachable from OpenClaw). |
|
|
47
|
+
| `apiKey` | string | No | – | Optional API key if the service requires authentication. |
|
|
48
|
+
|
|
49
|
+
## Tool
|
|
50
|
+
|
|
51
|
+
### `storyblok_generate_page`
|
|
52
|
+
|
|
53
|
+
Generates a Storyblok page by sending a prompt to the external AI service.
|
|
54
|
+
|
|
55
|
+
**Parameters:**
|
|
56
|
+
|
|
57
|
+
| Parameter | Type | Description |
|
|
58
|
+
|-----------|------|-------------|
|
|
59
|
+
| `prompt` | string | Description of the page you want to generate. |
|
|
60
|
+
|
|
61
|
+
**Example usage (in OpenClaw chat):**
|
|
62
|
+
|
|
63
|
+
```
|
|
64
|
+
@assistant storyblok_generate_page prompt="Landing page for an AI consulting company"
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
**Response:**
|
|
68
|
+
|
|
69
|
+
Returns the JSON response from the external service (typically containing the created story details).
|
|
70
|
+
|
|
71
|
+
## Development
|
|
72
|
+
|
|
73
|
+
### Prerequisites
|
|
74
|
+
|
|
75
|
+
- Node.js 18+
|
|
76
|
+
- npm or yarn
|
|
77
|
+
|
|
78
|
+
### Building
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
npm run build
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Testing
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
npm test
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Type Checking
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
npm run typecheck
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Architecture
|
|
97
|
+
|
|
98
|
+
```
|
|
99
|
+
openclaw-storyblok-plugin/
|
|
100
|
+
├── src/
|
|
101
|
+
│ └── index.ts # Plugin entry point (registers the tool)
|
|
102
|
+
├── openclaw.plugin.json # Plugin manifest
|
|
103
|
+
├── package.json # Dependencies and scripts
|
|
104
|
+
└── tsconfig.json # TypeScript configuration
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
The plugin uses `node-fetch` to call the external REST service. It is designed to be a thin client—all business logic resides in the separate Storyblok AI service.
|
|
108
|
+
|
|
109
|
+
## Related Projects
|
|
110
|
+
|
|
111
|
+
- **Storyblok AI Service**: Python FastAPI service that does the actual LLM generation and Storyblok integration. Available at `git@bitbucket.org:scaledagilequa/openclaw-storyblok.git`.
|
|
112
|
+
|
|
113
|
+
## License
|
|
114
|
+
|
|
115
|
+
MIT
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://openclaw.ai/schema/openclaw.plugin.v1.json",
|
|
3
|
+
"id": "storyblok",
|
|
4
|
+
"name": "Storyblok",
|
|
5
|
+
"description": "Thin‑client plugin for Storyblok AI page generation (calls external REST service)",
|
|
6
|
+
"version": "0.1.0",
|
|
7
|
+
"config": {
|
|
8
|
+
"serviceUrl": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "Base URL of the Storyblok AI service (e.g., http://localhost:8000)",
|
|
11
|
+
"required": true,
|
|
12
|
+
"default": "http://localhost:8000"
|
|
13
|
+
},
|
|
14
|
+
"apiKey": {
|
|
15
|
+
"type": "string",
|
|
16
|
+
"description": "Optional API key for the service (if required)",
|
|
17
|
+
"secret": true,
|
|
18
|
+
"required": false
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"tools": [
|
|
22
|
+
{
|
|
23
|
+
"name": "storyblok_generate_page",
|
|
24
|
+
"description": "Generate a Storyblok page via AI using the external service",
|
|
25
|
+
"parameters": {
|
|
26
|
+
"prompt": { "type": "string", "description": "Description of the page you want to generate" }
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
}
|