@statelyai/agent 1.1.6 → 2.0.0-next.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.
Files changed (62) hide show
  1. package/.changeset/cyan-carpets-perform.md +5 -0
  2. package/.changeset/fast-donkeys-argue.md +5 -0
  3. package/.changeset/light-hats-drive.md +9 -0
  4. package/.changeset/old-jobs-check.md +5 -0
  5. package/.changeset/pre.json +13 -0
  6. package/.vscode/launch.json +6 -0
  7. package/CHANGELOG.md +22 -0
  8. package/dist/index.d.mts +262 -171
  9. package/dist/index.d.ts +262 -171
  10. package/dist/index.js +383 -274
  11. package/dist/index.mjs +386 -272
  12. package/examples/chatbot-alt.ts +57 -0
  13. package/examples/chatbot.ts +12 -17
  14. package/examples/cot.ts +26 -23
  15. package/examples/customer-service-sim.ts +107 -0
  16. package/examples/email.ts +37 -41
  17. package/examples/example.ts +6 -6
  18. package/examples/executor.ts +66 -0
  19. package/examples/goal.ts +12 -12
  20. package/examples/helpers/helpers.ts +26 -14
  21. package/examples/joke.ts +79 -76
  22. package/examples/jugs.ts +125 -0
  23. package/examples/multi.ts +5 -5
  24. package/examples/newspaper.ts +98 -104
  25. package/examples/number.ts +6 -5
  26. package/examples/raffle.ts +11 -12
  27. package/examples/river-crossing.ts +140 -0
  28. package/examples/sandbox.ts +1 -1
  29. package/examples/simple.ts +5 -3
  30. package/examples/summary.ts +121 -0
  31. package/examples/support.ts +6 -6
  32. package/examples/ticTacToe.ts +86 -45
  33. package/examples/todo.ts +7 -7
  34. package/examples/tutor.ts +15 -15
  35. package/examples/verify.ts +3 -3
  36. package/examples/weather.ts +6 -9
  37. package/examples/wiki.ts +27 -8
  38. package/examples/word.ts +16 -11
  39. package/package.json +16 -11
  40. package/readme.md +1 -1
  41. package/src/agent-experimental.ts +1 -1
  42. package/src/agent.test.ts +243 -214
  43. package/src/agent.ts +286 -95
  44. package/src/decide.test.ts +276 -0
  45. package/src/decide.ts +163 -0
  46. package/src/index.ts +1 -1
  47. package/src/middleware.ts +91 -0
  48. package/src/mockModel.ts +47 -0
  49. package/src/planners/shortestPath.test.ts +94 -0
  50. package/src/planners/shortestPath.ts +177 -0
  51. package/src/planners/simple.ts +105 -0
  52. package/src/strategies/chain-of-note.ts +6 -55
  53. package/src/text.ts +51 -144
  54. package/src/types.ts +187 -212
  55. package/src/utils.ts +48 -4
  56. package/vitest.config.ts +9 -3
  57. package/src/adapters/vercel.ts +0 -7
  58. package/src/decision.test.ts +0 -179
  59. package/src/decision.ts +0 -84
  60. package/src/memory.ts +0 -25
  61. package/src/planners/shortestPathPlanner.ts +0 -22
  62. package/src/planners/simplePlanner.ts +0 -139
@@ -0,0 +1,57 @@
1
+ import { z } from 'zod';
2
+ import { createAgent } from '../src';
3
+ import { openai } from '@ai-sdk/openai';
4
+ import { getFromTerminal } from './helpers/helpers';
5
+
6
+ const agent = createAgent({
7
+ id: 'chatbot',
8
+ model: openai('gpt-4o-mini'),
9
+ events: {
10
+ 'agent.respond': z.object({
11
+ response: z.string().describe('The response from the agent'),
12
+ }),
13
+ 'agent.endConversation': z.object({}).describe('Stop the conversation'),
14
+ },
15
+ context: {
16
+ userMessage: z.string(),
17
+ },
18
+ });
19
+
20
+ async function main() {
21
+ let status = 'listening';
22
+ let userMessage = '';
23
+
24
+ while (status !== 'finished') {
25
+ switch (status) {
26
+ case 'listening':
27
+ userMessage = await getFromTerminal('User:');
28
+ status = 'responding';
29
+ break;
30
+
31
+ case 'responding':
32
+ const decision = await agent.decide({
33
+ messages: agent.getMessages(),
34
+ goal: 'Respond to the user, unless they want to end the conversation.',
35
+ state: {
36
+ value: status,
37
+ context: {
38
+ userMessage: 'User says: ' + userMessage,
39
+ },
40
+ },
41
+ });
42
+
43
+ if (decision?.nextEvent?.type === 'agent.respond') {
44
+ console.log(`Agent: ${decision.nextEvent.response}`);
45
+ status = 'listening';
46
+ } else if (decision?.nextEvent?.type === 'agent.endConversation') {
47
+ status = 'finished';
48
+ }
49
+ break;
50
+ }
51
+ }
52
+
53
+ console.log('End of conversation.');
54
+ process.exit();
55
+ }
56
+
57
+ main().catch(console.error);
@@ -2,11 +2,11 @@ import { z } from 'zod';
2
2
  import { createAgent, fromDecision } from '../src';
