@fluxy-chat/create-fluxy-chat 0.3.0 → 0.5.0
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 +16 -0
- package/dist/index.js +105 -29
- package/package.json +4 -3
- package/readme.md +22 -16
- package/templates/full/.env.example +10 -0
- package/templates/full/README.md +66 -0
- package/templates/full/index.html +12 -0
- package/templates/full/package.json +29 -0
- package/templates/full/scripts/fluxy-dev.mjs +169 -0
- package/templates/full/scripts/fluxy-doctor.mjs +122 -0
- package/templates/full/scripts/fluxy-setup.mjs +323 -0
- package/templates/full/src/App.tsx +223 -0
- package/templates/full/src/index.css +242 -0
- package/templates/full/src/main.tsx +10 -0
- package/templates/full/src/vite-env.d.ts +16 -0
- package/templates/full/tsconfig.json +21 -0
- package/templates/full/vite.config.ts +7 -0
- package/templates/hr-feedback/.env.example +9 -0
- package/templates/hr-feedback/README.md +45 -0
- package/templates/hr-feedback/package.json +19 -0
- package/templates/hr-feedback/src/feedback.ts +71 -0
- package/templates/hr-feedback/src/index.ts +22 -0
- package/templates/hr-feedback/tsconfig.json +18 -0
- package/templates/hr-feedback/wrangler.toml +7 -0
- package/templates/minimal/README.md +1 -1
- package/templates/react/.env.example +4 -3
- package/templates/react/README.md +17 -7
- package/templates/react/src/App.tsx +116 -23
- package/templates/react/src/index.css +10 -0
- package/templates/react/src/vite-env.d.ts +3 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.5.0] - 2026-08-19
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `--full` / `--template full` — Vite app with realtime chat, `@assistant` invoke, and tool thread
|
|
8
|
+
- `--mode hosted` — guest JWT via `GET /demo/session` (no local wrangler)
|
|
9
|
+
- `--mode local` — provision against a local worker (`POST /dev/provision`)
|
|
10
|
+
- Template scripts: `pnpm setup`, `pnpm setup:hosted`, `pnpm doctor`, `pnpm dev`
|
|
11
|
+
- CLI next-steps link to `/onboarding?from=cli` (keep / import `.env`)
|
|
12
|
+
|
|
13
|
+
### Changed
|
|
14
|
+
|
|
15
|
+
- Interactive picker lists Full stack first
|
|
16
|
+
- README hero documents hosted vs local paths
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
// src/utils.ts
|
|
16
16
|
import fs from "fs";
|
|
17
17
|
import path from "path";
|
|
18
|
+
import { fileURLToPath } from "url";
|
|
18
19
|
var PACKAGE_NAME_PATTERN = /^[a-z0-9][a-z0-9._-]*$/;
|
|
19
20
|
var PACKAGE_MANAGERS = /* @__PURE__ */ new Set(["npm", "yarn", "pnpm"]);
|
|
20
21
|
function validateProjectName(value) {
|
|
@@ -30,7 +31,7 @@ function isPackageManager(value) {
|
|
|
30
31
|
return PACKAGE_MANAGERS.has(value);
|
|
31
32
|
}
|
|
32
33
|
function isAdapterType(value) {
|
|
33
|
-
return ["basic", "slack", "telegram", "discord", "web", "react"].includes(value);
|
|
34
|
+
return ["basic", "slack", "telegram", "discord", "web", "react", "hr-feedback", "full"].includes(value);
|
|
34
35
|
}
|
|
35
36
|
function detectPackageManagerFromLockfiles(cwd) {
|
|
36
37
|
if (fs.existsSync(path.join(cwd, "pnpm-lock.yaml"))) {
|
|
@@ -92,20 +93,19 @@ function copyDir(source, destination) {
|
|
|
92
93
|
}
|
|
93
94
|
}
|
|
94
95
|
function templatesDir() {
|
|
95
|
-
return path.resolve(
|
|
96
|
-
path.dirname(new URL(import.meta.url).pathname.replace(/^\//, "")),
|
|
97
|
-
"..",
|
|
98
|
-
"templates"
|
|
99
|
-
);
|
|
96
|
+
return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "templates");
|
|
100
97
|
}
|
|
101
98
|
|
|
102
99
|
// src/prompts.ts
|
|
103
100
|
var DEFAULT_PROJECT_NAME = "my-fluxy-bot";
|
|
101
|
+
var DEFAULT_FULL_PROJECT_NAME = "my-fluxy-app";
|
|
104
102
|
async function runPrompts(inputs) {
|
|
103
|
+
const full = inputs.full ?? inputs.adapter === "full";
|
|
104
|
+
const mode = inputs.mode ?? (full ? "local" : void 0);
|
|
105
105
|
let name = inputs.name;
|
|
106
106
|
if (!name) {
|
|
107
107
|
if (inputs.yes) {
|
|
108
|
-
name = DEFAULT_PROJECT_NAME;
|
|
108
|
+
name = full ? DEFAULT_FULL_PROJECT_NAME : DEFAULT_PROJECT_NAME;
|
|
109
109
|
} else {
|
|
110
110
|
const result = await text({
|
|
111
111
|
message: "Project name:",
|
|
@@ -122,15 +122,19 @@ async function runPrompts(inputs) {
|
|
|
122
122
|
}
|
|
123
123
|
const minimal = inputs.minimal ?? false;
|
|
124
124
|
let adapter = inputs.adapter;
|
|
125
|
-
if (
|
|
125
|
+
if (full) {
|
|
126
|
+
adapter = "full";
|
|
127
|
+
} else if (!minimal && !adapter) {
|
|
126
128
|
if (inputs.yes) {
|
|
127
129
|
adapter = "react";
|
|
128
130
|
} else {
|
|
129
131
|
const result = await select({
|
|
130
|
-
message: "Select
|
|
132
|
+
message: "Select a template:",
|
|
131
133
|
options: [
|
|
132
|
-
{ label: "
|
|
134
|
+
{ label: "Full stack \u2014 chat + @assistant + setup (recommended)", value: "full" },
|
|
135
|
+
{ label: "Minimal chat widget (ui-kit)", value: "minimal" },
|
|
133
136
|
{ label: "React chat app (Vite + useChat)", value: "react" },
|
|
137
|
+
{ label: "HR anonymous feedback (compliance starter)", value: "hr-feedback" },
|
|
134
138
|
{ label: "Basic (Cloudflare Workers bot)", value: "basic" },
|
|
135
139
|
{ label: "Slack", value: "slack" },
|
|
136
140
|
{ label: "Telegram", value: "telegram" },
|
|
@@ -146,7 +150,8 @@ async function runPrompts(inputs) {
|
|
|
146
150
|
}
|
|
147
151
|
}
|
|
148
152
|
let language = inputs.language;
|
|
149
|
-
|
|
153
|
+
const isWorkerBot = !minimal && adapter !== "react" && adapter !== "full" && adapter !== "hr-feedback";
|
|
154
|
+
if (isWorkerBot && !language) {
|
|
150
155
|
if (inputs.yes) {
|
|
151
156
|
language = "typescript";
|
|
152
157
|
} else {
|
|
@@ -160,6 +165,8 @@ async function runPrompts(inputs) {
|
|
|
160
165
|
if (isCancel(result)) return null;
|
|
161
166
|
language = result;
|
|
162
167
|
}
|
|
168
|
+
} else if (!language) {
|
|
169
|
+
language = "typescript";
|
|
163
170
|
}
|
|
164
171
|
let packageManager = inputs.packageManager;
|
|
165
172
|
if (!packageManager) {
|
|
@@ -194,10 +201,12 @@ async function runPrompts(inputs) {
|
|
|
194
201
|
name,
|
|
195
202
|
adapter: adapter ?? "react",
|
|
196
203
|
packageManager,
|
|
197
|
-
language,
|
|
204
|
+
language: language ?? "typescript",
|
|
198
205
|
shouldInstall,
|
|
199
206
|
shouldInitGit,
|
|
200
|
-
minimal: minimal || inputs.minimal === true
|
|
207
|
+
minimal: minimal || inputs.minimal === true,
|
|
208
|
+
full: full || adapter === "full",
|
|
209
|
+
mode: mode ?? (adapter === "full" ? "local" : void 0)
|
|
201
210
|
};
|
|
202
211
|
}
|
|
203
212
|
|
|
@@ -798,10 +807,12 @@ import path2 from "path";
|
|
|
798
807
|
import { exec } from "child_process";
|
|
799
808
|
import { promisify } from "util";
|
|
800
809
|
var execAsync = promisify(exec);
|
|
810
|
+
var TEMPLATE_CHOICES = "react, full, basic, slack, telegram, discord, web, hr-feedback";
|
|
801
811
|
function parseArgs(argv) {
|
|
802
812
|
const args = {
|
|
803
813
|
yes: false,
|
|
804
814
|
minimal: false,
|
|
815
|
+
full: false,
|
|
805
816
|
skipInstall: false,
|
|
806
817
|
noGit: false,
|
|
807
818
|
help: false
|
|
@@ -815,6 +826,21 @@ function parseArgs(argv) {
|
|
|
815
826
|
args.yes = true;
|
|
816
827
|
} else if (arg === "--minimal") {
|
|
817
828
|
args.minimal = true;
|
|
829
|
+
} else if (arg === "--full") {
|
|
830
|
+
args.full = true;
|
|
831
|
+
args.adapter = "full";
|
|
832
|
+
} else if (arg === "--mode") {
|
|
833
|
+
const value = argv[++i]?.trim().toLowerCase();
|
|
834
|
+
if (value === "local" || value === "hosted") {
|
|
835
|
+
args.mode = value;
|
|
836
|
+
if (value === "hosted") {
|
|
837
|
+
args.full = true;
|
|
838
|
+
args.adapter = "full";
|
|
839
|
+
}
|
|
840
|
+
} else {
|
|
841
|
+
console.error(`Invalid mode: ${value}. Choose: local, hosted`);
|
|
842
|
+
process.exit(1);
|
|
843
|
+
}
|
|
818
844
|
} else if (arg === "--skip-install") {
|
|
819
845
|
args.skipInstall = true;
|
|
820
846
|
} else if (arg === "--no-git") {
|
|
@@ -825,8 +851,10 @@ function parseArgs(argv) {
|
|
|
825
851
|
args.adapter = value;
|
|
826
852
|
} else if (value === "react") {
|
|
827
853
|
args.adapter = "react";
|
|
854
|
+
} else if (value === "hr-feedback") {
|
|
855
|
+
args.adapter = "hr-feedback";
|
|
828
856
|
} else {
|
|
829
|
-
console.error(`Invalid template: ${value}. Choose:
|
|
857
|
+
console.error(`Invalid template: ${value}. Choose: ${TEMPLATE_CHOICES}`);
|
|
830
858
|
process.exit(1);
|
|
831
859
|
}
|
|
832
860
|
} else if (arg === "--adapter" || arg === "-a") {
|
|
@@ -834,7 +862,7 @@ function parseArgs(argv) {
|
|
|
834
862
|
if (value && isAdapterType(value)) {
|
|
835
863
|
args.adapter = value;
|
|
836
864
|
} else {
|
|
837
|
-
console.error(`Invalid adapter: ${value}. Choose:
|
|
865
|
+
console.error(`Invalid adapter: ${value}. Choose: ${TEMPLATE_CHOICES}`);
|
|
838
866
|
process.exit(1);
|
|
839
867
|
}
|
|
840
868
|
} else if (arg === "--pm" || arg === "--package-manager") {
|
|
@@ -872,18 +900,23 @@ ${pc.bold("Usage:")}
|
|
|
872
900
|
npx create-fluxy-chat [project-name] [options]
|
|
873
901
|
|
|
874
902
|
${pc.bold("Options:")}
|
|
875
|
-
-a, --adapter <type> Adapter:
|
|
876
|
-
-t, --template <type> Alias for --adapter (e.g. react)
|
|
903
|
+
-a, --adapter <type> Adapter: ${TEMPLATE_CHOICES}
|
|
904
|
+
-t, --template <type> Alias for --adapter (e.g. full, react)
|
|
877
905
|
--pm <manager> Package manager: npm, pnpm, yarn
|
|
878
906
|
-l, --language <lang> Language: typescript (default) or javascript
|
|
879
907
|
-y, --yes Skip prompts and accept defaults
|
|
908
|
+
--full Full stack: chat + @assistant + setup scripts (recommended)
|
|
909
|
+
--mode <local|hosted> hosted = no wrangler (uses fluxychat.com demo session)
|
|
880
910
|
--minimal Chat-only widget (ui-kit) \u2014 no platform modules
|
|
881
911
|
--skip-install Skip dependency installation
|
|
882
912
|
--no-git Skip git repository initialization
|
|
883
913
|
-h, --help Show this help
|
|
884
914
|
|
|
885
915
|
${pc.bold("Examples:")}
|
|
916
|
+
${pc.cyan("npx create-fluxy-chat my-app --mode hosted -y")}
|
|
917
|
+
${pc.cyan("npx create-fluxy-chat my-app --full -y")}
|
|
886
918
|
${pc.cyan("npx create-fluxy-chat my-chat --minimal")}
|
|
919
|
+
${pc.cyan("npx create-fluxy-chat my-hr-bot --template hr-feedback")}
|
|
887
920
|
${pc.cyan("npx create-fluxy-chat my-chat --template react")}
|
|
888
921
|
${pc.cyan("npx create-fluxy-chat my-bot --adapter basic")}
|
|
889
922
|
${pc.cyan("npx create-fluxy-chat my-bot --adapter slack")}
|
|
@@ -904,6 +937,8 @@ async function main() {
|
|
|
904
937
|
language: args.language,
|
|
905
938
|
yes: args.yes,
|
|
906
939
|
minimal: args.minimal,
|
|
940
|
+
full: args.full,
|
|
941
|
+
mode: args.mode,
|
|
907
942
|
shouldInstall: args.skipInstall ? false : void 0,
|
|
908
943
|
shouldInitGit: args.noGit ? false : void 0
|
|
909
944
|
});
|
|
@@ -934,6 +969,26 @@ async function main() {
|
|
|
934
969
|
pkg.name = config.name;
|
|
935
970
|
writeJson(projectDir, "package.json", pkg);
|
|
936
971
|
s.stop("Minimal chat widget created.");
|
|
972
|
+
} else if (config.full || config.adapter === "full") {
|
|
973
|
+
const templateRoot = path2.join(templatesDir(), "full");
|
|
974
|
+
copyDir(templateRoot, projectDir);
|
|
975
|
+
const pkgPath = path2.join(projectDir, "package.json");
|
|
976
|
+
const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf8"));
|
|
977
|
+
pkg.name = config.name;
|
|
978
|
+
writeJson(projectDir, "package.json", pkg);
|
|
979
|
+
if (config.full || config.adapter === "full") {
|
|
980
|
+
fs2.mkdirSync(path2.join(projectDir, ".fluxy"), { recursive: true });
|
|
981
|
+
const setupMode = config.mode === "hosted" ? "hosted" : "local";
|
|
982
|
+
writeFile(
|
|
983
|
+
projectDir,
|
|
984
|
+
".fluxy/mode",
|
|
985
|
+
`${setupMode}
|
|
986
|
+
`
|
|
987
|
+
);
|
|
988
|
+
}
|
|
989
|
+
s.stop(
|
|
990
|
+
config.mode === "hosted" ? "Full stack app created (hosted mode \u2014 run pnpm setup:hosted)." : "Full stack app created (chat + agent + setup scripts)."
|
|
991
|
+
);
|
|
937
992
|
} else if (config.adapter === "react") {
|
|
938
993
|
const templateRoot = path2.join(templatesDir(), "react");
|
|
939
994
|
copyDir(templateRoot, projectDir);
|
|
@@ -942,6 +997,14 @@ async function main() {
|
|
|
942
997
|
pkg.name = config.name;
|
|
943
998
|
writeJson(projectDir, "package.json", pkg);
|
|
944
999
|
s.stop("React chat app created.");
|
|
1000
|
+
} else if (config.adapter === "hr-feedback") {
|
|
1001
|
+
const templateRoot = path2.join(templatesDir(), "hr-feedback");
|
|
1002
|
+
copyDir(templateRoot, projectDir);
|
|
1003
|
+
const pkgPath = path2.join(projectDir, "package.json");
|
|
1004
|
+
const pkg = JSON.parse(fs2.readFileSync(pkgPath, "utf8"));
|
|
1005
|
+
pkg.name = config.name;
|
|
1006
|
+
writeJson(projectDir, "package.json", pkg);
|
|
1007
|
+
s.stop("HR feedback starter created.");
|
|
945
1008
|
} else {
|
|
946
1009
|
writeJson(projectDir, "package.json", generatePackageJson(config));
|
|
947
1010
|
if (config.language === "typescript") {
|
|
@@ -989,18 +1052,31 @@ async function main() {
|
|
|
989
1052
|
);
|
|
990
1053
|
}
|
|
991
1054
|
}
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1055
|
+
const devCmd = config.packageManager === "npm" ? "npm run" : config.packageManager;
|
|
1056
|
+
const nextSteps = config.full || config.adapter === "full" ? config.mode === "hosted" ? [
|
|
1057
|
+
`cd ${config.name}`,
|
|
1058
|
+
`${devCmd} setup:hosted # guest JWT from fluxychat.com`,
|
|
1059
|
+
`${devCmd} dev # http://localhost:5173`,
|
|
1060
|
+
`# Keep this project: https://fluxychat.com/onboarding?from=cli`
|
|
1061
|
+
].join("\n") : [
|
|
1062
|
+
`# Terminal 1 \u2014 FluxyChat monorepo (if not already running)`,
|
|
1063
|
+
`pnpm --filter @fluxy-chat/worker dev`,
|
|
1064
|
+
``,
|
|
1065
|
+
`cd ${config.name}`,
|
|
1066
|
+
`${devCmd} setup # provision worker \u2192 writes .env`,
|
|
1067
|
+
`${devCmd} dev # http://localhost:5173 (+ dashboard if monorepo nearby)`,
|
|
1068
|
+
`# Keep / import .env: http://localhost:3000/onboarding?from=cli`
|
|
1069
|
+
].join("\n") : config.adapter === "react" || config.minimal ? [
|
|
1070
|
+
`cd ${config.name}`,
|
|
1071
|
+
"cp .env.example .env",
|
|
1072
|
+
"# Set VITE_FLUXYCHAT_WORKER_URL + JWT or public room ID",
|
|
1073
|
+
`${devCmd} dev`
|
|
1074
|
+
].join("\n") : [
|
|
1075
|
+
`cd ${config.name}`,
|
|
1076
|
+
"cp .env.example .dev.vars",
|
|
1077
|
+
`${devCmd} dev`
|
|
1078
|
+
].join("\n");
|
|
1079
|
+
note(nextSteps, "Next steps");
|
|
1004
1080
|
outro(
|
|
1005
1081
|
`${pc.green("Done!")} Visit ${pc.cyan("https://github.com/AlessandroFare/fluxychat")} for the docs.`
|
|
1006
1082
|
);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fluxy-chat/create-fluxy-chat",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Scaffold a new FluxyChat bot project with a single command",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"files": [
|
|
14
14
|
"dist",
|
|
15
15
|
"templates",
|
|
16
|
-
"README.md"
|
|
16
|
+
"README.md",
|
|
17
|
+
"CHANGELOG.md"
|
|
17
18
|
],
|
|
18
19
|
"dependencies": {
|
|
19
20
|
"@clack/prompts": "^0.7.0",
|
|
@@ -38,7 +39,7 @@
|
|
|
38
39
|
},
|
|
39
40
|
"license": "MIT",
|
|
40
41
|
"scripts": {
|
|
41
|
-
"build": "tsup src/index.ts --format esm
|
|
42
|
+
"build": "tsup src/index.ts --format esm",
|
|
42
43
|
"dev": "tsup src/index.ts --format esm --watch",
|
|
43
44
|
"typecheck": "tsc --noEmit"
|
|
44
45
|
}
|
package/readme.md
CHANGED
|
@@ -5,19 +5,24 @@ Scaffold a new [FluxyChat](https://github.com/AlessandroFare/fluxychat) bot proj
|
|
|
5
5
|
## Quick start
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
#
|
|
8
|
+
# Full stack — chat + @assistant + setup scripts (recommended)
|
|
9
|
+
npx create-fluxy-chat my-app --full -y
|
|
10
|
+
cd my-app && pnpm setup && pnpm dev
|
|
11
|
+
|
|
12
|
+
# Minimal chat widget
|
|
9
13
|
npx create-fluxy-chat my-chat --minimal
|
|
10
14
|
|
|
11
|
-
# React + useChat (
|
|
15
|
+
# React + useChat only (bring your own worker URL)
|
|
12
16
|
npx create-fluxy-chat my-chat --template react
|
|
13
17
|
```
|
|
14
18
|
|
|
15
|
-
Interactive mode defaults to the **React chat app** template.
|
|
16
|
-
|
|
17
19
|
## Non-interactive usage
|
|
18
20
|
|
|
19
21
|
```bash
|
|
20
|
-
#
|
|
22
|
+
# Full stack (chat + agent + setup)
|
|
23
|
+
npx create-fluxy-chat my-app --full -y
|
|
24
|
+
|
|
25
|
+
# React + Vite + useChat
|
|
21
26
|
npx create-fluxy-chat my-chat --template react -y
|
|
22
27
|
|
|
23
28
|
# Create a Slack bot with pnpm
|
|
@@ -37,12 +42,13 @@ npx create-fluxy-chat my-bot --adapter basic
|
|
|
37
42
|
|
|
38
43
|
| Flag | Short | Description |
|
|
39
44
|
| --- | --- | --- |
|
|
40
|
-
| `--adapter <type>` | `-a` | Adapter: `react`, `basic`, `slack`, `telegram`, `discord`, `web` |
|
|
41
|
-
| `--template <type>` | `-t` | Alias for `--adapter (e.g. react)` |
|
|
45
|
+
| `--adapter <type>` | `-a` | Adapter: `full`, `react`, `basic`, `slack`, `telegram`, `discord`, `web`, `hr-feedback` |
|
|
46
|
+
| `--template <type>` | `-t` | Alias for `--adapter (e.g. full, react)` |
|
|
42
47
|
| `--pm <manager>` | | Package manager: `npm`, `pnpm`, `yarn` |
|
|
43
48
|
| `--language <lang>` | `-l` | Language: `typescript` (default) or `javascript` |
|
|
44
49
|
| `--yes` | `-y` | Skip prompts and accept defaults |
|
|
45
|
-
| `--
|
|
50
|
+
| `--full` | | Full stack template: chat + `@assistant` + `pnpm setup` / `pnpm dev` |
|
|
51
|
+
| `--minimal` | | Chat-only widget (`@fluxy-chat/ui-kit`), no platform modules |
|
|
46
52
|
| `--skip-install` | | Skip dependency installation |
|
|
47
53
|
| `--no-git` | | Skip git repository initialization |
|
|
48
54
|
| `--help` | `-h` | Show help |
|
|
@@ -62,14 +68,14 @@ npx create-fluxy-chat my-bot --adapter basic
|
|
|
62
68
|
|
|
63
69
|
Each generated project includes:
|
|
64
70
|
|
|
65
|
-
- **`src/index.ts
|
|
66
|
-
- **`src/bot.ts
|
|
67
|
-
- **`fluxy.config.ts
|
|
68
|
-
- **`wrangler.toml
|
|
69
|
-
- **`.dev.vars
|
|
70
|
-
- **`.env.example
|
|
71
|
-
- **`tsconfig.json
|
|
72
|
-
- **`README.md
|
|
71
|
+
- **`src/index.ts`**: Cloudflare Workers entry point with route handling
|
|
72
|
+
- **`src/bot.ts`**: Bot handler using `@fluxy-chat/sdk`
|
|
73
|
+
- **`fluxy.config.ts`**: Room authz and publish middleware (basic template)
|
|
74
|
+
- **`wrangler.toml`**: Cloudflare Workers deployment config
|
|
75
|
+
- **`.dev.vars`**: Local development environment variables
|
|
76
|
+
- **`.env.example`**: Example environment variables for your adapter
|
|
77
|
+
- **`tsconfig.json`**: TypeScript configuration (for TS projects)
|
|
78
|
+
- **`README.md`**: Project-specific setup instructions
|
|
73
79
|
|
|
74
80
|
## Package manager detection
|
|
75
81
|
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Generated by `pnpm setup` — or copy manually after onboarding / first-message.
|
|
2
|
+
|
|
3
|
+
VITE_FLUXYCHAT_WORKER_URL=http://127.0.0.1:8787
|
|
4
|
+
VITE_FLUXYCHAT_MEMBER_JWT=
|
|
5
|
+
VITE_FLUXYCHAT_ROOM_ID=general
|
|
6
|
+
VITE_FLUXYCHAT_AGENT_ID=
|
|
7
|
+
VITE_FLUXYCHAT_AGENT_HANDLE=@assistant
|
|
8
|
+
VITE_FLUXYCHAT_PROJECT_ID=
|
|
9
|
+
VITE_FLUXYCHAT_CONSOLE_URL=http://localhost:3000
|
|
10
|
+
VITE_FLUXYCHAT_USER_ID=demo-user
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# FluxyChat — full stack starter
|
|
2
|
+
|
|
3
|
+
Chat + AI agent + tool calls in one Vite app. Provisions against a local FluxyChat worker in ~60 seconds.
|
|
4
|
+
|
|
5
|
+
## Quick start
|
|
6
|
+
|
|
7
|
+
**Hosted (no wrangler):**
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npx create-fluxy-chat my-app --mode hosted -y
|
|
11
|
+
cd my-app
|
|
12
|
+
pnpm install
|
|
13
|
+
pnpm setup:hosted
|
|
14
|
+
pnpm dev
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
**Local (full control):**
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
# Terminal 1 — monorepo worker
|
|
21
|
+
pnpm --filter @fluxy-chat/worker dev
|
|
22
|
+
|
|
23
|
+
# Terminal 2 — new project
|
|
24
|
+
npx create-fluxy-chat my-app --full -y
|
|
25
|
+
cd my-app
|
|
26
|
+
pnpm install
|
|
27
|
+
pnpm setup
|
|
28
|
+
pnpm dev
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Optional: `pnpm doctor` checks `.env`, worker health, room access.
|
|
32
|
+
|
|
33
|
+
## What you get
|
|
34
|
+
|
|
35
|
+
- Realtime chat (`useChat` + WebSocket)
|
|
36
|
+
- `@assistant` invoke with tool call thread in the UI
|
|
37
|
+
- Sidebar with project/room/agent metadata
|
|
38
|
+
- Link to operator console
|
|
39
|
+
|
|
40
|
+
## Scripts
|
|
41
|
+
|
|
42
|
+
| Command | Description |
|
|
43
|
+
|---------|-------------|
|
|
44
|
+
| `pnpm setup:hosted` | Guest JWT via `GET /demo/session` on api.fluxychat.com |
|
|
45
|
+
| `pnpm setup:local` | Local `/dev/provision` (same as `pnpm setup`) |
|
|
46
|
+
| `pnpm doctor` | Health check: .env, worker, room |
|
|
47
|
+
| `pnpm dev` | Start Vite; auto-start worker if FluxyChat monorepo is detected nearby |
|
|
48
|
+
| `pnpm dev:app` | Vite only |
|
|
49
|
+
|
|
50
|
+
## Environment
|
|
51
|
+
|
|
52
|
+
See `.env.example`. Generated by `pnpm setup`:
|
|
53
|
+
|
|
54
|
+
- `VITE_FLUXYCHAT_WORKER_URL` — default `http://127.0.0.1:8787`
|
|
55
|
+
- `VITE_FLUXYCHAT_MEMBER_JWT` — member token with admin roles
|
|
56
|
+
- `VITE_FLUXYCHAT_ROOM_ID` — `{projectId}-general`
|
|
57
|
+
- `VITE_FLUXYCHAT_AGENT_ID` — built-in `@assistant`
|
|
58
|
+
|
|
59
|
+
## Hosted credentials
|
|
60
|
+
|
|
61
|
+
Use [fluxychat.com/onboarding](https://fluxychat.com/onboarding) and copy values into `.env` manually.
|
|
62
|
+
|
|
63
|
+
## Learn more
|
|
64
|
+
|
|
65
|
+
- [One-click product roadmap](https://github.com/AlessandroFare/fluxychat/blob/main/docs/ONE-CLICK-PRODUCT-ROADMAP.md)
|
|
66
|
+
- [Docs](https://docs.fluxychat.com)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
6
|
+
<title>FluxyChat</title>
|
|
7
|
+
</head>
|
|
8
|
+
<body>
|
|
9
|
+
<div id="root"></div>
|
|
10
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
11
|
+
</body>
|
|
12
|
+
</html>
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "fluxy-chat-full",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"setup": "node scripts/fluxy-setup.mjs",
|
|
8
|
+
"setup:hosted": "node scripts/fluxy-setup.mjs --mode hosted",
|
|
9
|
+
"setup:local": "node scripts/fluxy-setup.mjs --mode local",
|
|
10
|
+
"doctor": "node scripts/fluxy-doctor.mjs",
|
|
11
|
+
"dev": "node scripts/fluxy-dev.mjs",
|
|
12
|
+
"dev:app": "vite",
|
|
13
|
+
"build": "tsc -b && vite build",
|
|
14
|
+
"preview": "vite preview"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@fluxy-chat/react": "^0.1.1",
|
|
18
|
+
"@fluxy-chat/sdk": "^0.6.1",
|
|
19
|
+
"react": "^19.0.0",
|
|
20
|
+
"react-dom": "^19.0.0"
|
|
21
|
+
},
|
|
22
|
+
"devDependencies": {
|
|
23
|
+
"@types/react": "^19.0.0",
|
|
24
|
+
"@types/react-dom": "^19.0.0",
|
|
25
|
+
"@vitejs/plugin-react": "^4.3.0",
|
|
26
|
+
"typescript": "^5.6.0",
|
|
27
|
+
"vite": "^6.0.0"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Start Vite app; optionally worker + dashboard from detected monorepo.
|
|
4
|
+
* Auto-runs setup when .env is missing and worker is reachable.
|
|
5
|
+
*/
|
|
6
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
7
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
8
|
+
import { dirname, join, resolve } from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
|
|
11
|
+
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
12
|
+
const envPath = join(root, ".env");
|
|
13
|
+
const modePath = join(root, ".fluxy", "mode");
|
|
14
|
+
const WORKER_URL = process.env.FLUXY_WORKER_URL || "http://127.0.0.1:8787";
|
|
15
|
+
const POLL_MS = 500;
|
|
16
|
+
const POLL_MAX = 90_000;
|
|
17
|
+
const START_DASHBOARD = process.env.FLUXY_START_DASHBOARD !== "0";
|
|
18
|
+
|
|
19
|
+
function findMonorepo(startDir) {
|
|
20
|
+
const explicit = process.env.FLUXYCHAT_ROOT?.trim();
|
|
21
|
+
if (explicit && existsSync(join(explicit, "apps", "worker", "package.json"))) {
|
|
22
|
+
return explicit;
|
|
23
|
+
}
|
|
24
|
+
let dir = startDir;
|
|
25
|
+
for (let i = 0; i < 10; i++) {
|
|
26
|
+
const workerPkg = join(dir, "apps", "worker", "package.json");
|
|
27
|
+
if (existsSync(workerPkg)) {
|
|
28
|
+
try {
|
|
29
|
+
const pkg = JSON.parse(readFileSync(workerPkg, "utf8"));
|
|
30
|
+
if (pkg.name === "@fluxy-chat/worker") return dir;
|
|
31
|
+
} catch {
|
|
32
|
+
/* ignore */
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
const parent = dirname(dir);
|
|
36
|
+
if (parent === dir) break;
|
|
37
|
+
dir = parent;
|
|
38
|
+
}
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function readSetupMode() {
|
|
43
|
+
if (existsSync(modePath)) {
|
|
44
|
+
const m = readFileSync(modePath, "utf8").trim().toLowerCase();
|
|
45
|
+
if (m === "hosted" || m === "local") return m;
|
|
46
|
+
}
|
|
47
|
+
return "local";
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
async function isWorkerUp(url = WORKER_URL) {
|
|
51
|
+
try {
|
|
52
|
+
const r = await fetch(`${url}/health`, { signal: AbortSignal.timeout(1500) });
|
|
53
|
+
return r.ok;
|
|
54
|
+
} catch {
|
|
55
|
+
return false;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function sleep(ms) {
|
|
60
|
+
return new Promise((r) => setTimeout(r, ms));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function runProc(label, cmd, args, cwd, env = {}) {
|
|
64
|
+
const child = spawn(cmd, args, {
|
|
65
|
+
cwd,
|
|
66
|
+
shell: process.platform === "win32",
|
|
67
|
+
stdio: "inherit",
|
|
68
|
+
env: { ...process.env, ...env },
|
|
69
|
+
});
|
|
70
|
+
child.on("exit", (code) => {
|
|
71
|
+
if (code && code !== 0) {
|
|
72
|
+
console.error(`[${label}] exited with code ${code}`);
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
return child;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function runSetup(mode) {
|
|
79
|
+
const args = ["scripts/fluxy-setup.mjs"];
|
|
80
|
+
if (mode === "hosted") args.push("--mode", "hosted");
|
|
81
|
+
const result = spawnSync("node", args, {
|
|
82
|
+
cwd: root,
|
|
83
|
+
stdio: "inherit",
|
|
84
|
+
shell: process.platform === "win32",
|
|
85
|
+
});
|
|
86
|
+
return result.status === 0;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function main() {
|
|
90
|
+
const monorepo = findMonorepo(root);
|
|
91
|
+
const children = [];
|
|
92
|
+
const setupMode = readSetupMode();
|
|
93
|
+
|
|
94
|
+
if (!existsSync(envPath)) {
|
|
95
|
+
if (setupMode === "hosted") {
|
|
96
|
+
console.log("No .env — running hosted setup…");
|
|
97
|
+
if (!runSetup("hosted")) {
|
|
98
|
+
console.warn("Hosted setup failed. Fix network or run: pnpm setup -- --mode hosted");
|
|
99
|
+
}
|
|
100
|
+
} else if (await isWorkerUp()) {
|
|
101
|
+
console.log("No .env — running local setup…");
|
|
102
|
+
if (!runSetup("local")) {
|
|
103
|
+
console.warn("Setup failed. Run: pnpm setup");
|
|
104
|
+
}
|
|
105
|
+
} else if (monorepo) {
|
|
106
|
+
console.log("No .env — will start worker then run setup…");
|
|
107
|
+
} else {
|
|
108
|
+
console.warn(
|
|
109
|
+
"No .env found.\n" +
|
|
110
|
+
" Hosted: pnpm setup -- --mode hosted\n" +
|
|
111
|
+
" Local: start worker, then pnpm setup",
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
if (!(await isWorkerUp()) && setupMode === "local" && monorepo) {
|
|
117
|
+
console.log(`Starting worker from monorepo: ${monorepo}`);
|
|
118
|
+
children.push(
|
|
119
|
+
runProc("worker", "pnpm", ["--filter", "@fluxy-chat/worker", "dev"], monorepo, {
|
|
120
|
+
ALLOW_DEV_PROVISION: "true",
|
|
121
|
+
NODE_ENV: "development",
|
|
122
|
+
}),
|
|
123
|
+
);
|
|
124
|
+
const start = Date.now();
|
|
125
|
+
while (Date.now() - start < POLL_MAX) {
|
|
126
|
+
if (await isWorkerUp()) {
|
|
127
|
+
console.log(`Worker ready at ${WORKER_URL}`);
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
await sleep(POLL_MS);
|
|
131
|
+
}
|
|
132
|
+
if (!existsSync(envPath) && (await isWorkerUp())) {
|
|
133
|
+
console.log("Running setup after worker start…");
|
|
134
|
+
runSetup("local");
|
|
135
|
+
}
|
|
136
|
+
} else if (await isWorkerUp()) {
|
|
137
|
+
console.log(`Worker reachable at ${WORKER_URL}`);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (START_DASHBOARD && monorepo && setupMode === "local") {
|
|
141
|
+
console.log("Starting dashboard console on :3000");
|
|
142
|
+
children.push(
|
|
143
|
+
runProc("dashboard", "pnpm", ["--filter", "@fluxy-chat/dashboard", "dev"], monorepo),
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
children.push(runProc("vite", "pnpm", ["run", "dev:app"], root));
|
|
148
|
+
|
|
149
|
+
function shutdown() {
|
|
150
|
+
for (const child of children) {
|
|
151
|
+
if (child && !child.killed) {
|
|
152
|
+
try {
|
|
153
|
+
child.kill("SIGTERM");
|
|
154
|
+
} catch {
|
|
155
|
+
/* ignore */
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
process.exit(0);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
process.on("SIGINT", shutdown);
|
|
163
|
+
process.on("SIGTERM", shutdown);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
main().catch((err) => {
|
|
167
|
+
console.error(err);
|
|
168
|
+
process.exit(1);
|
|
169
|
+
});
|