@statelyai/agent 0.0.1 → 0.0.3
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/.changeset/README.md +8 -0
- package/.changeset/config.json +11 -0
- package/.env.template +6 -0
- package/.github/actions/ci-setup/action.yml +24 -0
- package/.github/workflows/release.yml +35 -0
- package/.vscode/launch.json +17 -0
- package/CHANGELOG.md +13 -0
- package/dist/index.d.ts +66 -6
- package/dist/index.js +86 -11
- package/examples/helpers/helpers.ts +17 -0
- package/examples/helpers/loader.ts +32 -0
- package/examples/helpers/runner.ts +27 -0
- package/examples/joke.ts +181 -152
- package/examples/ticTacToe.ts +133 -121
- package/examples/weather.ts +147 -0
- package/package.json +22 -10
- package/readme.md +41 -0
- package/src/index.ts +1 -0
- package/src/openai.ts +154 -12
- package/src/utils.ts +59 -1
- package/dist/index.d.mts +0 -3
- package/dist/index.mjs +0 -7
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Changesets
|
|
2
|
+
|
|
3
|
+
Hello and welcome! This folder has been automatically generated by `@changesets/cli`, a build tool that works
|
|
4
|
+
with multi-package repos, or single-package repos to help you version and publish your code. You can
|
|
5
|
+
find the full documentation for it [in our repository](https://github.com/changesets/changesets)
|
|
6
|
+
|
|
7
|
+
We have a quick list of common questions to get you started engaging with this project in
|
|
8
|
+
[our documentation](https://github.com/changesets/changesets/blob/main/docs/common-questions.md)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://unpkg.com/@changesets/config@3.0.0/schema.json",
|
|
3
|
+
"changelog": ["@changesets/changelog-github", { "repo": "statelyai/agent" }],
|
|
4
|
+
"commit": false,
|
|
5
|
+
"fixed": [],
|
|
6
|
+
"linked": [],
|
|
7
|
+
"access": "restricted",
|
|
8
|
+
"baseBranch": "main",
|
|
9
|
+
"updateInternalDependencies": "patch",
|
|
10
|
+
"ignore": []
|
|
11
|
+
}
|
package/.env.template
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: Setup Workflow
|
|
2
|
+
description: Composite action that sets up pnpm
|
|
3
|
+
runs:
|
|
4
|
+
using: 'composite'
|
|
5
|
+
steps:
|
|
6
|
+
- uses: pnpm/action-setup@v2
|
|
7
|
+
- uses: actions/setup-node@v4
|
|
8
|
+
with:
|
|
9
|
+
node-version: 20.x
|
|
10
|
+
|
|
11
|
+
- name: Get pnpm store directory
|
|
12
|
+
shell: bash
|
|
13
|
+
id: pnpm-cache
|
|
14
|
+
run: |
|
|
15
|
+
echo "pnpm_cache_dir=$(pnpm store path)" >> $GITHUB_OUTPUT
|
|
16
|
+
- uses: actions/cache@v4
|
|
17
|
+
name: Setup pnpm cache
|
|
18
|
+
with:
|
|
19
|
+
path: ${{ steps.pnpm-cache.outputs.pnpm_cache_dir }}
|
|
20
|
+
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
|
|
21
|
+
restore-keys: |
|
|
22
|
+
${{ runner.os }}-pnpm-store-
|
|
23
|
+
- run: pnpm install
|
|
24
|
+
shell: bash
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches:
|
|
6
|
+
- main
|
|
7
|
+
|
|
8
|
+
concurrency: ${{ github.workflow }}-${{ github.ref }}
|
|
9
|
+
|
|
10
|
+
permissions: {}
|
|
11
|
+
jobs:
|
|
12
|
+
release:
|
|
13
|
+
permissions:
|
|
14
|
+
contents: write # to create release (changesets/action)
|
|
15
|
+
issues: write # to post issue comments (changesets/action)
|
|
16
|
+
pull-requests: write # to create pull request (changesets/action)
|
|
17
|
+
|
|
18
|
+
if: github.repository == 'statelyai/agent'
|
|
19
|
+
|
|
20
|
+
timeout-minutes: 20
|
|
21
|
+
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
|
|
24
|
+
steps:
|
|
25
|
+
- uses: actions/checkout@v3
|
|
26
|
+
- uses: ./.github/actions/ci-setup
|
|
27
|
+
|
|
28
|
+
- name: Create Release Pull Request or Publish to npm
|
|
29
|
+
uses: changesets/action@v1
|
|
30
|
+
with:
|
|
31
|
+
publish: pnpm run release
|
|
32
|
+
version: pnpm run version
|
|
33
|
+
env:
|
|
34
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
35
|
+
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
{
|
|
2
|
+
// Use IntelliSense to learn about possible attributes.
|
|
3
|
+
// Hover to view descriptions of existing attributes.
|
|
4
|
+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
5
|
+
"version": "0.2.0",
|
|
6
|
+
"configurations": [
|
|
7
|
+
{
|
|
8
|
+
"type": "node",
|
|
9
|
+
"request": "launch",
|
|
10
|
+
"name": "Launch Program",
|
|
11
|
+
"skipFiles": ["<node_internals>/**"],
|
|
12
|
+
"program": "${file}",
|
|
13
|
+
"preLaunchTask": "tsc: build - tsconfig.json",
|
|
14
|
+
"outFiles": ["${workspaceFolder}/**/*.js"]
|
|
15
|
+
}
|
|
16
|
+
]
|
|
17
|
+
}
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# @statelyai/agent
|
|
2
|
+
|
|
3
|
+
## 0.0.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#1](https://github.com/statelyai/agent/pull/1) [`3dc2880`](https://github.com/statelyai/agent/commit/3dc28809a7ffd915a69d9f3374531c31fc1ee357) Thanks [@mellson](https://github.com/mellson)! - Adds a convenient way to run the examples with `pnpm example ${exampleName}`. If no example name is provided, the script will print the available examples. Also, adds a fun little loading animation to the joke example.
|
|
8
|
+
|
|
9
|
+
## 0.0.2
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- e125728: Added `createAgent(...)`
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,39 @@
|
|
|
1
|
-
import * as xstate from 'xstate';
|
|
2
|
-
import { AnyEventObject } from 'xstate';
|
|
3
1
|
import OpenAI from 'openai';
|
|
2
|
+
import { Prop, PromiseActorLogic, ObservableActorLogic, AnyEventObject, Values } from 'xstate';
|
|
3
|
+
import { JSONSchema7 } from 'json-schema-to-ts/lib/types/definitions';
|
|
4
|
+
import { FromSchema } from 'json-schema-to-ts';
|
|
5
|
+
import { ChatCompletionCreateParamsNonStreaming } from 'openai/resources';
|
|
6
|
+
import { ChatCompletionCreateParamsBase, ChatCompletionCreateParamsStreaming } from 'openai/resources/chat/completions';
|
|
7
|
+
|
|
8
|
+
type EventSchemas = {
|
|
9
|
+
[key: string]: {
|
|
10
|
+
description?: string;
|
|
11
|
+
properties?: {
|
|
12
|
+
[key: string]: JSONSchema7;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
};
|
|
16
|
+
interface ContextSchema {
|
|
17
|
+
[key: string]: JSONSchema7;
|
|
18
|
+
}
|
|
19
|
+
type ConvertToJSONSchemas<T> = {
|
|
20
|
+
[K in keyof T]: {
|
|
21
|
+
properties: {
|
|
22
|
+
type: {
|
|
23
|
+
const: K;
|
|
24
|
+
};
|
|
25
|
+
};
|
|
26
|
+
type: 'object';
|
|
27
|
+
required: Array<keyof Prop<T[K], 'properties'> | 'type'>;
|
|
28
|
+
additionalProperties: false;
|
|
29
|
+
} & T[K];
|
|
30
|
+
} & {};
|
|
31
|
+
type ConvertContextToJSONSchema<T extends ContextSchema> = {
|
|
32
|
+
type: 'object';
|
|
33
|
+
properties: T;
|
|
34
|
+
readonly required: Array<keyof T & string>;
|
|
35
|
+
additionalProperties: false;
|
|
36
|
+
};
|
|
4
37
|
|
|
5
38
|
/**
|
|
6
39
|
* Creates [promise actor logic](https://stately.ai/docs/promise-actors) that uses the OpenAI API to generate a completion.
|
|
@@ -9,20 +42,47 @@ import OpenAI from 'openai';
|
|
|
9
42
|
* @param inputFn A function that maps arbitrary input to OpenAI chat completion input.
|
|
10
43
|
*
|
|
11
44
|
*/
|
|
12
|
-
declare function fromChatCompletion<TInput>(openai: OpenAI, inputFn: (input: TInput) => OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming):
|
|
45
|
+
declare function fromChatCompletion<TInput>(openai: OpenAI, agentSettings: CreateAgentOutput<any>, inputFn: (input: TInput) => string | OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming): PromiseActorLogic<OpenAI.Chat.Completions.ChatCompletion, TInput>;
|
|
13
46
|
/**
|
|
14
47
|
* Creates [observable actor logic](https://stately.ai/docs/observable-actors) that uses the OpenAI API to generate a completion stream.
|
|
15
48
|
*
|
|
16
49
|
* @param openai The OpenAI instance to use.
|
|
17
50
|
* @param inputFn A function that maps arbitrary input to OpenAI chat completion input.
|
|
18
51
|
*/
|
|
19
|
-
declare function fromChatCompletionStream<TInput>(openai: OpenAI, inputFn: (input: TInput) => OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming):
|
|
52
|
+
declare function fromChatCompletionStream<TInput>(openai: OpenAI, agentSettings: CreateAgentOutput<any>, inputFn: (input: TInput) => string | OpenAI.Chat.Completions.ChatCompletionCreateParamsStreaming): ObservableActorLogic<OpenAI.Chat.Completions.ChatCompletionChunk, TInput>;
|
|
20
53
|
/**
|
|
21
54
|
* Creates [promise actor logic](https://stately.ai/docs/promise-actors) that passes the next possible transitions as functions to [OpenAI tool calls](https://platform.openai.com/docs/guides/function-calling) and returns an array of potential next events.
|
|
22
55
|
*
|
|
23
56
|
* @param openai The OpenAI instance to use.
|
|
24
57
|
* @param inputFn A function that maps arbitrary input to OpenAI chat completion input.
|
|
25
58
|
*/
|
|
26
|
-
declare function fromEventChoice<TInput>(openai: OpenAI, inputFn: (input: TInput) => OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming
|
|
59
|
+
declare function fromEventChoice<TInput>(openai: OpenAI, agentSettings: CreateAgentOutput<any>, inputFn: (input: TInput) => string | OpenAI.Chat.Completions.ChatCompletionCreateParamsNonStreaming, options?: {
|
|
60
|
+
/**
|
|
61
|
+
* Immediately execute sending the event to the parent actor.
|
|
62
|
+
* @default false
|
|
63
|
+
*/
|
|
64
|
+
execute?: boolean;
|
|
65
|
+
}): PromiseActorLogic<AnyEventObject[] | undefined, TInput>;
|
|
66
|
+
interface CreateAgentOutput<T extends {
|
|
67
|
+
model: ChatCompletionCreateParamsBase['model'];
|
|
68
|
+
context: ContextSchema;
|
|
69
|
+
events: EventSchemas;
|
|
70
|
+
}> {
|
|
71
|
+
model: T['model'];
|
|
72
|
+
schemas: T;
|
|
73
|
+
types: {
|
|
74
|
+
context: FromSchema<ConvertContextToJSONSchema<T['context']>>;
|
|
75
|
+
events: FromSchema<Values<ConvertToJSONSchemas<T['events']>>>;
|
|
76
|
+
};
|
|
77
|
+
fromEvent: <TInput>(inputFn: (input: TInput) => string | ChatCompletionCreateParamsNonStreaming) => PromiseActorLogic<FromSchema<Values<ConvertToJSONSchemas<T['events']>>>[] | undefined, TInput>;
|
|
78
|
+
fromEventChoice: <TInput>(inputFn: (input: TInput) => string | ChatCompletionCreateParamsNonStreaming) => PromiseActorLogic<FromSchema<Values<ConvertToJSONSchemas<T['events']>>>[] | undefined, TInput>;
|
|
79
|
+
fromChatCompletion: <TInput>(inputFn: (input: TInput) => string | ChatCompletionCreateParamsNonStreaming) => PromiseActorLogic<OpenAI.Chat.Completions.ChatCompletion, TInput>;
|
|
80
|
+
fromChatCompletionStream: <TInput>(inputFn: (input: TInput) => string | ChatCompletionCreateParamsStreaming) => ObservableActorLogic<OpenAI.Chat.Completions.ChatCompletionChunk, TInput>;
|
|
81
|
+
}
|
|
82
|
+
declare function createAgent<T extends {
|
|
83
|
+
model: ChatCompletionCreateParamsBase['model'];
|
|
84
|
+
context: ContextSchema;
|
|
85
|
+
events: EventSchemas;
|
|
86
|
+
}>(openai: OpenAI, settings: T): CreateAgentOutput<T>;
|
|
27
87
|
|
|
28
|
-
export { fromChatCompletion, fromChatCompletionStream, fromEventChoice };
|
|
88
|
+
export { createAgent, fromChatCompletion, fromChatCompletionStream, fromEventChoice };
|
package/dist/index.js
CHANGED
|
@@ -20,6 +20,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var src_exports = {};
|
|
22
22
|
__export(src_exports, {
|
|
23
|
+
createAgent: () => createAgent,
|
|
23
24
|
fromChatCompletion: () => fromChatCompletion,
|
|
24
25
|
fromChatCompletionStream: () => fromChatCompletionStream,
|
|
25
26
|
fromEventChoice: () => fromEventChoice
|
|
@@ -35,25 +36,61 @@ function getAllTransitions(state) {
|
|
|
35
36
|
const transitions = nodes.map((node) => [...node.transitions.values()]).flat(2);
|
|
36
37
|
return transitions;
|
|
37
38
|
}
|
|
39
|
+
function createEventSchemas(eventSchemaMap) {
|
|
40
|
+
const resolvedEventSchemaMap = {};
|
|
41
|
+
for (const [key, schema] of Object.entries(eventSchemaMap)) {
|
|
42
|
+
resolvedEventSchemaMap[key] = {
|
|
43
|
+
type: "object",
|
|
44
|
+
required: ["type"],
|
|
45
|
+
properties: {
|
|
46
|
+
type: {
|
|
47
|
+
const: key
|
|
48
|
+
},
|
|
49
|
+
...schema.properties
|
|
50
|
+
},
|
|
51
|
+
additionalProperties: false,
|
|
52
|
+
...schema
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
return resolvedEventSchemaMap;
|
|
56
|
+
}
|
|
38
57
|
|
|
39
58
|
// src/openai.ts
|
|
40
|
-
function fromChatCompletion(openai, inputFn) {
|
|
59
|
+
function fromChatCompletion(openai, agentSettings, inputFn) {
|
|
41
60
|
return (0, import_xstate.fromPromise)(
|
|
42
61
|
async ({ input }) => {
|
|
43
62
|
const openAiInput = inputFn(input);
|
|
44
|
-
const
|
|
63
|
+
const params = typeof openAiInput === "string" ? {
|
|
64
|
+
model: agentSettings.model,
|
|
65
|
+
messages: [
|
|
66
|
+
{
|
|
67
|
+
role: "user",
|
|
68
|
+
content: openAiInput
|
|
69
|
+
}
|
|
70
|
+
]
|
|
71
|
+
} : openAiInput;
|
|
72
|
+
const response = await openai.chat.completions.create(params);
|
|
45
73
|
return response;
|
|
46
74
|
}
|
|
47
75
|
);
|
|
48
76
|
}
|
|
49
|
-
function fromChatCompletionStream(openai, inputFn) {
|
|
77
|
+
function fromChatCompletionStream(openai, agentSettings, inputFn) {
|
|
50
78
|
return (0, import_xstate.fromObservable)(
|
|
51
79
|
({ input }) => {
|
|
52
80
|
const observers = /* @__PURE__ */ new Set();
|
|
53
81
|
(async () => {
|
|
54
82
|
const openAiInput = inputFn(input);
|
|
83
|
+
const resolvedParams = typeof openAiInput === "string" ? {
|
|
84
|
+
model: agentSettings.model,
|
|
85
|
+
messages: [
|
|
86
|
+
{
|
|
87
|
+
role: "user",
|
|
88
|
+
content: openAiInput
|
|
89
|
+
}
|
|
90
|
+
]
|
|
91
|
+
} : openAiInput;
|
|
55
92
|
const stream = await openai.chat.completions.create({
|
|
56
|
-
...
|
|
93
|
+
...resolvedParams,
|
|
57
94
|
stream: true
|
|
58
95
|
});
|
|
59
96
|
for await (const part of stream) {
|
|
@@ -76,9 +113,9 @@ function fromChatCompletionStream(openai, inputFn) {
|
|
|
76
113
|
}
|
|
77
114
|
);
|
|
78
115
|
}
|
|
79
|
-
function fromEventChoice(openai, inputFn) {
|
|
116
|
+
function fromEventChoice(openai, agentSettings, inputFn, options) {
|
|
80
117
|
return (0, import_xstate.fromPromise)(
|
|
81
|
-
async ({ input, self }) => {
|
|
118
|
+
async ({ input, self, system }) => {
|
|
82
119
|
const transitions = getAllTransitions(self._parent.getSnapshot());
|
|
83
120
|
const functionNameMapping = {};
|
|
84
121
|
const tools = transitions.filter((t) => {
|
|
@@ -90,34 +127,72 @@ function fromEventChoice(openai, inputFn) {
|
|
|
90
127
|
type: "function",
|
|
91
128
|
function: {
|
|
92
129
|
name,
|
|
93
|
-
description: t.description,
|
|
130
|
+
description: t.description ?? agentSettings.schemas.events[t.eventType]?.description,
|
|
94
131
|
parameters: {
|
|
95
132
|
type: "object",
|
|
96
|
-
properties: t.
|
|
133
|
+
properties: agentSettings.schemas.events[t.eventType]?.properties ?? {}
|
|
97
134
|
}
|
|
98
135
|
}
|
|
99
136
|
};
|
|
100
137
|
});
|
|
101
138
|
const openAiInput = inputFn(input);
|
|
139
|
+
const completionParams = typeof openAiInput === "string" ? {
|
|
140
|
+
model: agentSettings.model,
|
|
141
|
+
messages: [
|
|
142
|
+
{
|
|
143
|
+
role: "user",
|
|
144
|
+
content: openAiInput
|
|
145
|
+
}
|
|
146
|
+
]
|
|
147
|
+
} : openAiInput;
|
|
102
148
|
const completion = await openai.chat.completions.create({
|
|
103
|
-
...
|
|
149
|
+
...completionParams,
|
|
104
150
|
tools
|
|
105
151
|
});
|
|
106
152
|
const toolCalls = completion.choices[0]?.message.tool_calls;
|
|
107
153
|
if (toolCalls) {
|
|
108
|
-
|
|
154
|
+
const events = toolCalls.map((tc) => {
|
|
109
155
|
return {
|
|
110
156
|
type: functionNameMapping[tc.function.name],
|
|
111
157
|
...JSON.parse(tc.function.arguments)
|
|
112
158
|
};
|
|
113
159
|
});
|
|
160
|
+
if (options?.execute) {
|
|
161
|
+
events.forEach((event) => {
|
|
162
|
+
system._relay(self, self._parent, event);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
114
165
|
}
|
|
115
|
-
return
|
|
166
|
+
return void 0;
|
|
116
167
|
}
|
|
117
168
|
);
|
|
118
169
|
}
|
|
170
|
+
function createAgent(openai, settings) {
|
|
171
|
+
const agentSettings = {
|
|
172
|
+
model: settings.model,
|
|
173
|
+
schemas: {
|
|
174
|
+
context: {
|
|
175
|
+
type: "object",
|
|
176
|
+
properties: settings.context,
|
|
177
|
+
additionalProperties: false
|
|
178
|
+
},
|
|
179
|
+
events: createEventSchemas(settings.events)
|
|
180
|
+
},
|
|
181
|
+
types: {},
|
|
182
|
+
fromEvent: (input) => (
|
|
183
|
+
// @ts-ignore
|
|
184
|
+
fromEventChoice(openai, agentSettings, input, { execute: true })
|
|
185
|
+
),
|
|
186
|
+
// @ts-ignore infinitely deep
|
|
187
|
+
fromEventChoice: (input) => fromEventChoice(openai, agentSettings, input),
|
|
188
|
+
fromChatCompletion: (input) => fromChatCompletion(openai, agentSettings, input),
|
|
189
|
+
fromChatCompletionStream: (input) => fromChatCompletionStream(openai, agentSettings, input)
|
|
190
|
+
};
|
|
191
|
+
return agentSettings;
|
|
192
|
+
}
|
|
119
193
|
// Annotate the CommonJS export names for ESM import in node:
|
|
120
194
|
0 && (module.exports = {
|
|
195
|
+
createAgent,
|
|
121
196
|
fromChatCompletion,
|
|
122
197
|
fromChatCompletionStream,
|
|
123
198
|
fromEventChoice
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { fromPromise } from 'xstate';
|
|
2
|
+
|
|
3
|
+
export const getFromTerminal = fromPromise<string, string>(
|
|
4
|
+
async ({ input }) => {
|
|
5
|
+
const topic = await new Promise<string>((res) => {
|
|
6
|
+
console.log(input);
|
|
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
|
+
});
|
|
14
|
+
|
|
15
|
+
return topic;
|
|
16
|
+
}
|
|
17
|
+
);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// Adapted from https://stackoverflow.com/questions/34848505/how-to-make-a-loading-animation-in-console-application-written-in-javascript-or
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Create and display a loader in the console.
|
|
5
|
+
*
|
|
6
|
+
* @param {string} [text=""] Text to display after loader
|
|
7
|
+
* @param {array.<string>} [chars=["⠙", "⠘", "⠰", "⠴", "⠤", "⠦", "⠆", "⠃", "⠋", "⠉"]]
|
|
8
|
+
* Array of characters representing loader steps
|
|
9
|
+
* @param {number} [delay=100] Delay in ms between loader steps
|
|
10
|
+
* @example
|
|
11
|
+
* let loader = loadingAnimation("Loading…");
|
|
12
|
+
*
|
|
13
|
+
* // Stop loader after 1 second
|
|
14
|
+
* setTimeout(() => clearInterval(loader), 1000);
|
|
15
|
+
* @returns {number} An interval that can be cleared to stop the animation
|
|
16
|
+
*/
|
|
17
|
+
export function loadingAnimation(
|
|
18
|
+
text: string = '',
|
|
19
|
+
chars: Array<string> = ['⠙', '⠘', '⠰', '⠴', '⠤', '⠦', '⠆', '⠃', '⠋', '⠉'],
|
|
20
|
+
delay: number = 100
|
|
21
|
+
) {
|
|
22
|
+
let x = 0;
|
|
23
|
+
|
|
24
|
+
const i = setInterval(function () {
|
|
25
|
+
process.stdout.write('\r' + chars[x++] + ' ' + text);
|
|
26
|
+
x = x % chars.length;
|
|
27
|
+
}, delay);
|
|
28
|
+
|
|
29
|
+
return {
|
|
30
|
+
stop: () => clearInterval(i),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import dotenv from 'dotenv';
|
|
2
|
+
import { existsSync, readdirSync } from 'fs';
|
|
3
|
+
dotenv.config();
|
|
4
|
+
|
|
5
|
+
function showExamples() {
|
|
6
|
+
const exampleFiles = readdirSync('./examples', { withFileTypes: true });
|
|
7
|
+
exampleFiles.forEach((file) => {
|
|
8
|
+
if (file.isDirectory()) return;
|
|
9
|
+
const exampleName = file.name.split('.')[0];
|
|
10
|
+
console.log(`- ${exampleName}`);
|
|
11
|
+
});
|
|
12
|
+
process.exit();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const exampleParams = process.argv.slice(2);
|
|
16
|
+
if (exampleParams.length === 0) {
|
|
17
|
+
console.error('No example specified, you can choose from:');
|
|
18
|
+
showExamples();
|
|
19
|
+
}
|
|
20
|
+
const exampleName = exampleParams[0];
|
|
21
|
+
const filePath = `./examples/${exampleName}.ts`;
|
|
22
|
+
if (existsSync(filePath)) {
|
|
23
|
+
require(`../${exampleName}.ts`);
|
|
24
|
+
} else {
|
|
25
|
+
console.error(`Example ${exampleName} does not exist, you can choose from:`);
|
|
26
|
+
showExamples();
|
|
27
|
+
}
|