3
3
  import { openai } from '@ai-sdk/openai';
4
4
  import { assign, createActor, log, setup } from 'xstate';
5
- import { getFromTerminal } from './helpers/helpers';
5
+ import { fromTerminal } from './helpers/helpers';
6
6
 
7
7
  const agent = createAgent({
8
- name: 'chatbot',
9
- model: openai('gpt-4-turbo'),
8
+ id: 'chatbot',
9
+ model: openai('gpt-4o-mini'),
10
10
  events: {
11
11
  'agent.respond': z.object({
12
12
  response: z.string().describe('The response from the agent'),
@@ -20,7 +20,7 @@ const agent = createAgent({
20
20
 
21
21
  const machine = setup({
22
22
  types: agent.types,
23
- actors: { agent: fromDecision(agent), getFromTerminal },
23
+ actors: { getFromTerminal: fromTerminal },
24
24
  }).createMachine({
25
25
  initial: 'listening',
26
26
  context: {
@@ -33,26 +33,16 @@ const machine = setup({
33
33
  input: 'User:',
34
34
  onDone: {
35
35
  actions: assign({
36
- userMessage: (x) => x.event.output,
36
+ userMessage: ({ event }) => event.output,
37
37
  }),
38
38
  target: 'responding',
39
39
  },
40
40
  },
41
41
  },
42
42
  responding: {
43
- invoke: {
44
- src: 'agent',
45
- input: (x) => ({
46
- context: {
47
- userMessage: 'User says: ' + x.context.userMessage,
48
- },
49
- messages: agent.getMessages(),
50
- goal: 'Respond to the user, unless they want to end the conversation.',
51
- }),
52
- },
53
43
  on: {
54
44
  'agent.respond': {
55
- actions: [log((x) => `Agent: ${x.event.response}`)],
45
+ actions: log(({ event }) => `Agent: ${event.response}`),
56
46
  target: 'listening',
57
47
  },
58
48
  'agent.endConversation': 'finished',
@@ -68,4 +58,9 @@ const machine = setup({
68
58
  },
69
59
  });
70
60
 
71
- createActor(machine).start();
61
+ const actor = createActor(machine).start();
62
+
63
+ agent.interact(actor, () => ({
64
+ goal: 'Respond to the user, unless they want to end the conversation.',
65
+ messages: agent.getMessages(),
66
+ }));
package/examples/cot.ts CHANGED
@@ -2,11 +2,11 @@ import { z } from 'zod';
2
2
  import { createAgent, fromDecision } from '../src';
3
3
  import { openai } from '@ai-sdk/openai';
4
4
  import { assign, createActor, log, setup } from 'xstate';
5
- import { getFromTerminal } from './helpers/helpers';
5
+ import { fromTerminal } from './helpers/helpers';
6
6
 
7
7
  const agent = createAgent({
8
- name: 'chain-of-thought',
9
- model: openai('gpt-4o'),
8
+ id: 'chain-of-thought',
9
+ model: openai('gpt-4o-mini'),
10
10
  events: {
11
11
  'agent.think': z.object({
12
12
  thought: z
@@ -25,7 +25,7 @@ const agent = createAgent({
25
25
 
26
26
  const machine = setup({
27
27
  types: agent.types,
28
- actors: { agent: fromDecision(agent), getFromTerminal },
28
+ actors: { getFromTerminal: fromTerminal },
29
29
  }).createMachine({
30
30
  initial: 'asking',
31
31
  context: {
@@ -39,26 +39,19 @@ const machine = setup({
39
39
  input: 'What would you like to ask?',
40
40
  onDone: {
41
41
  actions: assign({
42
- question: (x) => x.event.output,
42
+ question: ({ event }) => event.output,
43
43
  }),
44
44
  target: 'thinking',
45
45
  },
46
46
  },
47
47
  },
48
48
  thinking: {
49
- invoke: {
50
- src: 'agent',
51
- input: (x) => ({
52
- context: x.context,
53
- goal: 'Answer the question. Think step-by-step.',
54
- }),
55
- },
56
49
  on: {
57
50
  'agent.think': {
58
51
  actions: [
59
- log((x) => x.event.thought),
52
+ log(({ event }) => `Thought: ${event.thought}`),
60
53
  assign({
61
- thought: (x) => x.event.thought,
54
+ thought: ({ event }) => event.thought,
62
55
  }),
63
56
  ],
64
57
  target: 'answering',
@@ -66,16 +59,9 @@ const machine = setup({
66
59
  },
67
60
  },
68
61
  answering: {
69
- invoke: {
70
- src: 'agent',
71
- input: (x) => ({
72
- context: x.context,
73
- goal: 'Answer the question',
74
- }),
75
- },
76
62
  on: {
77
63
  'agent.answer': {
78
- actions: [log((x) => x.event.answer)],
64
+ actions: log(({ event }) => `Answer: ${event.answer}`),
79
65
  target: 'answered',
80
66
  },
81
67
  },
@@ -86,4 +72,21 @@ const machine = setup({
86
72
  },
87
73
  });
88
74
 
89
- createActor(machine).start();
75
+ const actor = createActor(machine).start();
76
+
77
+ agent.interact(actor, (obs) => {
78
+ if (obs.state.matches('thinking')) {
79
+ return {
80
+ goal: 'Think step-by-step about how you would answer the question',
81
+ context: obs.state.context,
82
+ messages: agent.getMessages(),
83
+ };
84
+ }
85
+ if (obs.state.matches('answering')) {
86
+ return {
87
+ goal: 'Answer the question',
88
+ context: obs.state.context,
89
+ messages: agent.getMessages(),
90
+ };
91
+ }
92
+ });
@@ -0,0 +1,107 @@
1
+ import { createAgent, fromDecision } from '../src';
2
+ import { assign, createActor, setup } from 'xstate';
3
+ import { openai } from '@ai-sdk/openai';
4
+ import { z } from 'zod';
5
+
6
+ // Create customer service agent
7
+ const customerServiceAgent = createAgent({
8
+ id: 'customer-service',
9
+ model: openai('gpt-4o'),
10
+ events: {
11
+ 'agent.respond': z.object({
12
+ response: z
13
+ .string()
14
+ .describe('The response from the customer service agent'),
15
+ }),
16
+ },
17
+ description: 'You are a customer service agent for an airline.',
18
+ });
19
+
20
+ // Create simulated customer agent
21
+ const customerAgent = createAgent({
22
+ id: 'customer',
23
+ model: openai('gpt-4o-mini'),
24
+ events: {
25
+ 'agent.respond': z.object({
26
+ response: z.string().describe('The response from the customer'),
27
+ }),
28
+ 'agent.finish': z.object({}).describe('End the conversation'),
29
+ },
30
+ description: `You are Harrison, a customer trying to get a refund for a trip to Alaska.
31
+ You want them to give you ALL the money back. Be extremely persistent. This trip happened 5 years ago.
32
+ If you have nothing more to add to the conversation, send agent.finish event.`,
33
+ });
34
+
35
+ const machine = setup({
36
+ types: {
37
+ context: {} as {
38
+ messages: string[];
39
+ },
40
+ events: {} as
41
+ | typeof customerServiceAgent.types.events
42
+ | typeof customerAgent.types.events,
43
+ },
44
+ actors: {
45
+ customerService: fromDecision(customerServiceAgent),
46
+ customer: fromDecision(customerAgent),
47
+ },
48
+ }).createMachine({
49
+ initial: 'customerService',
50
+ context: {
51
+ messages: [],
52
+ },
53
+ states: {
54
+ customerService: {
55
+ invoke: {
56
+ src: 'customerService',
57
+ input: ({ context }) => ({
58
+ goal: 'Respond to the customer message',
59
+ context,
60
+ }),
61
+ },
62
+ on: {
63
+ 'agent.respond': {
64
+ target: 'customer',
65
+ actions: assign({
66
+ messages: ({ context, event }) => [
67
+ ...context.messages,
68
+ event.response,
69
+ ],
70
+ }),
71
+ },
72
+ },
73
+ },
74
+ customer: {
75
+ invoke: {
76
+ src: 'customer',
77
+ input: ({ context }) => ({
78
+ goal: 'Respond to the customer service agent, or finish the conversation if you have nothing more to add.',
79
+ context,
80
+ }),
81
+ },
82
+ on: {
83
+ 'agent.respond': {
84
+ target: 'customerService',
85
+ actions: assign({
86
+ messages: ({ context, event }) => [
87
+ ...context.messages,
88
+ event.response,
89
+ ],
90
+ }),
91
+ },
92
+ 'agent.finish': 'done',
93
+ },
94
+ },
95
+ done: {
96
+ type: 'final',
97
+ },
98
+ },
99
+ });
100
+
101
+ const actor = createActor(machine);
102
+ actor.subscribe((state) => {
103
+ console.log('State:', state.value);
104
+ console.log('Messages:', state.context.messages);
105
+ });
106
+
107
+ actor.start();
package/examples/email.ts CHANGED
@@ -2,11 +2,11 @@ import { z } from 'zod';
2
2
  import { createAgent, fromDecision } from '../src';
3
3
  import { openai } from '@ai-sdk/openai';
4
4
  import { assign, createActor, setup } from 'xstate';
5
- import { getFromTerminal } from './helpers/helpers';
5
+ import { fromTerminal } from './helpers/helpers';
6
6
 
7
7
  const agent = createAgent({
8
- name: 'email',
9
- model: openai('gpt-4'),
8
+ id: 'email',
9
+ model: openai('gpt-4o-mini'),
10
10
  events: {
11
11
  askForClarification: z.object({
12
12
  questions: z.array(z.string()).describe('The questions to ask the agent'),
@@ -15,6 +15,14 @@ const agent = createAgent({
15
15
  email: z.string().describe('The email to submit'),
16
16
  }),
17
17
  },
18
+ context: {
19
+ email: z.string().describe('The email to respond to'),
20
+ instructions: z.string().describe('The instructions for the email'),
21
+ clarifications: z
22
+ .array(z.string())
23
+ .describe('The clarifications to the email'),
24
+ replyEmail: z.string().nullable().describe('The email to submit'),
25
+ },
18
26
  });
19
27
 
20
28
  const machine = setup({
@@ -24,39 +32,22 @@ const machine = setup({
24
32
  email: string;
25
33
  instructions: string;
26
34
  },
27
- context: {} as {
28
- email: string;
29
- instructions: string;
30
- clarifications: string[];
31
- replyEmail: string | null;
32
- },
35
+ context: agent.types.context,
33
36
  },
34
- actors: { agent: fromDecision(agent), getFromTerminal },
37
+ actors: { getFromTerminal: fromTerminal },
35
38
  }).createMachine({
36
39
  initial: 'checking',
37
- context: (x) => ({
38
- email: x.input.email,
39
- instructions: x.input.instructions,
40
+ context: ({ input }) => ({
41
+ email: input.email,
42
+ instructions: input.instructions,
40
43
  clarifications: [],
41
44
  replyEmail: null,
42
45
  }),
43
46
  states: {
44
47
  checking: {
45
- invoke: {
46
- src: 'agent',
47
- input: (x) => ({
48
- context: {
49
- email: x.context.email,
50
- instructions: x.context.instructions,
51
- clarifications: x.context.clarifications,
52
- },
53
- messages: agent.getMessages(),
54
- goal: 'Respond to the email given the instructions and the provided clarifications. If not enough information is provided, ask for clarification. Otherwise, if you are absolutely sure that there is no ambiguous or missing information, create and submit a response email.',
55
- }),
56
- },
57
48
  on: {
58
49
  askForClarification: {
59
- actions: (x) => console.log(x.event.questions.join('\n')),
50
+ actions: ({ event }) => console.log(event.questions.join('\n')),
60
51
  target: 'clarifying',
61
52
  },
62
53
  submitEmail: {
@@ -70,25 +61,14 @@ const machine = setup({
70
61
  input: `Please provide answers to the questions above`,
71
62
  onDone: {
72
63
  actions: assign({
73
- clarifications: (x) =>
74
- x.context.clarifications.concat(x.event.output),
64
+ clarifications: ({ context, event }) =>
65
+ context.clarifications.concat(event.output),
75
66
  }),
76
67
  target: 'checking',
77
68
  },
78
69
  },
79
70
  },
80
71
  submitting: {
81
- invoke: {
82
- src: 'agent',
83
- input: ({ context }) => ({
84
- context: {
85
- email: context.email,
86
- instructions: context.instructions,
87
- clarifications: context.clarifications,
88
- },
89
- goal: `Create and submit an email based on the instructions.`,
90
- }),
91
- },
92
72
  on: {
93
73
  submitEmail: {
94
74
  actions: assign({
@@ -100,7 +80,7 @@ const machine = setup({
100
80
  },
101
81
  done: {
102
82
  type: 'final',
103
- entry: (x) => console.log(x.context.replyEmail),
83
+ entry: ({ context }) => console.log(context.replyEmail),
104
84
  },
105
85
  },
106
86
  exit: () => {
@@ -109,10 +89,26 @@ const machine = setup({
109
89
  },
110
90
  });
111
91
 
112
- createActor(machine, {
92
+ const actor = createActor(machine, {
113
93
  input: {
114
94
  email: 'That sounds great! When are you available?',
115
95
  instructions:
116
96
  'Tell them exactly when I am available. Address them by his full (first and last) name.',
117
97
  },
118
98
  }).start();
99
+
100
+ agent.interact(actor, ({ state }) => {
101
+ if (state.matches('checking')) {
102
+ return {
103
+ goal: 'Respond to the email given the instructions and the provided clarifications. If not enough information is provided, ask for clarification. Otherwise, if you are absolutely sure that there is no ambiguous or missing information, create and submit a response email.',
104
+ context: state.context,
105
+ };
106
+ }
107
+
108
+ if (state.matches('submitting')) {
109
+ return {
110
+ goal: 'Create and submit an email based on the instructions.',
111
+ context: state.context,
112
+ };
113
+ }
114
+ });
@@ -4,8 +4,8 @@ import { openai } from '@ai-sdk/openai';
4
4
  import { assign, createActor, setup } from 'xstate';
5
5
 
6
6
  const agent = createAgent({
7
- name: 'example',
8
- model: openai('gpt-4-turbo'),
7
+ id: 'example',
8
+ model: openai('gpt-4o-mini'),
9
9
  events: {
10
10
  'agent.englishSummary': z.object({
11
11
  text: z.string().describe('The summary in English'),
@@ -32,8 +32,8 @@ const machine = setup({
32
32
  invoke: [
33
33
  {
34
34
  src: 'summarizer',
35
- input: (x) => ({
36
- context: x.context,
35
+ input: ({ context }) => ({
36
+ context,
37
37
  prompt:
38
38
  'Summarize the patient visit in a single sentence. The summary should be in English.',
39
39
  }),
@@ -45,8 +45,8 @@ const machine = setup({
45
45
  },
46
46
  {
47
47
  src: 'summarizer',
48
- input: (x) => ({
49
- context: x.context,
48
+ input: ({ context }) => ({
49
+ context,
50
50
  prompt:
51
51
  'Summarize the patient visit in a single sentence. The summary should be in Spanish.',
52
52
  }),
@@ -0,0 +1,66 @@
1
+ import { openai } from '@ai-sdk/openai';
2
+ import { createAgent, fromDecision } from '../src';
3
+ import { assign, createActor, createMachine, fromPromise } from 'xstate';
4
+ import { z } from 'zod';
5
+ import { fromTerminal } from './helpers/helpers';
6
+
7
+ const agent = createAgent({
8
+ model: openai('gpt-4o-mini'),
9
+ events: {
10
+ getTime: z.object({}).describe('Get the current time'),
11
+ other: z.object({}).describe('Do something else'),
12
+ },
13
+ });
14
+
15
+ const machine = createMachine({
16
+ initial: 'start',
17
+ context: {
18
+ question: null,
19
+ },
20
+ states: {
21
+ start: {
22
+ invoke: {
23
+ src: fromTerminal,
24
+ input: 'What do you want to do?',
25
+ onDone: {
26
+ actions: assign({
27
+ question: ({ event }) => event.output,
28
+ }),
29
+ target: 'deciding',
30
+ },
31
+ },
32
+ },
33
+ deciding: {
34
+ invoke: {
35
+ src: fromDecision(agent),
36
+ input: ({ context }) => ({
37
+ goal: 'Satisfy the user question',
38
+ context,
39
+ }),
40
+ },
41
+ on: {
42
+ getTime: 'gettingTime',
43
+ other: 'other',
44
+ },
45
+ },
46
+ gettingTime: {
47
+ invoke: {
48
+ src: fromPromise(async () => {
49
+ console.log('Time:', new Date().toLocaleTimeString());
50
+ }),
51
+ onDone: 'start',
52
+ },
53
+ },
54
+ other: {
55
+ entry: () =>
56
+ console.log(
57
+ 'You want me to do something else. I can only tell the time.'
58
+ ),
59
+ after: {
60
+ 1000: 'start',
61
+ },
62
+ },
63
+ },
64
+ });
65
+
66
+ createActor(machine).start();
package/examples/goal.ts CHANGED
@@ -2,11 +2,11 @@ import { z } from 'zod';
2
2
  import { createAgent, fromDecision } from '../src';
3
3
  import { openai } from '@ai-sdk/openai';
4
4
  import { assign, createActor, log, setup } from 'xstate';
5
- import { getFromTerminal } from './helpers/helpers';
5
+ import { fromTerminal } from './helpers/helpers';
6
6
 
7
7
  const agent = createAgent({
8
- name: 'goal',
9
- model: openai('gpt-4-turbo'),
8
+ id: 'goal',
9
+ model: openai('gpt-4o-mini'),
10
10
  events: {
11
11
  'agent.createGoal': z.object({
12
12
  goal: z.string().describe('The goal for the conversation'),
@@ -27,7 +27,7 @@ const machine = setup({
27
27
  },
28
28
  events: agent.types.events,
29
29
  },
30
- actors: { decider, getFromTerminal },
30
+ actors: { decider, getFromTerminal: fromTerminal },
31
31
  }).createMachine({
32
32
  initial: 'gettingQuestion',
33
33
  context: {
@@ -50,11 +50,11 @@ const machine = setup({
50
50
  makingGoal: {
51
51
  invoke: {
52
52
  src: 'decider',
53
- input: {
54
- context: true,
53
+ input: ({ context }) => ({
54
+ context,
55
55
  goal: 'Determine what the user wants to accomplish. What is their ideal goal state? ',
56
56
  maxRetries: 3,
57
- },
57
+ }),
58
58
  },
59
59
  on: {
60
60
  'agent.createGoal': {
@@ -62,7 +62,7 @@ const machine = setup({
62
62
  assign({
63
63
  goal: ({ event }) => event.goal,
64
64
  }),
65
- log((x) => x.event),
65
+ log(({ event }) => `Goal: ${event.goal}`),
66
66
  ],
67
67
  target: 'responding',
68
68
  },
@@ -71,15 +71,15 @@ const machine = setup({
71
71
  responding: {
72
72
  invoke: {
73
73
  src: 'decider',
74
- input: {
75
- context: true,
74
+ input: ({ context }) => ({
75
+ context,
76
76
  goal: 'Answer the question to achieve the stated goal, unless the goal is impossible to achieve.',
77
77
  maxRetries: 3,
78
- },
78
+ }),
79
79
  },
80
80
  on: {
81
81
  'agent.respond': {
82
- actions: log(({ event }) => event),
82
+ actions: log(({ event }) => `Response: ${event.response}`),
83
83
  },
84
84
  },
85
85
  },
@@ -1,17 +1,29 @@
1
1
  import { fromPromise } from 'xstate';
2
2
 
3
- export const getFromTerminal = fromPromise<string, string>(
4
- async ({ input }) => {
5
- const topic = await new Promise<string>((res) => {
6
- console.log(input + '\n');
7
- const listener = (data: Buffer) => {
8
- const result = data.toString().trim();
9
- process.stdin.off('data', listener);
10
- res(result);
11
- };
12
- process.stdin.on('data', listener);
13
- });
3
+ export const fromTerminal = fromPromise<string, string>(async ({ input }) => {
4
+ const topic = await new Promise<string>((res) => {
5
+ console.log(input + '\n');
6
+ const listener = (data: Buffer) => {
7
+ const result = data.toString().trim();
8
+ process.stdin.off('data', listener);
9
+ res(result);
10
+ };
11
+ process.stdin.on('data', listener);
12
+ });
14
13
 
15
- return topic;
16
- }
17
- );
14
+ return topic;
15
+ });
16
+
17
+ export async function getFromTerminal(msg: string) {
18
+ const topic = await new Promise<string>((res) => {
19
+ console.log(msg + '\n');
20
+ const listener = (data: Buffer) => {
21
+ const result = data.toString().trim();
22
+ process.stdin.off('data', listener);
23
+ res(result);
24
+ };
25
+ process.stdin.on('data', listener);
26
+ });
27
+
28
+ return topic;
29
+ }