@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 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
+ });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "openclaw-storyblok",
3
3
  "name": "Storyblok Integration",
4
- "version": "0.1.13",
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.13",
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": "./dist/index.js",
6
+ "main": "./index.ts",
7
7
  "files": [
8
- "dist/",
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
+ }