@positronic/template-new-project 0.0.52 → 0.0.53
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.js +11 -3
- package/package.json +1 -1
- package/template/brain.ts +54 -63
- package/template/components/bundle.ts +23 -0
- package/template/components/index.ts +26 -0
- package/template/docs/brain-dsl-guide.md +158 -6
- package/template/docs/positronic-guide.md +1 -1
- package/template/docs/tips-for-agents.md +50 -0
- package/template/esbuild.config.mjs +26 -0
- package/template/package.json +8 -4
- package/template/tests/test-utils.ts +6 -0
package/index.js
CHANGED
|
@@ -53,9 +53,10 @@ module.exports = {
|
|
|
53
53
|
],
|
|
54
54
|
setup: async ctx => {
|
|
55
55
|
const devRootPath = process.env.POSITRONIC_LOCAL_PATH;
|
|
56
|
-
let coreVersion = '^0.0.
|
|
57
|
-
let cloudflareVersion = '^0.0.
|
|
58
|
-
let clientVercelVersion = '^0.0.
|
|
56
|
+
let coreVersion = '^0.0.53';
|
|
57
|
+
let cloudflareVersion = '^0.0.53';
|
|
58
|
+
let clientVercelVersion = '^0.0.53';
|
|
59
|
+
let genUIComponentsVersion = '^0.0.53';
|
|
59
60
|
|
|
60
61
|
// Map backend selection to package names
|
|
61
62
|
const backendPackageMap = {
|
|
@@ -98,6 +99,12 @@ module.exports = {
|
|
|
98
99
|
if (existsSync(clientVercelPath)) {
|
|
99
100
|
clientVercelVersion = `file:${clientVercelPath}`;
|
|
100
101
|
}
|
|
102
|
+
|
|
103
|
+
const genUIComponentsPath = path.resolve(devRootPath, 'packages', 'gen-ui-components');
|
|
104
|
+
if (existsSync(genUIComponentsPath)) {
|
|
105
|
+
genUIComponentsVersion = `file:${genUIComponentsPath}`;
|
|
106
|
+
console.log(` - Mapping @positronic/gen-ui-components to ${genUIComponentsVersion}`);
|
|
107
|
+
}
|
|
101
108
|
}
|
|
102
109
|
|
|
103
110
|
ctx.answers.positronicCoreVersion = coreVersion;
|
|
@@ -106,6 +113,7 @@ module.exports = {
|
|
|
106
113
|
}
|
|
107
114
|
|
|
108
115
|
ctx.answers.positronicClientVercelVersion = clientVercelVersion;
|
|
116
|
+
ctx.answers.positronicGenUIComponentsVersion = genUIComponentsVersion;
|
|
109
117
|
|
|
110
118
|
if (ctx.answers.install) {
|
|
111
119
|
const pm = ctx.answers.pm;
|
package/package.json
CHANGED
package/template/brain.ts
CHANGED
|
@@ -1,72 +1,63 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createBrain } from '@positronic/core';
|
|
2
|
+
import { components } from './components/index.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* To add services:
|
|
10
|
-
* 1. Define your service interfaces
|
|
11
|
-
* 2. Create service instances
|
|
12
|
-
* 3. Call .withServices() on the brain before returning it
|
|
13
|
-
*
|
|
14
|
-
* Example with services:
|
|
5
|
+
* Project-level brain function with pre-configured components.
|
|
6
|
+
*
|
|
7
|
+
* All brains in your project should import from this file:
|
|
8
|
+
*
|
|
15
9
|
* ```typescript
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* };
|
|
21
|
-
* api: {
|
|
22
|
-
* fetch: (endpoint: string) => Promise<any>;
|
|
23
|
-
* };
|
|
24
|
-
* }
|
|
25
|
-
*
|
|
26
|
-
* export const brain: BrainFactory = (brainConfig) => {
|
|
27
|
-
* return coreBrain(brainConfig)
|
|
28
|
-
* .withServices({
|
|
29
|
-
* logger: {
|
|
30
|
-
* info: (msg) => console.log(`[INFO] <%= '${msg}' %>`),
|
|
31
|
-
* error: (msg) => console.error(`[ERROR] <%= '${msg}' %>`)
|
|
32
|
-
* },
|
|
33
|
-
* api: {
|
|
34
|
-
* fetch: async (endpoint) => {
|
|
35
|
-
* const response = await fetch(`https://api.example.com<%= '${endpoint}' %>`);
|
|
36
|
-
* return response.json();
|
|
37
|
-
* }
|
|
38
|
-
* }
|
|
39
|
-
* });
|
|
40
|
-
* }
|
|
10
|
+
* import { brain } from '../brain.js';
|
|
11
|
+
*
|
|
12
|
+
* export default brain('my-brain')
|
|
13
|
+
* .step('Do something', ({ state }) => ({ ...state, done: true }));
|
|
41
14
|
* ```
|
|
42
|
-
*
|
|
43
|
-
*
|
|
15
|
+
*
|
|
16
|
+
* To add services (e.g., Slack, Gmail, database clients):
|
|
17
|
+
*
|
|
44
18
|
* ```typescript
|
|
45
|
-
* import {
|
|
46
|
-
* import {
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
19
|
+
* import { createBrain } from '@positronic/core';
|
|
20
|
+
* import { components } from './components/index.js';
|
|
21
|
+
* import slack from './services/slack.js';
|
|
22
|
+
* import gmail from './services/gmail.js';
|
|
23
|
+
*
|
|
24
|
+
* export const brain = createBrain({
|
|
25
|
+
* services: { slack, gmail },
|
|
26
|
+
* components,
|
|
51
27
|
* });
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
* return { users: data };
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* Then services are available in all brain steps:
|
|
31
|
+
*
|
|
32
|
+
* ```typescript
|
|
33
|
+
* export default brain('notify')
|
|
34
|
+
* .step('Send alert', ({ slack }) => {
|
|
35
|
+
* slack.postMessage('#alerts', 'Something happened!');
|
|
36
|
+
* return { notified: true };
|
|
62
37
|
* });
|
|
63
38
|
* ```
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
*
|
|
39
|
+
*
|
|
40
|
+
* You can also create agents directly:
|
|
41
|
+
*
|
|
42
|
+
* ```typescript
|
|
43
|
+
* export default brain('my-agent', ({ slack, env }) => ({
|
|
44
|
+
* system: 'You are a helpful assistant',
|
|
45
|
+
* prompt: 'Help the user with their request',
|
|
46
|
+
* tools: {
|
|
47
|
+
* notify: {
|
|
48
|
+
* description: 'Send a Slack notification',
|
|
49
|
+
* inputSchema: z.object({ message: z.string() }),
|
|
50
|
+
* execute: ({ message }) => slack.postMessage('#general', message),
|
|
51
|
+
* },
|
|
52
|
+
* done: {
|
|
53
|
+
* description: 'Complete the task',
|
|
54
|
+
* inputSchema: z.object({ result: z.string() }),
|
|
55
|
+
* terminal: true,
|
|
56
|
+
* },
|
|
57
|
+
* },
|
|
58
|
+
* }));
|
|
59
|
+
* ```
|
|
67
60
|
*/
|
|
68
|
-
export const brain
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return coreBrain(brainConfig);
|
|
72
|
-
};
|
|
61
|
+
export const brain = createBrain({
|
|
62
|
+
components,
|
|
63
|
+
});
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bundle entry point for client-side rendering.
|
|
3
|
+
*
|
|
4
|
+
* This file is bundled by esbuild into dist/components.js which exposes
|
|
5
|
+
* React components to window.PositronicComponents for use by generated pages.
|
|
6
|
+
*
|
|
7
|
+
* When you add custom components to ./index.ts, they will automatically
|
|
8
|
+
* be included in the bundle.
|
|
9
|
+
*/
|
|
10
|
+
import { components } from './index.js';
|
|
11
|
+
|
|
12
|
+
// Extract the React component from each UIComponent and expose to window
|
|
13
|
+
const PositronicComponents: Record<string, React.ComponentType<any>> = {};
|
|
14
|
+
|
|
15
|
+
for (const [name, uiComponent] of Object.entries(components)) {
|
|
16
|
+
PositronicComponents[name] = uiComponent.component;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Expose to window for client-side rendering
|
|
20
|
+
(window as unknown as { PositronicComponents: typeof PositronicComponents }).PositronicComponents =
|
|
21
|
+
PositronicComponents;
|
|
22
|
+
|
|
23
|
+
export { PositronicComponents };
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* UI Components for this project.
|
|
3
|
+
*
|
|
4
|
+
* This file re-exports the default Positronic components and is the place
|
|
5
|
+
* to add your own custom components.
|
|
6
|
+
*
|
|
7
|
+
* To add a custom component:
|
|
8
|
+
* 1. Create a component file (e.g., CustomButton.ts) with UIComponent structure
|
|
9
|
+
* 2. Import and add it to the components object below
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* import { CustomButton } from './CustomButton.js';
|
|
14
|
+
*
|
|
15
|
+
* export const components = {
|
|
16
|
+
* ...defaultComponents,
|
|
17
|
+
* CustomButton,
|
|
18
|
+
* };
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
import { components as defaultComponents } from '@positronic/gen-ui-components';
|
|
22
|
+
|
|
23
|
+
// Re-export default components - add your custom components here
|
|
24
|
+
export const components = {
|
|
25
|
+
...defaultComponents,
|
|
26
|
+
};
|
|
@@ -678,6 +678,163 @@ Extract prompts to separate files when:
|
|
|
678
678
|
- The prompt might be reused in other brains
|
|
679
679
|
- You want to test the prompt logic separately
|
|
680
680
|
|
|
681
|
+
## UI Steps
|
|
682
|
+
|
|
683
|
+
UI steps allow brains to generate dynamic user interfaces using AI. The `.ui()` step generates a page and provides a `page` object to the next step. You then notify users and use `waitFor` to pause until the form is submitted.
|
|
684
|
+
|
|
685
|
+
### Basic UI Step
|
|
686
|
+
|
|
687
|
+
```typescript
|
|
688
|
+
import { z } from 'zod';
|
|
689
|
+
|
|
690
|
+
brain('Feedback Collector')
|
|
691
|
+
.step('Initialize', ({ state }) => ({
|
|
692
|
+
...state,
|
|
693
|
+
userName: 'John Doe',
|
|
694
|
+
}))
|
|
695
|
+
// Generate the form
|
|
696
|
+
.ui('Collect Feedback', {
|
|
697
|
+
template: (state) => `
|
|
698
|
+
Create a feedback form for <%= '${state.userName}' %>.
|
|
699
|
+
Include fields for rating (1-5) and comments.
|
|
700
|
+
`,
|
|
701
|
+
responseSchema: z.object({
|
|
702
|
+
rating: z.number().min(1).max(5),
|
|
703
|
+
comments: z.string(),
|
|
704
|
+
}),
|
|
705
|
+
})
|
|
706
|
+
// Notify user and wait for submission
|
|
707
|
+
.step('Notify and Wait', async ({ state, page, slack }) => {
|
|
708
|
+
await slack.post('#feedback', `Please fill out: <%= '${page.url}' %>`);
|
|
709
|
+
return {
|
|
710
|
+
state,
|
|
711
|
+
waitFor: [page.webhook],
|
|
712
|
+
};
|
|
713
|
+
})
|
|
714
|
+
// Process the form data (comes through response, not page)
|
|
715
|
+
.step('Process Feedback', ({ state, response }) => ({
|
|
716
|
+
...state,
|
|
717
|
+
feedbackReceived: true,
|
|
718
|
+
rating: response.rating, // typed from responseSchema
|
|
719
|
+
comments: response.comments,
|
|
720
|
+
}));
|
|
721
|
+
```
|
|
722
|
+
|
|
723
|
+
### How UI Steps Work
|
|
724
|
+
|
|
725
|
+
1. **Template**: The `template` function generates a prompt describing the desired UI
|
|
726
|
+
2. **AI Generation**: The AI creates a component tree based on the prompt
|
|
727
|
+
3. **Page Object**: Next step receives `page` with `url` and `webhook`
|
|
728
|
+
4. **Notification**: You notify users however you want (Slack, email, etc.)
|
|
729
|
+
5. **Wait**: Use `waitFor: [page.webhook]` to pause until form submission
|
|
730
|
+
6. **Form Data**: Step after `waitFor` receives form data via `response`
|
|
731
|
+
|
|
732
|
+
### The `page` Object
|
|
733
|
+
|
|
734
|
+
After a `.ui()` step, the next step receives:
|
|
735
|
+
- `page.url` - URL where users can access the form
|
|
736
|
+
- `page.webhook` - Pre-configured webhook for form submissions
|
|
737
|
+
|
|
738
|
+
### Template Best Practices
|
|
739
|
+
|
|
740
|
+
Be specific about layout and content:
|
|
741
|
+
|
|
742
|
+
```typescript
|
|
743
|
+
.ui('Contact Form', {
|
|
744
|
+
template: (state) => `
|
|
745
|
+
Create a contact form with:
|
|
746
|
+
- Header: "Get in Touch"
|
|
747
|
+
- Name field (required)
|
|
748
|
+
- Email field (required, pre-filled with "<%= '${state.email}' %>")
|
|
749
|
+
- Message textarea (required)
|
|
750
|
+
- Submit button labeled "Send Message"
|
|
751
|
+
|
|
752
|
+
Use a clean, centered single-column layout.
|
|
753
|
+
`,
|
|
754
|
+
responseSchema: z.object({
|
|
755
|
+
name: z.string(),
|
|
756
|
+
email: z.string().email(),
|
|
757
|
+
message: z.string(),
|
|
758
|
+
}),
|
|
759
|
+
})
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
### Data Bindings
|
|
763
|
+
|
|
764
|
+
Use `{{path}}` syntax to bind props to runtime data:
|
|
765
|
+
|
|
766
|
+
```typescript
|
|
767
|
+
.ui('Order Summary', {
|
|
768
|
+
template: (state) => `
|
|
769
|
+
Create an order summary showing:
|
|
770
|
+
- List of items from {{cart.items}}
|
|
771
|
+
- Total: {{cart.total}}
|
|
772
|
+
- Shipping address input
|
|
773
|
+
- Confirm button
|
|
774
|
+
`,
|
|
775
|
+
responseSchema: z.object({
|
|
776
|
+
shippingAddress: z.string(),
|
|
777
|
+
}),
|
|
778
|
+
})
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
### Multi-Step Forms
|
|
782
|
+
|
|
783
|
+
Chain UI steps for multi-page workflows:
|
|
784
|
+
|
|
785
|
+
```typescript
|
|
786
|
+
brain('User Onboarding')
|
|
787
|
+
.step('Start', () => ({ userData: {} }))
|
|
788
|
+
|
|
789
|
+
// Step 1: Personal info
|
|
790
|
+
.ui('Personal Info', {
|
|
791
|
+
template: () => `
|
|
792
|
+
Create a form for personal information:
|
|
793
|
+
- First name, Last name
|
|
794
|
+
- Date of birth
|
|
795
|
+
- Next button
|
|
796
|
+
`,
|
|
797
|
+
responseSchema: z.object({
|
|
798
|
+
firstName: z.string(),
|
|
799
|
+
lastName: z.string(),
|
|
800
|
+
dob: z.string(),
|
|
801
|
+
}),
|
|
802
|
+
})
|
|
803
|
+
.step('Wait for Personal', async ({ state, page, notify }) => {
|
|
804
|
+
await notify(`Step 1: <%= '${page.url}' %>`);
|
|
805
|
+
return { state, waitFor: [page.webhook] };
|
|
806
|
+
})
|
|
807
|
+
.step('Save Personal', ({ state, response }) => ({
|
|
808
|
+
...state,
|
|
809
|
+
userData: { ...state.userData, ...response },
|
|
810
|
+
}))
|
|
811
|
+
|
|
812
|
+
// Step 2: Preferences
|
|
813
|
+
.ui('Preferences', {
|
|
814
|
+
template: (state) => `
|
|
815
|
+
Create preferences form for <%= '${state.userData.firstName}' %>:
|
|
816
|
+
- Newsletter subscription checkbox
|
|
817
|
+
- Contact preference (email/phone/sms)
|
|
818
|
+
- Complete button
|
|
819
|
+
`,
|
|
820
|
+
responseSchema: z.object({
|
|
821
|
+
newsletter: z.boolean(),
|
|
822
|
+
contactMethod: z.enum(['email', 'phone', 'sms']),
|
|
823
|
+
}),
|
|
824
|
+
})
|
|
825
|
+
.step('Wait for Preferences', async ({ state, page, notify }) => {
|
|
826
|
+
await notify(`Step 2: <%= '${page.url}' %>`);
|
|
827
|
+
return { state, waitFor: [page.webhook] };
|
|
828
|
+
})
|
|
829
|
+
.step('Complete', ({ state, response }) => ({
|
|
830
|
+
...state,
|
|
831
|
+
userData: { ...state.userData, preferences: response },
|
|
832
|
+
onboardingComplete: true,
|
|
833
|
+
}));
|
|
834
|
+
```
|
|
835
|
+
|
|
836
|
+
For more details on UI steps, see the full UI Step Guide in the main Positronic documentation.
|
|
837
|
+
|
|
681
838
|
## Complete Example
|
|
682
839
|
|
|
683
840
|
```typescript
|
|
@@ -722,14 +879,9 @@ const completeBrain = brain({
|
|
|
722
879
|
}),
|
|
723
880
|
name: 'plan' as const,
|
|
724
881
|
},
|
|
725
|
-
},
|
|
726
|
-
// Services available in reduce function too
|
|
727
|
-
({ state, response, logger }) => {
|
|
728
|
-
logger.log(`Plan generated with <%= '${response.tasks.length}' %> tasks`);
|
|
729
|
-
return { ...state, plan: response };
|
|
730
882
|
})
|
|
731
883
|
.step('Process Plan', ({ state, logger, analytics }) => {
|
|
732
|
-
logger.log(`
|
|
884
|
+
logger.log(`Plan generated with <%= '${state.plan.tasks.length}' %> tasks`);
|
|
733
885
|
analytics.track('plan_processed', {
|
|
734
886
|
task_count: state.plan.tasks.length,
|
|
735
887
|
duration: state.plan.duration
|
|
@@ -239,5 +239,5 @@ const api = {
|
|
|
239
239
|
|
|
240
240
|
- **Documentation**: https://positronic.dev
|
|
241
241
|
- **CLI Help**: `px --help`
|
|
242
|
-
- **Brain DSL Guide**: `/docs/brain-dsl-guide.md`
|
|
242
|
+
- **Brain DSL Guide**: `/docs/brain-dsl-guide.md` (includes UI steps for generating forms)
|
|
243
243
|
- **Testing Guide**: `/docs/brain-testing-guide.md`
|
|
@@ -182,6 +182,56 @@ brain('validation-example')
|
|
|
182
182
|
|
|
183
183
|
Most generated brains should not have try-catch blocks. Only use them when the error state is meaningful to subsequent steps in the workflow.
|
|
184
184
|
|
|
185
|
+
## UI Steps for Form Generation
|
|
186
|
+
|
|
187
|
+
When you need to collect user input, use the `.ui()` method. The pattern is:
|
|
188
|
+
1. `.ui()` generates the page
|
|
189
|
+
2. Next step gets `page.url` and `page.webhook`
|
|
190
|
+
3. Notify users and use `waitFor: [page.webhook]`
|
|
191
|
+
4. Step after `waitFor` gets form data in `response`
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
import { z } from 'zod';
|
|
195
|
+
|
|
196
|
+
brain('feedback-collector')
|
|
197
|
+
.step('Initialize', ({ state }) => ({
|
|
198
|
+
...state,
|
|
199
|
+
userName: 'John',
|
|
200
|
+
}))
|
|
201
|
+
// Generate the form
|
|
202
|
+
.ui('Collect Feedback', {
|
|
203
|
+
template: (state) => <%= '\`' %>
|
|
204
|
+
Create a feedback form for <%= '${state.userName}' %>:
|
|
205
|
+
- Rating (1-5)
|
|
206
|
+
- Comments textarea
|
|
207
|
+
- Submit button
|
|
208
|
+
<%= '\`' %>,
|
|
209
|
+
responseSchema: z.object({
|
|
210
|
+
rating: z.number().min(1).max(5),
|
|
211
|
+
comments: z.string(),
|
|
212
|
+
}),
|
|
213
|
+
})
|
|
214
|
+
// Notify and wait for submission
|
|
215
|
+
.step('Notify', async ({ state, page, slack }) => {
|
|
216
|
+
await slack.post('#feedback', `Fill out: <%= '${page.url}' %>`);
|
|
217
|
+
return { state, waitFor: [page.webhook] };
|
|
218
|
+
})
|
|
219
|
+
// Form data comes through response (not page)
|
|
220
|
+
.step('Process', ({ state, response }) => ({
|
|
221
|
+
...state,
|
|
222
|
+
rating: response.rating, // Typed from responseSchema
|
|
223
|
+
comments: response.comments,
|
|
224
|
+
}));
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
Key points:
|
|
228
|
+
- `page.url` - where to send users
|
|
229
|
+
- `page.webhook` - use with `waitFor` to pause for submission
|
|
230
|
+
- `response` - form data arrives here (in step after `waitFor`)
|
|
231
|
+
- You control how users are notified (Slack, email, etc.)
|
|
232
|
+
|
|
233
|
+
See `/docs/brain-dsl-guide.md` for more UI step examples.
|
|
234
|
+
|
|
185
235
|
## Service Organization
|
|
186
236
|
|
|
187
237
|
When implementing services for the project brain, consider creating a `services/` directory at the root of your project to keep service implementations organized and reusable:
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* esbuild configuration for bundling UI components.
|
|
3
|
+
*
|
|
4
|
+
* This bundles the components from ./components/bundle.ts into a single
|
|
5
|
+
* JavaScript file that can be served to the browser.
|
|
6
|
+
*
|
|
7
|
+
* Run: npm run build:components
|
|
8
|
+
* Or let the dev server build it automatically.
|
|
9
|
+
*/
|
|
10
|
+
import * as esbuild from 'esbuild';
|
|
11
|
+
|
|
12
|
+
await esbuild.build({
|
|
13
|
+
entryPoints: ['components/bundle.ts'],
|
|
14
|
+
bundle: true,
|
|
15
|
+
external: ['react', 'react-dom'],
|
|
16
|
+
format: 'iife',
|
|
17
|
+
outfile: 'dist/components.js',
|
|
18
|
+
jsx: 'transform',
|
|
19
|
+
jsxFactory: 'React.createElement',
|
|
20
|
+
jsxFragment: 'React.Fragment',
|
|
21
|
+
tsconfigRaw: {
|
|
22
|
+
compilerOptions: {
|
|
23
|
+
jsx: 'react',
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
});
|
package/template/package.json
CHANGED
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
"type": "module",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"test": "NODE_OPTIONS='--experimental-vm-modules' jest",
|
|
9
|
-
"typecheck": "tsc --noEmit"
|
|
9
|
+
"typecheck": "tsc --noEmit",
|
|
10
|
+
"build:components": "node esbuild.config.mjs"
|
|
10
11
|
},
|
|
11
12
|
"keywords": [],
|
|
12
13
|
"author": "",
|
|
@@ -15,15 +16,18 @@
|
|
|
15
16
|
"zod": "^3.24.1",
|
|
16
17
|
"@positronic/client-vercel": "<%= positronicClientVercelVersion %>",
|
|
17
18
|
"@ai-sdk/openai": "^1.3.22",
|
|
18
|
-
"@positronic/core": "<%= positronicCoreVersion %>"
|
|
19
|
+
"@positronic/core": "<%= positronicCoreVersion %>",
|
|
20
|
+
"@positronic/gen-ui-components": "<%= positronicGenUIComponentsVersion %>"<% if (backend === 'cloudflare') { %>,
|
|
19
21
|
"@positronic/cloudflare": "<%= positronicCloudflareVersion %>"<% } %>
|
|
20
22
|
},
|
|
21
23
|
"devDependencies": {<% if (backend === 'cloudflare') { %>
|
|
22
|
-
"wrangler": "^4.
|
|
24
|
+
"wrangler": "^4.37.0",<% } %>
|
|
23
25
|
"typescript": "^5.0.0",
|
|
24
26
|
"jest": "^30.0.4",
|
|
25
27
|
"@jest/globals": "^30.0.4",
|
|
26
28
|
"ts-jest": "^29.2.6",
|
|
27
|
-
"@types/jest": "^30.0.0"
|
|
29
|
+
"@types/jest": "^30.0.0",
|
|
30
|
+
"esbuild": "^0.24.0",
|
|
31
|
+
"@types/react": "^18.2.0"
|
|
28
32
|
}
|
|
29
33
|
}
|
|
@@ -19,8 +19,13 @@ export function createMockClient(): MockClient {
|
|
|
19
19
|
return responses[responseIndex++];
|
|
20
20
|
});
|
|
21
21
|
|
|
22
|
+
const streamText = jest.fn(async () => {
|
|
23
|
+
throw new Error('streamText not implemented in mock');
|
|
24
|
+
});
|
|
25
|
+
|
|
22
26
|
return {
|
|
23
27
|
generateObject,
|
|
28
|
+
streamText,
|
|
24
29
|
mockResponses: (...newResponses: any[]) => {
|
|
25
30
|
responses.push(...newResponses);
|
|
26
31
|
},
|
|
@@ -28,6 +33,7 @@ export function createMockClient(): MockClient {
|
|
|
28
33
|
responses.length = 0;
|
|
29
34
|
responseIndex = 0;
|
|
30
35
|
generateObject.mockClear();
|
|
36
|
+
streamText.mockClear();
|
|
31
37
|
},
|
|
32
38
|
};
|
|
33
39
|
}
|