@kapeta/local-cluster-service 0.57.0 → 0.58.1

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/CHANGELOG.md CHANGED
@@ -1,3 +1,17 @@
1
+ ## [0.58.1](https://github.com/kapetacom/local-cluster-service/compare/v0.58.0...v0.58.1) (2024-07-24)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * Make each page its own conversation ([#201](https://github.com/kapetacom/local-cluster-service/issues/201)) ([13173a8](https://github.com/kapetacom/local-cluster-service/commit/13173a8871178dc9c0e00075e829a86095709f8f))
7
+
8
+ # [0.58.0](https://github.com/kapetacom/local-cluster-service/compare/v0.57.0...v0.58.0) (2024-07-24)
9
+
10
+
11
+ ### Features
12
+
13
+ * add /ui/edit endpoint for Page edits ([#200](https://github.com/kapetacom/local-cluster-service/issues/200)) ([9ac81df](https://github.com/kapetacom/local-cluster-service/commit/9ac81dfaa200976a865a9d9387dc6d2ed58891b2))
14
+
1
15
  # [0.57.0](https://github.com/kapetacom/local-cluster-service/compare/v0.56.2...v0.57.0) (2024-07-22)
2
16
 
3
17
 
@@ -272,6 +272,7 @@ export interface Page {
272
272
  content: string;
273
273
  path: string;
274
274
  method: string;
275
+ conversationId: string;
275
276
  }
276
277
  export interface StormEventPage {
277
278
  type: 'PAGE';
@@ -18,9 +18,36 @@ const events_1 = require("./events");
18
18
  const event_parser_1 = require("./event-parser");
19
19
  const codegen_1 = require("./codegen");
20
20
  const assetManager_1 = require("../assetManager");
21
+ const node_uuid_1 = __importDefault(require("node-uuid"));
21
22
  const router = (0, express_promise_router_1.default)();
22
23
  router.use('/', cors_1.corsHandler);
23
24
  router.use('/', stringBody_1.stringBody);
25
+ router.post('/:handle/ui/screen', async (req, res) => {
26
+ const handle = req.params.handle;
27
+ try {
28
+ const conversationId = req.headers[stormClient_1.ConversationIdHeader.toLowerCase()];
29
+ const aiRequest = JSON.parse(req.stringBody ?? '{}');
30
+ const screenStream = await stormClient_1.stormClient.createUIPage(aiRequest, conversationId);
31
+ onRequestAborted(req, res, () => {
32
+ screenStream.abort();
33
+ });
34
+ res.set('Content-Type', 'application/x-ndjson');
35
+ res.set('Access-Control-Expose-Headers', stormClient_1.ConversationIdHeader);
36
+ res.set(stormClient_1.ConversationIdHeader, screenStream.getConversationId());
37
+ screenStream.on('data', (data) => {
38
+ console.log('Processing screen event', data);
39
+ sendEvent(res, data);
40
+ });
41
+ await waitForStormStream(screenStream);
42
+ sendDone(res);
43
+ }
44
+ catch (err) {
45
+ sendError(err, res);
46
+ if (!res.closed) {
47
+ res.end();
48
+ }
49
+ }
50
+ });
24
51
  router.post('/:handle/ui', async (req, res) => {
25
52
  const handle = req.params.handle;
26
53
  try {
@@ -47,6 +74,7 @@ router.post('/:handle/ui', async (req, res) => {
47
74
  }
48
75
  promises[screen.name] = new Promise(async (resolve, reject) => {
49
76
  try {
77
+ const innerConversationId = node_uuid_1.default.v4();
50
78
  const screenStream = await stormClient_1.stormClient.createUIPage({
51
79
  prompt: screen.requirements,
52
80
  method: screen.method,
@@ -54,8 +82,11 @@ router.post('/:handle/ui', async (req, res) => {
54
82
  description: screen.requirements,
55
83
  name: screen.name,
56
84
  filename: screen.filename,
57
- }, conversationId);
85
+ }, innerConversationId);
58
86
  screenStream.on('data', (screenData) => {
87
+ if (screenData.type === 'PAGE') {
88
+ screenData.payload.conversationId = innerConversationId;
89
+ }
59
90
  console.log('Processing screen event', screenData);
60
91
  sendEvent(res, screenData);
61
92
  });
@@ -87,6 +118,38 @@ router.post('/:handle/ui', async (req, res) => {
87
118
  }
88
119
  }
89
120
  });
121
+ router.post('/ui/edit', async (req, res) => {
122
+ try {
123
+ const conversationId = req.headers[stormClient_1.ConversationIdHeader.toLowerCase()];
124
+ const aiRequest = JSON.parse(req.stringBody ?? '{}');
125
+ const editStream = await stormClient_1.stormClient.editPages(aiRequest.prompt, conversationId);
126
+ onRequestAborted(req, res, () => {
127
+ editStream.abort();
128
+ });
129
+ res.set('Content-Type', 'application/x-ndjson');
130
+ res.set('Access-Control-Expose-Headers', stormClient_1.ConversationIdHeader);
131
+ res.set(stormClient_1.ConversationIdHeader, editStream.getConversationId());
132
+ editStream.on('data', (data) => {
133
+ try {
134
+ sendEvent(res, data);
135
+ }
136
+ catch (e) {
137
+ console.error('Failed to process event', e);
138
+ }
139
+ });
140
+ await waitForStormStream(editStream);
141
+ if (editStream.isAborted()) {
142
+ return;
143
+ }
144
+ sendDone(res);
145
+ }
146
+ catch (err) {
147
+ sendError(err, res);
148
+ if (!res.closed) {
149
+ res.end();
150
+ }
151
+ }
152
+ });
90
153
  router.post('/:handle/all', async (req, res) => {
91
154
  const handle = req.params.handle;
92
155
  try {
@@ -1,7 +1,7 @@
1
1
  import { ConversationItem, StormFileImplementationPrompt, StormStream, StormUIImplementationPrompt, StormUIListPrompt } from './stream';
2
2
  export declare const STORM_ID = "storm";
3
3
  export declare const ConversationIdHeader = "Conversation-Id";
4
- interface UIPagePrompt {
4
+ export interface UIPagePrompt {
5
5
  name: string;
6
6
  filename: string;
7
7
  prompt: string;
@@ -9,6 +9,15 @@ interface UIPagePrompt {
9
9
  method: string;
10
10
  description: string;
11
11
  }
12
+ export interface UIPageEditPrompt {
13
+ planDescription: string;
14
+ blockDescription: string;
15
+ pages: {
16
+ filename: string;
17
+ content: string;
18
+ }[];
19
+ prompt: string;
20
+ }
12
21
  declare class StormClient {
13
22
  private readonly _baseUrl;
14
23
  constructor();
@@ -18,6 +27,7 @@ declare class StormClient {
18
27
  createUIPages(prompt: string, conversationId?: string): Promise<StormStream>;
19
28
  createUIUserJourneys(prompt: string, conversationId?: string): Promise<StormStream>;
20
29
  createUIPage(prompt: UIPagePrompt, conversationId?: string): Promise<StormStream>;
30
+ editPages(prompt: UIPageEditPrompt, conversationId?: string): Promise<StormStream>;
21
31
  listScreens(prompt: StormUIListPrompt, conversationId?: string): Promise<StormStream>;
22
32
  createUIImplementation(prompt: StormUIImplementationPrompt, conversationId?: string): Promise<StormStream>;
23
33
  createServiceImplementation(prompt: StormFileImplementationPrompt, conversationId?: string): Promise<StormStream>;
@@ -98,6 +98,12 @@ class StormClient {
98
98
  conversationId,
99
99
  });
100
100
  }
101
+ editPages(prompt, conversationId) {
102
+ return this.send('/v2/ui/edit', {
103
+ prompt: JSON.stringify(prompt),
104
+ conversationId,
105
+ });
106
+ }
101
107
  listScreens(prompt, conversationId) {
102
108
  return this.send('/v2/ui/list', {
103
109
  prompt,
@@ -272,6 +272,7 @@ export interface Page {
272
272
  content: string;
273
273
  path: string;
274
274
  method: string;
275
+ conversationId: string;
275
276
  }
276
277
  export interface StormEventPage {
277
278
  type: 'PAGE';
@@ -18,9 +18,36 @@ const events_1 = require("./events");
18
18
  const event_parser_1 = require("./event-parser");
19
19
  const codegen_1 = require("./codegen");
20
20
  const assetManager_1 = require("../assetManager");
21
+ const node_uuid_1 = __importDefault(require("node-uuid"));
21
22
  const router = (0, express_promise_router_1.default)();
22
23
  router.use('/', cors_1.corsHandler);
23
24
  router.use('/', stringBody_1.stringBody);
25
+ router.post('/:handle/ui/screen', async (req, res) => {
26
+ const handle = req.params.handle;
27
+ try {
28
+ const conversationId = req.headers[stormClient_1.ConversationIdHeader.toLowerCase()];
29
+ const aiRequest = JSON.parse(req.stringBody ?? '{}');
30
+ const screenStream = await stormClient_1.stormClient.createUIPage(aiRequest, conversationId);
31
+ onRequestAborted(req, res, () => {
32
+ screenStream.abort();
33
+ });
34
+ res.set('Content-Type', 'application/x-ndjson');
35
+ res.set('Access-Control-Expose-Headers', stormClient_1.ConversationIdHeader);
36
+ res.set(stormClient_1.ConversationIdHeader, screenStream.getConversationId());
37
+ screenStream.on('data', (data) => {
38
+ console.log('Processing screen event', data);
39
+ sendEvent(res, data);
40
+ });
41
+ await waitForStormStream(screenStream);
42
+ sendDone(res);
43
+ }
44
+ catch (err) {
45
+ sendError(err, res);
46
+ if (!res.closed) {
47
+ res.end();
48
+ }
49
+ }
50
+ });
24
51
  router.post('/:handle/ui', async (req, res) => {
25
52
  const handle = req.params.handle;
26
53
  try {
@@ -47,6 +74,7 @@ router.post('/:handle/ui', async (req, res) => {
47
74
  }
48
75
  promises[screen.name] = new Promise(async (resolve, reject) => {
49
76
  try {
77
+ const innerConversationId = node_uuid_1.default.v4();
50
78
  const screenStream = await stormClient_1.stormClient.createUIPage({
51
79
  prompt: screen.requirements,
52
80
  method: screen.method,
@@ -54,8 +82,11 @@ router.post('/:handle/ui', async (req, res) => {
54
82
  description: screen.requirements,
55
83
  name: screen.name,
56
84
  filename: screen.filename,
57
- }, conversationId);
85
+ }, innerConversationId);
58
86
  screenStream.on('data', (screenData) => {
87
+ if (screenData.type === 'PAGE') {
88
+ screenData.payload.conversationId = innerConversationId;
89
+ }
59
90
  console.log('Processing screen event', screenData);
60
91
  sendEvent(res, screenData);
61
92
  });
@@ -87,6 +118,38 @@ router.post('/:handle/ui', async (req, res) => {
87
118
  }
88
119
  }
89
120
  });
121
+ router.post('/ui/edit', async (req, res) => {
122
+ try {
123
+ const conversationId = req.headers[stormClient_1.ConversationIdHeader.toLowerCase()];
124
+ const aiRequest = JSON.parse(req.stringBody ?? '{}');
125
+ const editStream = await stormClient_1.stormClient.editPages(aiRequest.prompt, conversationId);
126
+ onRequestAborted(req, res, () => {
127
+ editStream.abort();
128
+ });
129
+ res.set('Content-Type', 'application/x-ndjson');
130
+ res.set('Access-Control-Expose-Headers', stormClient_1.ConversationIdHeader);
131
+ res.set(stormClient_1.ConversationIdHeader, editStream.getConversationId());
132
+ editStream.on('data', (data) => {
133
+ try {
134
+ sendEvent(res, data);
135
+ }
136
+ catch (e) {
137
+ console.error('Failed to process event', e);
138
+ }
139
+ });
140
+ await waitForStormStream(editStream);
141
+ if (editStream.isAborted()) {
142
+ return;
143
+ }
144
+ sendDone(res);
145
+ }
146
+ catch (err) {
147
+ sendError(err, res);
148
+ if (!res.closed) {
149
+ res.end();
150
+ }
151
+ }
152
+ });
90
153
  router.post('/:handle/all', async (req, res) => {
91
154
  const handle = req.params.handle;
92
155
  try {
@@ -1,7 +1,7 @@
1
1
  import { ConversationItem, StormFileImplementationPrompt, StormStream, StormUIImplementationPrompt, StormUIListPrompt } from './stream';
2
2
  export declare const STORM_ID = "storm";
3
3
  export declare const ConversationIdHeader = "Conversation-Id";
4
- interface UIPagePrompt {
4
+ export interface UIPagePrompt {
5
5
  name: string;
6
6
  filename: string;
7
7
  prompt: string;
@@ -9,6 +9,15 @@ interface UIPagePrompt {
9
9
  method: string;
10
10
  description: string;
11
11
  }
12
+ export interface UIPageEditPrompt {
13
+ planDescription: string;
14
+ blockDescription: string;
15
+ pages: {
16
+ filename: string;
17
+ content: string;
18
+ }[];
19
+ prompt: string;
20
+ }
12
21
  declare class StormClient {
13
22
  private readonly _baseUrl;
14
23
  constructor();
@@ -18,6 +27,7 @@ declare class StormClient {
18
27
  createUIPages(prompt: string, conversationId?: string): Promise<StormStream>;
19
28
  createUIUserJourneys(prompt: string, conversationId?: string): Promise<StormStream>;
20
29
  createUIPage(prompt: UIPagePrompt, conversationId?: string): Promise<StormStream>;
30
+ editPages(prompt: UIPageEditPrompt, conversationId?: string): Promise<StormStream>;
21
31
  listScreens(prompt: StormUIListPrompt, conversationId?: string): Promise<StormStream>;
22
32
  createUIImplementation(prompt: StormUIImplementationPrompt, conversationId?: string): Promise<StormStream>;
23
33
  createServiceImplementation(prompt: StormFileImplementationPrompt, conversationId?: string): Promise<StormStream>;
@@ -98,6 +98,12 @@ class StormClient {
98
98
  conversationId,
99
99
  });
100
100
  }
101
+ editPages(prompt, conversationId) {
102
+ return this.send('/v2/ui/edit', {
103
+ prompt: JSON.stringify(prompt),
104
+ conversationId,
105
+ });
106
+ }
101
107
  listScreens(prompt, conversationId) {
102
108
  return this.send('/v2/ui/list', {
103
109
  prompt,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kapeta/local-cluster-service",
3
- "version": "0.57.0",
3
+ "version": "0.58.1",
4
4
  "description": "Manages configuration, ports and service discovery for locally running Kapeta systems",
5
5
  "type": "commonjs",
6
6
  "exports": {
@@ -322,6 +322,7 @@ export interface Page {
322
322
  content: string;
323
323
  path: string;
324
324
  method: string;
325
+ conversationId: string;
325
326
  }
326
327
 
327
328
  // Event for creating a page
@@ -12,7 +12,7 @@ import { corsHandler } from '../middleware/cors';
12
12
  import { stringBody } from '../middleware/stringBody';
13
13
  import { KapetaBodyRequest } from '../types';
14
14
  import { StormCodegenRequest, StormContextRequest, StormCreateBlockRequest, StormStream } from './stream';
15
- import { ConversationIdHeader, stormClient } from './stormClient';
15
+ import { ConversationIdHeader, stormClient, UIPagePrompt, UIPageEditPrompt } from './stormClient';
16
16
  import { StormEvent, StormEventPhaseType } from './events';
17
17
  import {
18
18
  createPhaseEndEvent,
@@ -23,12 +23,46 @@ import {
23
23
  } from './event-parser';
24
24
  import { StormCodegen } from './codegen';
25
25
  import { assetManager } from '../assetManager';
26
+ import uuid from 'node-uuid';
26
27
 
27
28
  const router = Router();
28
29
 
29
30
  router.use('/', corsHandler);
30
31
  router.use('/', stringBody);
31
32
 
33
+ router.post('/:handle/ui/screen', async (req: KapetaBodyRequest, res: Response) => {
34
+ const handle = req.params.handle as string;
35
+ try {
36
+ const conversationId = req.headers[ConversationIdHeader.toLowerCase()] as string | undefined;
37
+
38
+ const aiRequest: UIPagePrompt = JSON.parse(req.stringBody ?? '{}');
39
+
40
+ const screenStream = await stormClient.createUIPage(aiRequest, conversationId);
41
+
42
+ onRequestAborted(req, res, () => {
43
+ screenStream.abort();
44
+ });
45
+
46
+ res.set('Content-Type', 'application/x-ndjson');
47
+ res.set('Access-Control-Expose-Headers', ConversationIdHeader);
48
+ res.set(ConversationIdHeader, screenStream.getConversationId());
49
+
50
+ screenStream.on('data', (data: StormEvent) => {
51
+ console.log('Processing screen event', data);
52
+ sendEvent(res, data);
53
+ });
54
+
55
+ await waitForStormStream(screenStream);
56
+
57
+ sendDone(res);
58
+ } catch (err: any) {
59
+ sendError(err, res);
60
+ if (!res.closed) {
61
+ res.end();
62
+ }
63
+ }
64
+ });
65
+
32
66
  router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
33
67
  const handle = req.params.handle as string;
34
68
  try {
@@ -62,6 +96,7 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
62
96
  }
63
97
  promises[screen.name] = new Promise(async (resolve, reject) => {
64
98
  try {
99
+ const innerConversationId = uuid.v4();
65
100
  const screenStream = await stormClient.createUIPage(
66
101
  {
67
102
  prompt: screen.requirements,
@@ -71,9 +106,12 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
71
106
  name: screen.name,
72
107
  filename: screen.filename,
73
108
  },
74
- conversationId
109
+ innerConversationId
75
110
  );
76
111
  screenStream.on('data', (screenData: StormEvent) => {
112
+ if (screenData.type === 'PAGE') {
113
+ screenData.payload.conversationId = innerConversationId;
114
+ }
77
115
  console.log('Processing screen event', screenData);
78
116
  sendEvent(res, screenData);
79
117
  });
@@ -107,6 +145,45 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
107
145
  }
108
146
  });
109
147
 
148
+ router.post('/ui/edit', async (req: KapetaBodyRequest, res: Response) => {
149
+ try {
150
+ const conversationId = req.headers[ConversationIdHeader.toLowerCase()] as string | undefined;
151
+
152
+ const aiRequest: StormContextRequest<UIPageEditPrompt> = JSON.parse(req.stringBody ?? '{}');
153
+
154
+ const editStream = await stormClient.editPages(aiRequest.prompt, conversationId);
155
+
156
+ onRequestAborted(req, res, () => {
157
+ editStream.abort();
158
+ });
159
+
160
+ res.set('Content-Type', 'application/x-ndjson');
161
+ res.set('Access-Control-Expose-Headers', ConversationIdHeader);
162
+ res.set(ConversationIdHeader, editStream.getConversationId());
163
+
164
+ editStream.on('data', (data: StormEvent) => {
165
+ try {
166
+ sendEvent(res, data);
167
+ } catch (e) {
168
+ console.error('Failed to process event', e);
169
+ }
170
+ });
171
+
172
+ await waitForStormStream(editStream);
173
+
174
+ if (editStream.isAborted()) {
175
+ return;
176
+ }
177
+
178
+ sendDone(res);
179
+ } catch (err: any) {
180
+ sendError(err, res);
181
+ if (!res.closed) {
182
+ res.end();
183
+ }
184
+ }
185
+ });
186
+
110
187
  router.post('/:handle/all', async (req: KapetaBodyRequest, res: Response) => {
111
188
  const handle = req.params.handle as string;
112
189
 
@@ -20,7 +20,7 @@ export const STORM_ID = 'storm';
20
20
 
21
21
  export const ConversationIdHeader = 'Conversation-Id';
22
22
 
23
- interface UIPagePrompt {
23
+ export interface UIPagePrompt {
24
24
  name: string;
25
25
  filename: string;
26
26
  prompt: string;
@@ -29,6 +29,16 @@ interface UIPagePrompt {
29
29
  description: string;
30
30
  }
31
31
 
32
+ export interface UIPageEditPrompt {
33
+ planDescription: string;
34
+ blockDescription: string;
35
+ pages: {
36
+ filename: string;
37
+ content: string;
38
+ }[];
39
+ prompt: string;
40
+ }
41
+
32
42
  class StormClient {
33
43
  private readonly _baseUrl: string;
34
44
 
@@ -143,6 +153,13 @@ class StormClient {
143
153
  });
144
154
  }
145
155
 
156
+ public editPages(prompt: UIPageEditPrompt, conversationId?: string) {
157
+ return this.send('/v2/ui/edit', {
158
+ prompt: JSON.stringify(prompt),
159
+ conversationId,
160
+ });
161
+ }
162
+
146
163
  public listScreens(prompt: StormUIListPrompt, conversationId?: string) {
147
164
  return this.send('/v2/ui/list', {
148
165
  prompt,