@kapeta/local-cluster-service 0.76.5 → 0.77.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/PageGenerator.d.ts +5 -3
- package/dist/cjs/src/storm/PageGenerator.js +27 -30
- package/dist/cjs/src/storm/routes.js +8 -1
- package/dist/cjs/src/storm/stormClient.d.ts +14 -0
- package/dist/cjs/src/storm/stormClient.js +8 -1
- package/dist/esm/src/storm/PageGenerator.d.ts +5 -3
- package/dist/esm/src/storm/PageGenerator.js +27 -30
- package/dist/esm/src/storm/routes.js +8 -1
- package/dist/esm/src/storm/stormClient.d.ts +14 -0
- package/dist/esm/src/storm/stormClient.js +8 -1
- package/package.json +2 -1
- package/src/storm/PageGenerator.ts +33 -34
- package/src/storm/routes.ts +8 -1
- package/src/storm/stormClient.ts +27 -1
package/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
## [0.77.1](https://github.com/kapetacom/local-cluster-service/compare/v0.77.0...v0.77.1) (2024-10-04)
|
2
|
+
|
3
|
+
|
4
|
+
### Bug Fixes
|
5
|
+
|
6
|
+
* disable timeouts for createSimpleBackend call ([#270](https://github.com/kapetacom/local-cluster-service/issues/270)) ([9a6ba9c](https://github.com/kapetacom/local-cluster-service/commit/9a6ba9cb09ff99a3e31e93e33c027232ddeff8fb))
|
7
|
+
|
8
|
+
# [0.77.0](https://github.com/kapetacom/local-cluster-service/compare/v0.76.5...v0.77.0) (2024-10-04)
|
9
|
+
|
10
|
+
|
11
|
+
### Features
|
12
|
+
|
13
|
+
* Enable page agent to distinguish beween global and local edits ([1e24653](https://github.com/kapetacom/local-cluster-service/commit/1e246536272f9a141acf2233eb96fdec2a30c709))
|
14
|
+
|
1
15
|
## [0.76.5](https://github.com/kapetacom/local-cluster-service/compare/v0.76.4...v0.76.5) (2024-10-03)
|
2
16
|
|
3
17
|
|
@@ -41,9 +41,11 @@ export declare class PageQueue extends EventEmitter {
|
|
41
41
|
addUiShell(uiShell: UIShell): void;
|
42
42
|
setUiTheme(theme: string): void;
|
43
43
|
private hasPrompt;
|
44
|
-
addPrompt(initialPrompt: InitialPrompt, conversationId?: string, overwrite?: boolean): Promise<void>;
|
45
|
-
|
46
|
-
|
44
|
+
addPrompt(initialPrompt: InitialPrompt, conversationId?: string, overwrite?: boolean, globalEdit?: boolean): Promise<void>;
|
45
|
+
/**
|
46
|
+
* Get the existing pages
|
47
|
+
*/
|
48
|
+
private getExistingPages;
|
47
49
|
private processPageEventWithReferences;
|
48
50
|
cancel(): void;
|
49
51
|
wait(): Promise<void>;
|
@@ -87,7 +87,9 @@ class PageQueue extends node_events_1.EventEmitter {
|
|
87
87
|
}
|
88
88
|
return false;
|
89
89
|
}
|
90
|
-
addPrompt(initialPrompt, conversationId = node_uuid_1.default.v4(), overwrite = false
|
90
|
+
addPrompt(initialPrompt, conversationId = node_uuid_1.default.v4(), overwrite = false,
|
91
|
+
// Set globalEdit to true when the same prompt is being sent to multiple pages
|
92
|
+
globalEdit = false) {
|
91
93
|
if (!overwrite && this.hasPrompt(initialPrompt.path)) {
|
92
94
|
//console.log('Ignoring duplicate prompt', initialPrompt.path);
|
93
95
|
return Promise.resolve();
|
@@ -96,39 +98,26 @@ class PageQueue extends node_events_1.EventEmitter {
|
|
96
98
|
const prompt = {
|
97
99
|
...initialPrompt,
|
98
100
|
shell_page: this.uiShells.find((shell) => shell.screens.includes(initialPrompt.name))?.content,
|
99
|
-
prompt:
|
101
|
+
prompt: initialPrompt.prompt,
|
100
102
|
theme: this.theme,
|
103
|
+
global_edit: globalEdit,
|
104
|
+
system_prompt: this.systemPrompt,
|
105
|
+
existing_pages: this.getExistingPages(initialPrompt.path),
|
106
|
+
existing_images: Object.entries(this.images).map(([path, description]) => ({ path, description })),
|
101
107
|
};
|
102
108
|
this.references.set(prompt.path, true);
|
103
109
|
this.pages.set(prompt.path, prompt.description);
|
104
110
|
return this.queue.add(() => this.generate(prompt, conversationId));
|
105
111
|
}
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
let promptPostfix = '';
|
116
|
-
if (this.pages.size > 0) {
|
117
|
-
promptPostfix = `\nThe following pages are already implemented:\n`;
|
118
|
-
this.pages.forEach((description, path) => {
|
119
|
-
if (pagePath === path) {
|
120
|
-
return;
|
121
|
-
}
|
122
|
-
promptPostfix += `- PAGE: '${path}' -> ${description}.\n`;
|
123
|
-
});
|
124
|
-
}
|
125
|
-
if (this.images.size > 0) {
|
126
|
-
promptPostfix += `\nThe following images already exist:\n`;
|
127
|
-
this.images.forEach((description, path) => {
|
128
|
-
promptPostfix += `- IMAGE: '${path}' -> ${description}.\n`;
|
129
|
-
});
|
130
|
-
}
|
131
|
-
return promptPrefix + prompt + promptPostfix;
|
112
|
+
/**
|
113
|
+
* Get the existing pages
|
114
|
+
*/
|
115
|
+
getExistingPages(excludePath) {
|
116
|
+
return (Object.entries(this.pages)
|
117
|
+
// Possibly exclude one page. This is useful when we don't want to include the page
|
118
|
+
// we're currently editing.
|
119
|
+
.filter(([path]) => (excludePath ? path !== excludePath : true))
|
120
|
+
.map(([path, description]) => ({ path, description })));
|
132
121
|
}
|
133
122
|
async processPageEventWithReferences(event) {
|
134
123
|
try {
|
@@ -176,9 +165,17 @@ class PageQueue extends node_events_1.EventEmitter {
|
|
176
165
|
path: normalizedPath,
|
177
166
|
method: 'GET',
|
178
167
|
storage_prefix: this.systemId + '_',
|
179
|
-
prompt: `Implement a page for ${reference.name} at ${reference.url} with the following description: ${reference.description}
|
180
|
-
`The page was referenced from this page: \n### PATH: ${event.payload.path}\n\`\`\`html\n${event.payload.content}\n\`\`\`\n`,
|
168
|
+
prompt: `Implement a page for ${reference.name} at ${reference.url} with the following description: ${reference.description}`,
|
181
169
|
description: reference.description,
|
170
|
+
referenced_from: {
|
171
|
+
path: event.payload.path,
|
172
|
+
content: event.payload.content,
|
173
|
+
},
|
174
|
+
existing_pages: this.getExistingPages(),
|
175
|
+
existing_images: Object.entries(this.images).map(([path, description]) => ({
|
176
|
+
path,
|
177
|
+
description,
|
178
|
+
})),
|
182
179
|
// Only used for matching
|
183
180
|
filename: reference.name + '.ref.html',
|
184
181
|
theme: this.theme,
|
@@ -226,6 +226,9 @@ router.delete('/ui/serve/:systemId', async (req, res) => {
|
|
226
226
|
}
|
227
227
|
res.status(200).json({ status: 'ok' });
|
228
228
|
});
|
229
|
+
/**
|
230
|
+
* Edit a single page
|
231
|
+
*/
|
229
232
|
router.post('/:handle/ui/screen', async (req, res) => {
|
230
233
|
try {
|
231
234
|
const handle = req.params.handle;
|
@@ -540,6 +543,9 @@ router.post('/:handle/ui', async (req, res) => {
|
|
540
543
|
}
|
541
544
|
}
|
542
545
|
});
|
546
|
+
/**
|
547
|
+
* Edit all pages
|
548
|
+
*/
|
543
549
|
router.post('/:handle/ui/edit', async (req, res) => {
|
544
550
|
try {
|
545
551
|
const handle = req.params.handle;
|
@@ -586,7 +592,8 @@ router.post('/:handle/ui/edit', async (req, res) => {
|
|
586
592
|
filename: page.filename,
|
587
593
|
prompt: aiRequest.prompt.prompt.prompt,
|
588
594
|
storage_prefix: storagePrefix,
|
589
|
-
}, page.conversationId, true
|
595
|
+
}, page.conversationId, true, true // this is a global edit
|
596
|
+
);
|
590
597
|
}
|
591
598
|
}));
|
592
599
|
await queue.wait();
|
@@ -28,6 +28,20 @@ export interface UIPagePrompt {
|
|
28
28
|
storage_prefix: string;
|
29
29
|
shell_page?: string;
|
30
30
|
theme?: string;
|
31
|
+
global_edit?: boolean;
|
32
|
+
system_prompt?: string;
|
33
|
+
existing_pages?: {
|
34
|
+
path: string;
|
35
|
+
description: string;
|
36
|
+
}[];
|
37
|
+
existing_images?: {
|
38
|
+
path: string;
|
39
|
+
description: string;
|
40
|
+
}[];
|
41
|
+
referenced_from?: {
|
42
|
+
path: string;
|
43
|
+
content: string;
|
44
|
+
};
|
31
45
|
}
|
32
46
|
export interface UIPageSamplePrompt extends UIPagePrompt {
|
33
47
|
variantId: string;
|
@@ -14,6 +14,7 @@ const promises_1 = __importDefault(require("node:readline/promises"));
|
|
14
14
|
const node_stream_1 = require("node:stream");
|
15
15
|
const stream_1 = require("./stream");
|
16
16
|
const fetch_retry_1 = __importDefault(require("fetch-retry"));
|
17
|
+
const undici_1 = require("undici");
|
17
18
|
const fetchWithRetries = (0, fetch_retry_1.default)(global.fetch, { retries: 5, retryDelay: 10 });
|
18
19
|
exports.STORM_ID = 'storm';
|
19
20
|
exports.ConversationIdHeader = 'Conversation-Id';
|
@@ -189,12 +190,18 @@ class StormClient {
|
|
189
190
|
headers[exports.HandleHeader] = this._handle;
|
190
191
|
headers[exports.ConversationIdHeader] = this._systemId;
|
191
192
|
headers[exports.SystemIdHeader] = this._systemId;
|
192
|
-
|
193
|
+
// Fetch with undici to override the default timeouts
|
194
|
+
const response = await (0, undici_1.fetch)(u, {
|
193
195
|
method: 'POST',
|
194
196
|
body: JSON.stringify({
|
195
197
|
pages: input.pages,
|
196
198
|
}),
|
197
199
|
headers: headers,
|
200
|
+
dispatcher: new undici_1.Agent({
|
201
|
+
headersTimeout: 0,
|
202
|
+
keepAliveTimeout: 0,
|
203
|
+
bodyTimeout: 0,
|
204
|
+
}),
|
198
205
|
});
|
199
206
|
if (!response.ok) {
|
200
207
|
throw new Error(`HTTP error! Status: ${response.status}`);
|
@@ -41,9 +41,11 @@ export declare class PageQueue extends EventEmitter {
|
|
41
41
|
addUiShell(uiShell: UIShell): void;
|
42
42
|
setUiTheme(theme: string): void;
|
43
43
|
private hasPrompt;
|
44
|
-
addPrompt(initialPrompt: InitialPrompt, conversationId?: string, overwrite?: boolean): Promise<void>;
|
45
|
-
|
46
|
-
|
44
|
+
addPrompt(initialPrompt: InitialPrompt, conversationId?: string, overwrite?: boolean, globalEdit?: boolean): Promise<void>;
|
45
|
+
/**
|
46
|
+
* Get the existing pages
|
47
|
+
*/
|
48
|
+
private getExistingPages;
|
47
49
|
private processPageEventWithReferences;
|
48
50
|
cancel(): void;
|
49
51
|
wait(): Promise<void>;
|
@@ -87,7 +87,9 @@ class PageQueue extends node_events_1.EventEmitter {
|
|
87
87
|
}
|
88
88
|
return false;
|
89
89
|
}
|
90
|
-
addPrompt(initialPrompt, conversationId = node_uuid_1.default.v4(), overwrite = false
|
90
|
+
addPrompt(initialPrompt, conversationId = node_uuid_1.default.v4(), overwrite = false,
|
91
|
+
// Set globalEdit to true when the same prompt is being sent to multiple pages
|
92
|
+
globalEdit = false) {
|
91
93
|
if (!overwrite && this.hasPrompt(initialPrompt.path)) {
|
92
94
|
//console.log('Ignoring duplicate prompt', initialPrompt.path);
|
93
95
|
return Promise.resolve();
|
@@ -96,39 +98,26 @@ class PageQueue extends node_events_1.EventEmitter {
|
|
96
98
|
const prompt = {
|
97
99
|
...initialPrompt,
|
98
100
|
shell_page: this.uiShells.find((shell) => shell.screens.includes(initialPrompt.name))?.content,
|
99
|
-
prompt:
|
101
|
+
prompt: initialPrompt.prompt,
|
100
102
|
theme: this.theme,
|
103
|
+
global_edit: globalEdit,
|
104
|
+
system_prompt: this.systemPrompt,
|
105
|
+
existing_pages: this.getExistingPages(initialPrompt.path),
|
106
|
+
existing_images: Object.entries(this.images).map(([path, description]) => ({ path, description })),
|
101
107
|
};
|
102
108
|
this.references.set(prompt.path, true);
|
103
109
|
this.pages.set(prompt.path, prompt.description);
|
104
110
|
return this.queue.add(() => this.generate(prompt, conversationId));
|
105
111
|
}
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
let promptPostfix = '';
|
116
|
-
if (this.pages.size > 0) {
|
117
|
-
promptPostfix = `\nThe following pages are already implemented:\n`;
|
118
|
-
this.pages.forEach((description, path) => {
|
119
|
-
if (pagePath === path) {
|
120
|
-
return;
|
121
|
-
}
|
122
|
-
promptPostfix += `- PAGE: '${path}' -> ${description}.\n`;
|
123
|
-
});
|
124
|
-
}
|
125
|
-
if (this.images.size > 0) {
|
126
|
-
promptPostfix += `\nThe following images already exist:\n`;
|
127
|
-
this.images.forEach((description, path) => {
|
128
|
-
promptPostfix += `- IMAGE: '${path}' -> ${description}.\n`;
|
129
|
-
});
|
130
|
-
}
|
131
|
-
return promptPrefix + prompt + promptPostfix;
|
112
|
+
/**
|
113
|
+
* Get the existing pages
|
114
|
+
*/
|
115
|
+
getExistingPages(excludePath) {
|
116
|
+
return (Object.entries(this.pages)
|
117
|
+
// Possibly exclude one page. This is useful when we don't want to include the page
|
118
|
+
// we're currently editing.
|
119
|
+
.filter(([path]) => (excludePath ? path !== excludePath : true))
|
120
|
+
.map(([path, description]) => ({ path, description })));
|
132
121
|
}
|
133
122
|
async processPageEventWithReferences(event) {
|
134
123
|
try {
|
@@ -176,9 +165,17 @@ class PageQueue extends node_events_1.EventEmitter {
|
|
176
165
|
path: normalizedPath,
|
177
166
|
method: 'GET',
|
178
167
|
storage_prefix: this.systemId + '_',
|
179
|
-
prompt: `Implement a page for ${reference.name} at ${reference.url} with the following description: ${reference.description}
|
180
|
-
`The page was referenced from this page: \n### PATH: ${event.payload.path}\n\`\`\`html\n${event.payload.content}\n\`\`\`\n`,
|
168
|
+
prompt: `Implement a page for ${reference.name} at ${reference.url} with the following description: ${reference.description}`,
|
181
169
|
description: reference.description,
|
170
|
+
referenced_from: {
|
171
|
+
path: event.payload.path,
|
172
|
+
content: event.payload.content,
|
173
|
+
},
|
174
|
+
existing_pages: this.getExistingPages(),
|
175
|
+
existing_images: Object.entries(this.images).map(([path, description]) => ({
|
176
|
+
path,
|
177
|
+
description,
|
178
|
+
})),
|
182
179
|
// Only used for matching
|
183
180
|
filename: reference.name + '.ref.html',
|
184
181
|
theme: this.theme,
|
@@ -226,6 +226,9 @@ router.delete('/ui/serve/:systemId', async (req, res) => {
|
|
226
226
|
}
|
227
227
|
res.status(200).json({ status: 'ok' });
|
228
228
|
});
|
229
|
+
/**
|
230
|
+
* Edit a single page
|
231
|
+
*/
|
229
232
|
router.post('/:handle/ui/screen', async (req, res) => {
|
230
233
|
try {
|
231
234
|
const handle = req.params.handle;
|
@@ -540,6 +543,9 @@ router.post('/:handle/ui', async (req, res) => {
|
|
540
543
|
}
|
541
544
|
}
|
542
545
|
});
|
546
|
+
/**
|
547
|
+
* Edit all pages
|
548
|
+
*/
|
543
549
|
router.post('/:handle/ui/edit', async (req, res) => {
|
544
550
|
try {
|
545
551
|
const handle = req.params.handle;
|
@@ -586,7 +592,8 @@ router.post('/:handle/ui/edit', async (req, res) => {
|
|
586
592
|
filename: page.filename,
|
587
593
|
prompt: aiRequest.prompt.prompt.prompt,
|
588
594
|
storage_prefix: storagePrefix,
|
589
|
-
}, page.conversationId, true
|
595
|
+
}, page.conversationId, true, true // this is a global edit
|
596
|
+
);
|
590
597
|
}
|
591
598
|
}));
|
592
599
|
await queue.wait();
|
@@ -28,6 +28,20 @@ export interface UIPagePrompt {
|
|
28
28
|
storage_prefix: string;
|
29
29
|
shell_page?: string;
|
30
30
|
theme?: string;
|
31
|
+
global_edit?: boolean;
|
32
|
+
system_prompt?: string;
|
33
|
+
existing_pages?: {
|
34
|
+
path: string;
|
35
|
+
description: string;
|
36
|
+
}[];
|
37
|
+
existing_images?: {
|
38
|
+
path: string;
|
39
|
+
description: string;
|
40
|
+
}[];
|
41
|
+
referenced_from?: {
|
42
|
+
path: string;
|
43
|
+
content: string;
|
44
|
+
};
|
31
45
|
}
|
32
46
|
export interface UIPageSamplePrompt extends UIPagePrompt {
|
33
47
|
variantId: string;
|
@@ -14,6 +14,7 @@ const promises_1 = __importDefault(require("node:readline/promises"));
|
|
14
14
|
const node_stream_1 = require("node:stream");
|
15
15
|
const stream_1 = require("./stream");
|
16
16
|
const fetch_retry_1 = __importDefault(require("fetch-retry"));
|
17
|
+
const undici_1 = require("undici");
|
17
18
|
const fetchWithRetries = (0, fetch_retry_1.default)(global.fetch, { retries: 5, retryDelay: 10 });
|
18
19
|
exports.STORM_ID = 'storm';
|
19
20
|
exports.ConversationIdHeader = 'Conversation-Id';
|
@@ -189,12 +190,18 @@ class StormClient {
|
|
189
190
|
headers[exports.HandleHeader] = this._handle;
|
190
191
|
headers[exports.ConversationIdHeader] = this._systemId;
|
191
192
|
headers[exports.SystemIdHeader] = this._systemId;
|
192
|
-
|
193
|
+
// Fetch with undici to override the default timeouts
|
194
|
+
const response = await (0, undici_1.fetch)(u, {
|
193
195
|
method: 'POST',
|
194
196
|
body: JSON.stringify({
|
195
197
|
pages: input.pages,
|
196
198
|
}),
|
197
199
|
headers: headers,
|
200
|
+
dispatcher: new undici_1.Agent({
|
201
|
+
headersTimeout: 0,
|
202
|
+
keepAliveTimeout: 0,
|
203
|
+
bodyTimeout: 0,
|
204
|
+
}),
|
198
205
|
});
|
199
206
|
if (!response.ok) {
|
200
207
|
throw new Error(`HTTP error! Status: ${response.status}`);
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@kapeta/local-cluster-service",
|
3
|
-
"version": "0.
|
3
|
+
"version": "0.77.1",
|
4
4
|
"description": "Manages configuration, ports and service discovery for locally running Kapeta systems",
|
5
5
|
"type": "commonjs",
|
6
6
|
"exports": {
|
@@ -90,6 +90,7 @@
|
|
90
90
|
"tar": "^7.4.3",
|
91
91
|
"tar-stream": "^3.1.6",
|
92
92
|
"typescript": "^5.1.6",
|
93
|
+
"undici": "^6.19.8",
|
93
94
|
"uuid": "^9.0.1",
|
94
95
|
"yaml": "^1.6.0"
|
95
96
|
},
|
@@ -95,7 +95,13 @@ export class PageQueue extends EventEmitter {
|
|
95
95
|
return false;
|
96
96
|
}
|
97
97
|
|
98
|
-
public addPrompt(
|
98
|
+
public addPrompt(
|
99
|
+
initialPrompt: InitialPrompt,
|
100
|
+
conversationId: string = uuid.v4(),
|
101
|
+
overwrite: boolean = false,
|
102
|
+
// Set globalEdit to true when the same prompt is being sent to multiple pages
|
103
|
+
globalEdit: boolean = false
|
104
|
+
) {
|
99
105
|
if (!overwrite && this.hasPrompt(initialPrompt.path)) {
|
100
106
|
//console.log('Ignoring duplicate prompt', initialPrompt.path);
|
101
107
|
return Promise.resolve();
|
@@ -105,8 +111,12 @@ export class PageQueue extends EventEmitter {
|
|
105
111
|
const prompt: UIPagePrompt = {
|
106
112
|
...initialPrompt,
|
107
113
|
shell_page: this.uiShells.find((shell) => shell.screens.includes(initialPrompt.name))?.content,
|
108
|
-
prompt:
|
114
|
+
prompt: initialPrompt.prompt,
|
109
115
|
theme: this.theme,
|
116
|
+
global_edit: globalEdit,
|
117
|
+
system_prompt: this.systemPrompt,
|
118
|
+
existing_pages: this.getExistingPages(initialPrompt.path),
|
119
|
+
existing_images: Object.entries(this.images).map(([path, description]) => ({ path, description })),
|
110
120
|
};
|
111
121
|
|
112
122
|
this.references.set(prompt.path, true);
|
@@ -114,36 +124,18 @@ export class PageQueue extends EventEmitter {
|
|
114
124
|
|
115
125
|
return this.queue.add<void>(() => this.generate(prompt, conversationId));
|
116
126
|
}
|
117
|
-
private getPrefix(): string {
|
118
|
-
let promptPrefix = '';
|
119
|
-
if (this.systemPrompt) {
|
120
|
-
promptPrefix = `For a system with this description: ${this.systemPrompt}\n`;
|
121
|
-
}
|
122
|
-
return promptPrefix;
|
123
|
-
}
|
124
|
-
|
125
|
-
private wrapPagePrompt(pagePath: string, prompt: string): string {
|
126
|
-
const promptPrefix = this.getPrefix();
|
127
|
-
let promptPostfix = '';
|
128
|
-
|
129
|
-
if (this.pages.size > 0) {
|
130
|
-
promptPostfix = `\nThe following pages are already implemented:\n`;
|
131
|
-
this.pages.forEach((description, path) => {
|
132
|
-
if (pagePath === path) {
|
133
|
-
return;
|
134
|
-
}
|
135
|
-
promptPostfix += `- PAGE: '${path}' -> ${description}.\n`;
|
136
|
-
});
|
137
|
-
}
|
138
|
-
|
139
|
-
if (this.images.size > 0) {
|
140
|
-
promptPostfix += `\nThe following images already exist:\n`;
|
141
|
-
this.images.forEach((description, path) => {
|
142
|
-
promptPostfix += `- IMAGE: '${path}' -> ${description}.\n`;
|
143
|
-
});
|
144
|
-
}
|
145
127
|
|
146
|
-
|
128
|
+
/**
|
129
|
+
* Get the existing pages
|
130
|
+
*/
|
131
|
+
private getExistingPages(excludePath?: string) {
|
132
|
+
return (
|
133
|
+
Object.entries(this.pages)
|
134
|
+
// Possibly exclude one page. This is useful when we don't want to include the page
|
135
|
+
// we're currently editing.
|
136
|
+
.filter(([path]) => (excludePath ? path !== excludePath : true))
|
137
|
+
.map(([path, description]) => ({ path, description }))
|
138
|
+
);
|
147
139
|
}
|
148
140
|
|
149
141
|
private async processPageEventWithReferences(event: StormEventPage) {
|
@@ -198,10 +190,17 @@ export class PageQueue extends EventEmitter {
|
|
198
190
|
path: normalizedPath,
|
199
191
|
method: 'GET',
|
200
192
|
storage_prefix: this.systemId + '_',
|
201
|
-
prompt:
|
202
|
-
`Implement a page for ${reference.name} at ${reference.url} with the following description: ${reference.description}.\n` +
|
203
|
-
`The page was referenced from this page: \n### PATH: ${event.payload.path}\n\`\`\`html\n${event.payload.content}\n\`\`\`\n`,
|
193
|
+
prompt: `Implement a page for ${reference.name} at ${reference.url} with the following description: ${reference.description}`,
|
204
194
|
description: reference.description,
|
195
|
+
referenced_from: {
|
196
|
+
path: event.payload.path,
|
197
|
+
content: event.payload.content,
|
198
|
+
},
|
199
|
+
existing_pages: this.getExistingPages(),
|
200
|
+
existing_images: Object.entries(this.images).map(([path, description]) => ({
|
201
|
+
path,
|
202
|
+
description,
|
203
|
+
})),
|
205
204
|
// Only used for matching
|
206
205
|
filename: reference.name + '.ref.html',
|
207
206
|
theme: this.theme,
|
package/src/storm/routes.ts
CHANGED
@@ -293,6 +293,9 @@ router.delete('/ui/serve/:systemId', async (req: KapetaBodyRequest, res: Respons
|
|
293
293
|
res.status(200).json({ status: 'ok' });
|
294
294
|
});
|
295
295
|
|
296
|
+
/**
|
297
|
+
* Edit a single page
|
298
|
+
*/
|
296
299
|
router.post('/:handle/ui/screen', async (req: KapetaBodyRequest, res: Response) => {
|
297
300
|
try {
|
298
301
|
const handle = req.params.handle as string;
|
@@ -673,6 +676,9 @@ router.post('/:handle/ui', async (req: KapetaBodyRequest, res: Response) => {
|
|
673
676
|
}
|
674
677
|
});
|
675
678
|
|
679
|
+
/**
|
680
|
+
* Edit all pages
|
681
|
+
*/
|
676
682
|
router.post('/:handle/ui/edit', async (req: KapetaBodyRequest, res: Response) => {
|
677
683
|
try {
|
678
684
|
const handle = req.params.handle as string;
|
@@ -732,7 +738,8 @@ router.post('/:handle/ui/edit', async (req: KapetaBodyRequest, res: Response) =>
|
|
732
738
|
storage_prefix: storagePrefix,
|
733
739
|
},
|
734
740
|
page.conversationId,
|
735
|
-
true
|
741
|
+
true,
|
742
|
+
true // this is a global edit
|
736
743
|
);
|
737
744
|
}
|
738
745
|
})
|
package/src/storm/stormClient.ts
CHANGED
@@ -19,6 +19,7 @@ import {
|
|
19
19
|
} from './stream';
|
20
20
|
import { Page, StormEventPageUrl } from './events';
|
21
21
|
import createFetch from 'fetch-retry';
|
22
|
+
import { Agent, fetch as undiciFetch } from 'undici';
|
22
23
|
const fetchWithRetries = createFetch(global.fetch, { retries: 5, retryDelay: 10 });
|
23
24
|
|
24
25
|
export const STORM_ID = 'storm';
|
@@ -51,6 +52,25 @@ export interface UIPagePrompt {
|
|
51
52
|
shell_page?: string;
|
52
53
|
// contents of theme.md
|
53
54
|
theme?: string;
|
55
|
+
// whether this prompt is for a global edit
|
56
|
+
global_edit?: boolean;
|
57
|
+
// The prompt that was used to start the conversation (user prompt or improved prompt)
|
58
|
+
system_prompt?: string;
|
59
|
+
// The pages we have already created
|
60
|
+
existing_pages?: {
|
61
|
+
path: string;
|
62
|
+
description: string;
|
63
|
+
}[];
|
64
|
+
// The images we have already created
|
65
|
+
existing_images?: {
|
66
|
+
path: string;
|
67
|
+
description: string;
|
68
|
+
}[];
|
69
|
+
// The page that referenced this page
|
70
|
+
referenced_from?: {
|
71
|
+
path: string;
|
72
|
+
content: string;
|
73
|
+
};
|
54
74
|
}
|
55
75
|
|
56
76
|
export interface UIPageSamplePrompt extends UIPagePrompt {
|
@@ -299,12 +319,18 @@ export class StormClient {
|
|
299
319
|
headers[ConversationIdHeader] = this._systemId;
|
300
320
|
headers[SystemIdHeader] = this._systemId;
|
301
321
|
|
302
|
-
|
322
|
+
// Fetch with undici to override the default timeouts
|
323
|
+
const response = await undiciFetch(u, {
|
303
324
|
method: 'POST',
|
304
325
|
body: JSON.stringify({
|
305
326
|
pages: input.pages,
|
306
327
|
}),
|
307
328
|
headers: headers,
|
329
|
+
dispatcher: new Agent({
|
330
|
+
headersTimeout: 0,
|
331
|
+
keepAliveTimeout: 0,
|
332
|
+
bodyTimeout: 0,
|
333
|
+
}),
|
308
334
|
});
|
309
335
|
if (!response.ok) {
|
310
336
|
throw new Error(`HTTP error! Status: ${response.status}`);
|