@kapeta/local-cluster-service 0.62.2 → 0.63.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 +14 -0
- package/dist/cjs/src/storm/UIServer.d.ts +8 -0
- package/dist/cjs/src/storm/UIServer.js +20 -5
- package/dist/cjs/src/storm/events.d.ts +10 -1
- package/dist/cjs/src/storm/routes.js +10 -0
- package/dist/esm/src/storm/UIServer.d.ts +8 -0
- package/dist/esm/src/storm/UIServer.js +20 -5
- package/dist/esm/src/storm/events.d.ts +10 -1
- package/dist/esm/src/storm/routes.js +10 -0
- package/package.json +1 -1
- package/src/storm/UIServer.ts +33 -5
- package/src/storm/events.ts +19 -2
- package/src/storm/routes.ts +14 -19
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
## [0.63.1](https://github.com/kapetacom/local-cluster-service/compare/v0.63.0...v0.63.1) (2024-08-20)
|
2
|
+
|
3
|
+
|
4
|
+
### Bug Fixes
|
5
|
+
|
6
|
+
* handle port conflict and hanging response on close ([#219](https://github.com/kapetacom/local-cluster-service/issues/219)) ([e4f3b26](https://github.com/kapetacom/local-cluster-service/commit/e4f3b268fec9958aa315a186a306d2350354452b))
|
7
|
+
|
8
|
+
# [0.63.0](https://github.com/kapetacom/local-cluster-service/compare/v0.62.2...v0.63.0) (2024-08-15)
|
9
|
+
|
10
|
+
|
11
|
+
### Features
|
12
|
+
|
13
|
+
* Add reset endpoint for resetting localStorage in UI server ([#218](https://github.com/kapetacom/local-cluster-service/issues/218)) ([1dd1edc](https://github.com/kapetacom/local-cluster-service/commit/1dd1edcba982f40b853569fd408e6ed561495686))
|
14
|
+
|
1
15
|
## [0.62.2](https://github.com/kapetacom/local-cluster-service/compare/v0.62.1...v0.62.2) (2024-08-14)
|
2
16
|
|
3
17
|
|
@@ -1,4 +1,11 @@
|
|
1
|
+
/// <reference types="node" />
|
2
|
+
import { Server } from 'http';
|
1
3
|
import { StormEventPage } from './events';
|
4
|
+
declare module 'express-serve-static-core' {
|
5
|
+
interface Application {
|
6
|
+
listen(port: number, callback?: (err?: Error) => void): Server;
|
7
|
+
}
|
8
|
+
}
|
2
9
|
export declare class UIServer {
|
3
10
|
private readonly express;
|
4
11
|
private readonly systemId;
|
@@ -8,4 +15,5 @@ export declare class UIServer {
|
|
8
15
|
start(): Promise<void>;
|
9
16
|
close(): void;
|
10
17
|
resolveUrl(screenData: StormEventPage): string;
|
18
|
+
resolveUrlFromPath(path: string): string;
|
11
19
|
}
|
@@ -22,11 +22,23 @@ class UIServer {
|
|
22
22
|
}
|
23
23
|
async start() {
|
24
24
|
this.port = await clusterService_1.clusterService.getNextAvailablePort(this.port);
|
25
|
-
this.express.
|
25
|
+
this.express.get('/_reset', (req, res) => {
|
26
|
+
res.send(`
|
27
|
+
<script>
|
28
|
+
window.localStorage.clear();
|
29
|
+
window.sessionStorage.clear();
|
30
|
+
</script>`);
|
31
|
+
});
|
32
|
+
this.express.all('/*', (req, res) => {
|
26
33
|
(0, page_utils_1.readPageFromDisk)(this.systemId, req.params[0], req.method, res);
|
27
34
|
});
|
28
|
-
return new Promise((resolve) => {
|
29
|
-
this.server = this.express.listen(this.port, () => {
|
35
|
+
return new Promise((resolve, reject) => {
|
36
|
+
this.server = this.express.listen(this.port, (err) => {
|
37
|
+
if (err) {
|
38
|
+
console.error('Failed to start UI server', err);
|
39
|
+
reject(err);
|
40
|
+
return;
|
41
|
+
}
|
30
42
|
console.log(`UI Server started on port ${this.port}`);
|
31
43
|
resolve();
|
32
44
|
});
|
@@ -40,8 +52,11 @@ class UIServer {
|
|
40
52
|
}
|
41
53
|
}
|
42
54
|
resolveUrl(screenData) {
|
43
|
-
|
44
|
-
|
55
|
+
return this.resolveUrlFromPath(screenData.payload.path);
|
56
|
+
}
|
57
|
+
resolveUrlFromPath(path) {
|
58
|
+
const resolvedPath = path.startsWith('/') ? path : `/${path}`;
|
59
|
+
return `http://localhost:${this.port}${resolvedPath}`;
|
45
60
|
}
|
46
61
|
}
|
47
62
|
exports.UIServer = UIServer;
|
@@ -375,5 +375,14 @@ export interface StormEventReferenceClassification {
|
|
375
375
|
created: number;
|
376
376
|
payload: ReferenceClassification;
|
377
377
|
}
|
378
|
-
export
|
378
|
+
export interface StormEventUIStarted {
|
379
|
+
type: 'UI_SERVER_STARTED';
|
380
|
+
reason: string;
|
381
|
+
created: number;
|
382
|
+
payload: {
|
383
|
+
conversationId: string;
|
384
|
+
resetUrl: string;
|
385
|
+
};
|
386
|
+
}
|
387
|
+
export type StormEvent = StormEventCreateBlock | StormEventCreateConnection | StormEventCreatePlanProperties | StormEventInvalidResponse | StormEventPlanRetry | StormEventCreateDSL | StormEventCreateDSLResource | StormEventError | StormEventScreen | StormEventScreenCandidate | StormEventFileLogical | StormEventFileState | StormEventFileDone | StormEventFileFailed | StormEventFileChunk | StormEventDone | StormEventDefinitionChange | StormEventErrorClassifier | StormEventCodeFix | StormEventErrorDetails | StormEventBlockReady | StormEventPhases | StormEventBlockStatus | StormEventCreateDSLRetry | StormEventUserJourney | StormEventUIShell | StormEventPage | StormEventPageUrl | StormEventPromptImprove | StormEventLandingPage | StormEventReferenceClassification | StormEventApiBase | StormEventUIStarted;
|
379
388
|
export {};
|
@@ -100,6 +100,7 @@ router.delete('/:handle/ui', async (req, res) => {
|
|
100
100
|
server.close();
|
101
101
|
delete UI_SERVERS[conversationId];
|
102
102
|
}
|
103
|
+
res.status(200).json({ status: 'ok' });
|
103
104
|
});
|
104
105
|
router.post('/:handle/ui/iterative', async (req, res) => {
|
105
106
|
const handle = req.params.handle;
|
@@ -247,6 +248,15 @@ router.post('/:handle/ui', async (req, res) => {
|
|
247
248
|
await waitForStormStream(shellsStream);
|
248
249
|
UI_SERVERS[outerConversationId] = new UIServer_1.UIServer(outerConversationId);
|
249
250
|
await UI_SERVERS[outerConversationId].start();
|
251
|
+
sendEvent(res, {
|
252
|
+
type: 'UI_SERVER_STARTED',
|
253
|
+
reason: '',
|
254
|
+
payload: {
|
255
|
+
conversationId: outerConversationId,
|
256
|
+
resetUrl: UI_SERVERS[outerConversationId].resolveUrlFromPath('/_reset'),
|
257
|
+
},
|
258
|
+
created: Date.now(),
|
259
|
+
});
|
250
260
|
// Get the pages (5 at a time)
|
251
261
|
const pagePromises = [];
|
252
262
|
onRequestAborted(req, res, () => {
|
@@ -1,4 +1,11 @@
|
|
1
|
+
/// <reference types="node" />
|
2
|
+
import { Server } from 'http';
|
1
3
|
import { StormEventPage } from './events';
|
4
|
+
declare module 'express-serve-static-core' {
|
5
|
+
interface Application {
|
6
|
+
listen(port: number, callback?: (err?: Error) => void): Server;
|
7
|
+
}
|
8
|
+
}
|
2
9
|
export declare class UIServer {
|
3
10
|
private readonly express;
|
4
11
|
private readonly systemId;
|
@@ -8,4 +15,5 @@ export declare class UIServer {
|
|
8
15
|
start(): Promise<void>;
|
9
16
|
close(): void;
|
10
17
|
resolveUrl(screenData: StormEventPage): string;
|
18
|
+
resolveUrlFromPath(path: string): string;
|
11
19
|
}
|
@@ -22,11 +22,23 @@ class UIServer {
|
|
22
22
|
}
|
23
23
|
async start() {
|
24
24
|
this.port = await clusterService_1.clusterService.getNextAvailablePort(this.port);
|
25
|
-
this.express.
|
25
|
+
this.express.get('/_reset', (req, res) => {
|
26
|
+
res.send(`
|
27
|
+
<script>
|
28
|
+
window.localStorage.clear();
|
29
|
+
window.sessionStorage.clear();
|
30
|
+
</script>`);
|
31
|
+
});
|
32
|
+
this.express.all('/*', (req, res) => {
|
26
33
|
(0, page_utils_1.readPageFromDisk)(this.systemId, req.params[0], req.method, res);
|
27
34
|
});
|
28
|
-
return new Promise((resolve) => {
|
29
|
-
this.server = this.express.listen(this.port, () => {
|
35
|
+
return new Promise((resolve, reject) => {
|
36
|
+
this.server = this.express.listen(this.port, (err) => {
|
37
|
+
if (err) {
|
38
|
+
console.error('Failed to start UI server', err);
|
39
|
+
reject(err);
|
40
|
+
return;
|
41
|
+
}
|
30
42
|
console.log(`UI Server started on port ${this.port}`);
|
31
43
|
resolve();
|
32
44
|
});
|
@@ -40,8 +52,11 @@ class UIServer {
|
|
40
52
|
}
|
41
53
|
}
|
42
54
|
resolveUrl(screenData) {
|
43
|
-
|
44
|
-
|
55
|
+
return this.resolveUrlFromPath(screenData.payload.path);
|
56
|
+
}
|
57
|
+
resolveUrlFromPath(path) {
|
58
|
+
const resolvedPath = path.startsWith('/') ? path : `/${path}`;
|
59
|
+
return `http://localhost:${this.port}${resolvedPath}`;
|
45
60
|
}
|
46
61
|
}
|
47
62
|
exports.UIServer = UIServer;
|
@@ -375,5 +375,14 @@ export interface StormEventReferenceClassification {
|
|
375
375
|
created: number;
|
376
376
|
payload: ReferenceClassification;
|
377
377
|
}
|
378
|
-
export
|
378
|
+
export interface StormEventUIStarted {
|
379
|
+
type: 'UI_SERVER_STARTED';
|
380
|
+
reason: string;
|
381
|
+
created: number;
|
382
|
+
payload: {
|
383
|
+
conversationId: string;
|
384
|
+
resetUrl: string;
|
385
|
+
};
|
386
|
+
}
|
387
|
+
export type StormEvent = StormEventCreateBlock | StormEventCreateConnection | StormEventCreatePlanProperties | StormEventInvalidResponse | StormEventPlanRetry | StormEventCreateDSL | StormEventCreateDSLResource | StormEventError | StormEventScreen | StormEventScreenCandidate | StormEventFileLogical | StormEventFileState | StormEventFileDone | StormEventFileFailed | StormEventFileChunk | StormEventDone | StormEventDefinitionChange | StormEventErrorClassifier | StormEventCodeFix | StormEventErrorDetails | StormEventBlockReady | StormEventPhases | StormEventBlockStatus | StormEventCreateDSLRetry | StormEventUserJourney | StormEventUIShell | StormEventPage | StormEventPageUrl | StormEventPromptImprove | StormEventLandingPage | StormEventReferenceClassification | StormEventApiBase | StormEventUIStarted;
|
379
388
|
export {};
|
@@ -100,6 +100,7 @@ router.delete('/:handle/ui', async (req, res) => {
|
|
100
100
|
server.close();
|
101
101
|
delete UI_SERVERS[conversationId];
|
102
102
|
}
|
103
|
+
res.status(200).json({ status: 'ok' });
|
103
104
|
});
|
104
105
|
router.post('/:handle/ui/iterative', async (req, res) => {
|
105
106
|
const handle = req.params.handle;
|
@@ -247,6 +248,15 @@ router.post('/:handle/ui', async (req, res) => {
|
|
247
248
|
await waitForStormStream(shellsStream);
|
248
249
|
UI_SERVERS[outerConversationId] = new UIServer_1.UIServer(outerConversationId);
|
249
250
|
await UI_SERVERS[outerConversationId].start();
|
251
|
+
sendEvent(res, {
|
252
|
+
type: 'UI_SERVER_STARTED',
|
253
|
+
reason: '',
|
254
|
+
payload: {
|
255
|
+
conversationId: outerConversationId,
|
256
|
+
resetUrl: UI_SERVERS[outerConversationId].resolveUrlFromPath('/_reset'),
|
257
|
+
},
|
258
|
+
created: Date.now(),
|
259
|
+
});
|
250
260
|
// Get the pages (5 at a time)
|
251
261
|
const pagePromises = [];
|
252
262
|
onRequestAborted(req, res, () => {
|
package/package.json
CHANGED
package/src/storm/UIServer.ts
CHANGED
@@ -8,6 +8,15 @@ import { clusterService } from '../clusterService';
|
|
8
8
|
import { Server } from 'http';
|
9
9
|
import { StormEventPage } from './events';
|
10
10
|
|
11
|
+
declare module 'express-serve-static-core' {
|
12
|
+
interface Application {
|
13
|
+
// Adds error callback support
|
14
|
+
// From the docs:
|
15
|
+
// All the forms of Node’s http.Server.listen() method are in fact actually supported.
|
16
|
+
listen(port: number, callback?: (err?: Error) => void): Server;
|
17
|
+
}
|
18
|
+
}
|
19
|
+
|
11
20
|
export class UIServer {
|
12
21
|
private readonly express: Express;
|
13
22
|
private readonly systemId: string;
|
@@ -23,12 +32,27 @@ export class UIServer {
|
|
23
32
|
public async start() {
|
24
33
|
this.port = await clusterService.getNextAvailablePort(this.port);
|
25
34
|
|
26
|
-
this.express.
|
35
|
+
this.express.get('/_reset', (req: Request, res: Response) => {
|
36
|
+
res.send(
|
37
|
+
`
|
38
|
+
<script>
|
39
|
+
window.localStorage.clear();
|
40
|
+
window.sessionStorage.clear();
|
41
|
+
</script>`
|
42
|
+
);
|
43
|
+
});
|
44
|
+
|
45
|
+
this.express.all('/*', (req: Request, res: Response) => {
|
27
46
|
readPageFromDisk(this.systemId, req.params[0], req.method, res);
|
28
47
|
});
|
29
48
|
|
30
|
-
return new Promise<void>((resolve) => {
|
31
|
-
this.server = this.express.listen(this.port, () => {
|
49
|
+
return new Promise<void>((resolve, reject) => {
|
50
|
+
this.server = this.express.listen(this.port, (err) => {
|
51
|
+
if (err) {
|
52
|
+
console.error('Failed to start UI server', err);
|
53
|
+
reject(err);
|
54
|
+
return;
|
55
|
+
}
|
32
56
|
console.log(`UI Server started on port ${this.port}`);
|
33
57
|
resolve();
|
34
58
|
});
|
@@ -44,7 +68,11 @@ export class UIServer {
|
|
44
68
|
}
|
45
69
|
|
46
70
|
resolveUrl(screenData: StormEventPage) {
|
47
|
-
|
48
|
-
|
71
|
+
return this.resolveUrlFromPath(screenData.payload.path);
|
72
|
+
}
|
73
|
+
|
74
|
+
resolveUrlFromPath(path: string) {
|
75
|
+
const resolvedPath = path.startsWith('/') ? path : `/${path}`;
|
76
|
+
return `http://localhost:${this.port}${resolvedPath}`;
|
49
77
|
}
|
50
78
|
}
|
package/src/storm/events.ts
CHANGED
@@ -264,7 +264,13 @@ export interface StormEventFileChunk extends StormEventFileBase {
|
|
264
264
|
}
|
265
265
|
|
266
266
|
export interface StormEventApiBase {
|
267
|
-
type:
|
267
|
+
type:
|
268
|
+
| 'API_STREAM_CHUNK'
|
269
|
+
| 'API_STREAM_DONE'
|
270
|
+
| 'API_STREAM_FAILED'
|
271
|
+
| 'API_STREAM_STATE'
|
272
|
+
| 'API_STREAM_START'
|
273
|
+
| 'API_STREAM_CHUNK_RESET';
|
268
274
|
payload: StormEventFileBasePayload;
|
269
275
|
}
|
270
276
|
|
@@ -445,6 +451,16 @@ export interface StormEventReferenceClassification {
|
|
445
451
|
payload: ReferenceClassification;
|
446
452
|
}
|
447
453
|
|
454
|
+
export interface StormEventUIStarted {
|
455
|
+
type: 'UI_SERVER_STARTED';
|
456
|
+
reason: string;
|
457
|
+
created: number;
|
458
|
+
payload: {
|
459
|
+
conversationId: string;
|
460
|
+
resetUrl: string;
|
461
|
+
};
|
462
|
+
}
|
463
|
+
|
448
464
|
export type StormEvent =
|
449
465
|
| StormEventCreateBlock
|
450
466
|
| StormEventCreateConnection
|
@@ -477,4 +493,5 @@ export type StormEvent =
|
|
477
493
|
| StormEventPromptImprove
|
478
494
|
| StormEventLandingPage
|
479
495
|
| StormEventReferenceClassification
|
480
|
-
| StormEventApiBase
|
496
|
+
| StormEventApiBase
|
497
|
+
| StormEventUIStarted;
|
package/src/storm/routes.ts
CHANGED
@@ -13,15 +13,8 @@ import { stringBody } from '../middleware/stringBody';
|
|
13
13
|
import { KapetaBodyRequest } from '../types';
|
14
14
|
import { StormCodegenRequest, StormContextRequest, StormCreateBlockRequest, StormStream } from './stream';
|
15
15
|
|
16
|
-
import {
|
17
|
-
|
18
|
-
stormClient,
|
19
|
-
UIPagePrompt,
|
20
|
-
UIPageEditPrompt,
|
21
|
-
UIPageEditRequest,
|
22
|
-
UIPageSamplePrompt,
|
23
|
-
} from './stormClient';
|
24
|
-
import { Page, StormEvent, StormEventPage, StormEventPhaseType, UIShell, UserJourneyScreen } from './events';
|
16
|
+
import { ConversationIdHeader, stormClient, UIPagePrompt, UIPageEditPrompt, UIPageEditRequest } from './stormClient';
|
17
|
+
import { Page, StormEvent, StormEventPage, StormEventPhaseType, UserJourneyScreen } from './events';
|
25
18
|
|
26
19
|
import {
|
27
20
|
createPhaseEndEvent,
|
@@ -33,18 +26,9 @@ import {
|
|
33
26
|
import { StormCodegen } from './codegen';
|
34
27
|
import { assetManager } from '../assetManager';
|
35
28
|
import uuid from 'node-uuid';
|
36
|
-
import {
|
37
|
-
import {
|
38
|
-
readConversationFromFile,
|
39
|
-
readPageFromDisk,
|
40
|
-
readPageFromDiskAsString,
|
41
|
-
SystemIdHeader,
|
42
|
-
writeConversationToFile,
|
43
|
-
writePageToDisk,
|
44
|
-
} from './page-utils';
|
29
|
+
import { readPageFromDisk, readPageFromDiskAsString, SystemIdHeader, writePageToDisk } from './page-utils';
|
45
30
|
import { UIServer } from './UIServer';
|
46
31
|
import { PageQueue } from './PageGenerator';
|
47
|
-
import FSExtra from 'fs-extra';
|
48
32
|
|
49
33
|
const UI_SERVERS: { [key: string]: UIServer } = {};
|
50
34
|
const router = Router();
|
@@ -140,6 +124,7 @@ router.delete('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
|
|
140
124
|
server.close();
|
141
125
|
delete UI_SERVERS[conversationId];
|
142
126
|
}
|
127
|
+
res.status(200).json({ status: 'ok' });
|
143
128
|
});
|
144
129
|
|
145
130
|
router.post('/:handle/ui/iterative', async (req: KapetaBodyRequest, res: Response) => {
|
@@ -325,6 +310,16 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
|
|
325
310
|
UI_SERVERS[outerConversationId] = new UIServer(outerConversationId);
|
326
311
|
await UI_SERVERS[outerConversationId].start();
|
327
312
|
|
313
|
+
sendEvent(res, {
|
314
|
+
type: 'UI_SERVER_STARTED',
|
315
|
+
reason: '',
|
316
|
+
payload: {
|
317
|
+
conversationId: outerConversationId,
|
318
|
+
resetUrl: UI_SERVERS[outerConversationId].resolveUrlFromPath('/_reset'),
|
319
|
+
},
|
320
|
+
created: Date.now(),
|
321
|
+
});
|
322
|
+
|
328
323
|
// Get the pages (5 at a time)
|
329
324
|
|
330
325
|
const pagePromises: Promise<void>[] = [];
|