@fluxy-chat/create-fluxy-chat 0.5.8 → 0.5.9
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 +7 -0
- package/dist/index.js +118 -30
- package/package.json +1 -1
- package/readme.md +17 -25
- package/templates/full/README.md +10 -4
- package/templates/full/package.json +1 -0
- package/templates/full/scripts/fluxy-setup.mjs +73 -18
- package/templates/react/src/App.tsx +14 -12
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.5.9] - 2026-08-21
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `--mode self-host` (alias of `local`). Interactive Worker URL, console URL, and optional Groq key. Writes `.fluxy/answers.json` and `.fluxy/worker.dev.vars` to paste into `apps/worker/.dev.vars`.
|
|
8
|
+
- `pnpm setup:self-host` on the full template. If the Worker is down, setup asks for a URL instead of exiting immediately.
|
|
9
|
+
|
|
3
10
|
## [0.5.8] - 2026-08-19
|
|
4
11
|
|
|
5
12
|
### Fixed
|
package/dist/index.js
CHANGED
|
@@ -5,6 +5,7 @@ import { intro, log, note, outro, spinner } from "@clack/prompts";
|
|
|
5
5
|
import pc from "picocolors";
|
|
6
6
|
|
|
7
7
|
// src/prompts.ts
|
|
8
|
+
import { randomBytes } from "crypto";
|
|
8
9
|
import {
|
|
9
10
|
confirm,
|
|
10
11
|
isCancel,
|
|
@@ -97,11 +98,15 @@ function templatesDir() {
|
|
|
97
98
|
}
|
|
98
99
|
|
|
99
100
|
// src/prompts.ts
|
|
101
|
+
var DEFAULT_WORKER_URL = "http://127.0.0.1:8787";
|
|
102
|
+
var DEFAULT_CONSOLE_URL = "http://localhost:3000";
|
|
103
|
+
function generateJwtSigningKey() {
|
|
104
|
+
return randomBytes(32).toString("hex");
|
|
105
|
+
}
|
|
100
106
|
var DEFAULT_PROJECT_NAME = "my-fluxy-bot";
|
|
101
107
|
var DEFAULT_FULL_PROJECT_NAME = "my-fluxy-app";
|
|
102
108
|
async function runPrompts(inputs) {
|
|
103
109
|
const full = inputs.full ?? inputs.adapter === "full";
|
|
104
|
-
const mode = inputs.mode ?? (full ? "local" : void 0);
|
|
105
110
|
let name = inputs.name;
|
|
106
111
|
if (!name) {
|
|
107
112
|
if (inputs.yes) {
|
|
@@ -197,6 +202,64 @@ async function runPrompts(inputs) {
|
|
|
197
202
|
initialValue: true
|
|
198
203
|
}));
|
|
199
204
|
if (isCancel(shouldInitGit)) return null;
|
|
205
|
+
const isFull = full || adapter === "full";
|
|
206
|
+
let resolvedMode = inputs.mode;
|
|
207
|
+
let workerUrl;
|
|
208
|
+
let consoleUrl;
|
|
209
|
+
let groqApiKey;
|
|
210
|
+
let jwtSigningKey;
|
|
211
|
+
if (isFull) {
|
|
212
|
+
if (!resolvedMode) {
|
|
213
|
+
if (inputs.yes) {
|
|
214
|
+
resolvedMode = "local";
|
|
215
|
+
} else {
|
|
216
|
+
const picked = await select({
|
|
217
|
+
message: "Where does the Worker run?",
|
|
218
|
+
options: [
|
|
219
|
+
{
|
|
220
|
+
label: "Hosted (fluxychat.com + Clerk \u2014 no wrangler)",
|
|
221
|
+
value: "hosted"
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
label: "Self-host (your Worker / wrangler dev)",
|
|
225
|
+
value: "local"
|
|
226
|
+
}
|
|
227
|
+
]
|
|
228
|
+
});
|
|
229
|
+
if (isCancel(picked)) return null;
|
|
230
|
+
resolvedMode = picked;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
if (resolvedMode === "local") {
|
|
234
|
+
jwtSigningKey = generateJwtSigningKey();
|
|
235
|
+
if (inputs.yes) {
|
|
236
|
+
workerUrl = DEFAULT_WORKER_URL;
|
|
237
|
+
consoleUrl = DEFAULT_CONSOLE_URL;
|
|
238
|
+
} else {
|
|
239
|
+
const workerResult = await text({
|
|
240
|
+
message: "Worker URL:",
|
|
241
|
+
placeholder: DEFAULT_WORKER_URL,
|
|
242
|
+
initialValue: DEFAULT_WORKER_URL
|
|
243
|
+
});
|
|
244
|
+
if (isCancel(workerResult)) return null;
|
|
245
|
+
workerUrl = String(workerResult).trim() || DEFAULT_WORKER_URL;
|
|
246
|
+
const consoleResult = await text({
|
|
247
|
+
message: "Console URL (dashboard):",
|
|
248
|
+
placeholder: DEFAULT_CONSOLE_URL,
|
|
249
|
+
initialValue: DEFAULT_CONSOLE_URL
|
|
250
|
+
});
|
|
251
|
+
if (isCancel(consoleResult)) return null;
|
|
252
|
+
consoleUrl = String(consoleResult).trim() || DEFAULT_CONSOLE_URL;
|
|
253
|
+
const groqResult = await text({
|
|
254
|
+
message: "Groq API key (optional, for @assistant):",
|
|
255
|
+
placeholder: "gsk_\u2026"
|
|
256
|
+
});
|
|
257
|
+
if (isCancel(groqResult)) return null;
|
|
258
|
+
const groq = String(groqResult).trim();
|
|
259
|
+
if (groq) groqApiKey = groq;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
200
263
|
return {
|
|
201
264
|
name,
|
|
202
265
|
adapter: adapter ?? "react",
|
|
@@ -205,8 +268,12 @@ async function runPrompts(inputs) {
|
|
|
205
268
|
shouldInstall,
|
|
206
269
|
shouldInitGit,
|
|
207
270
|
minimal: minimal || inputs.minimal === true,
|
|
208
|
-
full:
|
|
209
|
-
mode:
|
|
271
|
+
full: isFull,
|
|
272
|
+
mode: resolvedMode ?? (isFull ? "local" : void 0),
|
|
273
|
+
workerUrl,
|
|
274
|
+
consoleUrl,
|
|
275
|
+
groqApiKey,
|
|
276
|
+
jwtSigningKey
|
|
210
277
|
};
|
|
211
278
|
}
|
|
212
279
|
|
|
@@ -831,14 +898,12 @@ function parseArgs(argv) {
|
|
|
831
898
|
args.adapter = "full";
|
|
832
899
|
} else if (arg === "--mode") {
|
|
833
900
|
const value = argv[++i]?.trim().toLowerCase();
|
|
834
|
-
if (value === "local" || value === "hosted") {
|
|
835
|
-
args.mode = value;
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
args.adapter = "full";
|
|
839
|
-
}
|
|
901
|
+
if (value === "local" || value === "hosted" || value === "self-host") {
|
|
902
|
+
args.mode = value === "self-host" ? "local" : value;
|
|
903
|
+
args.full = true;
|
|
904
|
+
args.adapter = "full";
|
|
840
905
|
} else {
|
|
841
|
-
console.error(`Invalid mode: ${value}. Choose: local, hosted`);
|
|
906
|
+
console.error(`Invalid mode: ${value}. Choose: local, self-host, hosted`);
|
|
842
907
|
process.exit(1);
|
|
843
908
|
}
|
|
844
909
|
} else if (arg === "--skip-install") {
|
|
@@ -894,7 +959,7 @@ function parseArgs(argv) {
|
|
|
894
959
|
return args;
|
|
895
960
|
}
|
|
896
961
|
var HELP_TEXT = `
|
|
897
|
-
${pc.bold("create-fluxy-chat")} \u2014 Scaffold a
|
|
962
|
+
${pc.bold("create-fluxy-chat")} \u2014 Scaffold a FluxyChat app or bot worker
|
|
898
963
|
|
|
899
964
|
${pc.bold("Usage:")}
|
|
900
965
|
npx @fluxy-chat/create-fluxy-chat [project-name] [options]
|
|
@@ -906,22 +971,19 @@ ${pc.bold("Options:")}
|
|
|
906
971
|
-l, --language <lang> Language: typescript (default) or javascript
|
|
907
972
|
-y, --yes Skip prompts and accept defaults
|
|
908
973
|
--full Full stack: chat + @assistant + setup scripts (recommended)
|
|
909
|
-
--mode <local|
|
|
910
|
-
|
|
974
|
+
--mode <hosted|local|self-host>
|
|
975
|
+
hosted = Clerk on fluxychat.com (no wrangler)
|
|
976
|
+
local / self-host = your Worker (asks for URL + keys)
|
|
977
|
+
--minimal Chat-only widget (ui-kit)
|
|
911
978
|
--skip-install Skip dependency installation
|
|
912
979
|
--no-git Skip git repository initialization
|
|
913
980
|
-h, --help Show this help
|
|
914
981
|
|
|
915
982
|
${pc.bold("Examples:")}
|
|
916
|
-
${pc.cyan("npx @fluxy-chat/create-fluxy-chat my-app --mode hosted -y")}
|
|
917
|
-
${pc.cyan("npx @fluxy-chat/create-fluxy-chat my-app --
|
|
918
|
-
${pc.cyan("npx create-fluxy-chat my-chat --minimal")}
|
|
919
|
-
${pc.cyan("npx create-fluxy-chat my-
|
|
920
|
-
${pc.cyan("npx create-fluxy-chat my-chat --template react")}
|
|
921
|
-
${pc.cyan("npx create-fluxy-chat my-bot --adapter basic")}
|
|
922
|
-
${pc.cyan("npx create-fluxy-chat my-bot --adapter slack")}
|
|
923
|
-
${pc.cyan("npx create-fluxy-chat my-bot --adapter telegram --pm pnpm")}
|
|
924
|
-
${pc.cyan("npx create-fluxy-chat my-bot -y --adapter discord")}
|
|
983
|
+
${pc.cyan("npx @fluxy-chat/create-fluxy-chat@latest my-app --mode hosted -y")}
|
|
984
|
+
${pc.cyan("npx @fluxy-chat/create-fluxy-chat@latest my-app --mode self-host")}
|
|
985
|
+
${pc.cyan("npx @fluxy-chat/create-fluxy-chat@latest my-chat --minimal")}
|
|
986
|
+
${pc.cyan("npx @fluxy-chat/create-fluxy-chat@latest my-bot --adapter slack")}
|
|
925
987
|
`;
|
|
926
988
|
async function main() {
|
|
927
989
|
const args = parseArgs(process.argv.slice(2));
|
|
@@ -979,12 +1041,39 @@ async function main() {
|
|
|
979
1041
|
if (config.full || config.adapter === "full") {
|
|
980
1042
|
fs2.mkdirSync(path2.join(projectDir, ".fluxy"), { recursive: true });
|
|
981
1043
|
const setupMode = config.mode === "hosted" ? "hosted" : "local";
|
|
1044
|
+
writeFile(projectDir, ".fluxy/mode", `${setupMode}
|
|
1045
|
+
`);
|
|
982
1046
|
writeFile(
|
|
983
1047
|
projectDir,
|
|
984
|
-
".fluxy/
|
|
985
|
-
`${
|
|
1048
|
+
".fluxy/answers.json",
|
|
1049
|
+
`${JSON.stringify(
|
|
1050
|
+
{
|
|
1051
|
+
mode: setupMode,
|
|
1052
|
+
workerUrl: config.workerUrl ?? null,
|
|
1053
|
+
consoleUrl: config.consoleUrl ?? null,
|
|
1054
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
1055
|
+
},
|
|
1056
|
+
null,
|
|
1057
|
+
2
|
|
1058
|
+
)}
|
|
986
1059
|
`
|
|
987
1060
|
);
|
|
1061
|
+
if (setupMode === "local") {
|
|
1062
|
+
const groqLine = config.groqApiKey ? `GROQ_API_KEY=${config.groqApiKey}` : "# GROQ_API_KEY=";
|
|
1063
|
+
writeFile(
|
|
1064
|
+
projectDir,
|
|
1065
|
+
".fluxy/worker.dev.vars",
|
|
1066
|
+
[
|
|
1067
|
+
"# Merge into fluxychat/apps/worker/.dev.vars (or paste after clone).",
|
|
1068
|
+
"# Member JWTs are per-project in D1. This signing key is for bootstrap/secrets.",
|
|
1069
|
+
"ALLOW_DEV_PROVISION=true",
|
|
1070
|
+
`JWT_SIGNING_KEY=${config.jwtSigningKey ?? ""}`,
|
|
1071
|
+
groqLine,
|
|
1072
|
+
"AI_MODEL=openai/gpt-oss-20b",
|
|
1073
|
+
""
|
|
1074
|
+
].join("\n")
|
|
1075
|
+
);
|
|
1076
|
+
}
|
|
988
1077
|
}
|
|
989
1078
|
s.stop(
|
|
990
1079
|
config.mode === "hosted" ? "Full stack app created (hosted mode \u2014 run pnpm setup:hosted)." : "Full stack app created (chat + agent + setup scripts)."
|
|
@@ -1059,13 +1148,12 @@ async function main() {
|
|
|
1059
1148
|
`${devCmd} dev # http://localhost:5173`,
|
|
1060
1149
|
`# Keep this project: https://fluxychat.com/onboarding?from=cli`
|
|
1061
1150
|
].join("\n") : [
|
|
1062
|
-
`# Terminal 1 \u2014 FluxyChat monorepo (if not already running)`,
|
|
1063
|
-
`pnpm --filter @fluxy-chat/worker dev`,
|
|
1064
|
-
``,
|
|
1065
1151
|
`cd ${config.name}`,
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
`#
|
|
1152
|
+
`# 1. Clone FluxyChat and run: pnpm run self-host`,
|
|
1153
|
+
`# Merge .fluxy/worker.dev.vars into apps/worker/.dev.vars`,
|
|
1154
|
+
`# 2. Start Worker: pnpm --filter @fluxy-chat/worker dev`,
|
|
1155
|
+
`${devCmd} setup:local # POST /dev/provision \u2192 writes .env`,
|
|
1156
|
+
`${devCmd} dev # http://localhost:5173`
|
|
1069
1157
|
].join("\n") : config.adapter === "react" || config.minimal ? [
|
|
1070
1158
|
`cd ${config.name}`,
|
|
1071
1159
|
"cp .env.example .env",
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -1,41 +1,33 @@
|
|
|
1
1
|
# create-fluxy-chat
|
|
2
2
|
|
|
3
|
-
Scaffold a
|
|
3
|
+
Scaffold a FluxyChat Vite app or bot worker.
|
|
4
4
|
|
|
5
5
|
## Quick start
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
#
|
|
9
|
-
npx @fluxy-chat/create-fluxy-chat my-app --
|
|
10
|
-
cd my-app && pnpm setup && pnpm dev
|
|
8
|
+
# Hosted — Clerk, no wrangler
|
|
9
|
+
npx @fluxy-chat/create-fluxy-chat@latest my-app --mode hosted -y
|
|
10
|
+
cd my-app && pnpm setup:hosted && pnpm dev
|
|
11
11
|
|
|
12
|
-
#
|
|
13
|
-
npx create-fluxy-chat my-
|
|
12
|
+
# Your Worker
|
|
13
|
+
npx @fluxy-chat/create-fluxy-chat@latest my-app --mode self-host
|
|
14
|
+
cd my-app && pnpm setup:local && pnpm dev
|
|
14
15
|
|
|
15
|
-
#
|
|
16
|
-
npx create-fluxy-chat my-chat --
|
|
16
|
+
# Minimal widget
|
|
17
|
+
npx @fluxy-chat/create-fluxy-chat@latest my-chat --minimal
|
|
17
18
|
```
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
```bash
|
|
22
|
-
# Full stack (chat + agent + setup)
|
|
23
|
-
npx create-fluxy-chat my-app --full -y
|
|
24
|
-
|
|
25
|
-
# React + Vite + useChat
|
|
26
|
-
npx create-fluxy-chat my-chat --template react -y
|
|
20
|
+
Always use `@fluxy-chat/create-fluxy-chat`. Bare `npx create-fluxy-chat` is not this package.
|
|
27
21
|
|
|
28
|
-
|
|
29
|
-
npx create-fluxy-chat my-bot --adapter slack --pm pnpm
|
|
22
|
+
Self-host writes `.fluxy/worker.dev.vars` (Worker URL, Groq key, signing key). Merge that into `apps/worker/.dev.vars` after `pnpm run self-host` in the FluxyChat repo.
|
|
30
23
|
|
|
31
|
-
|
|
32
|
-
npx create-fluxy-chat my-bot --adapter telegram --skip-install
|
|
33
|
-
|
|
34
|
-
# Create a Discord bot with defaults
|
|
35
|
-
npx create-fluxy-chat my-bot -y --adapter discord
|
|
24
|
+
## Non-interactive usage
|
|
36
25
|
|
|
37
|
-
|
|
38
|
-
npx create-fluxy-chat my-
|
|
26
|
+
```bash
|
|
27
|
+
npx @fluxy-chat/create-fluxy-chat@latest my-app --mode hosted -y
|
|
28
|
+
npx @fluxy-chat/create-fluxy-chat@latest my-app --full -y
|
|
29
|
+
npx @fluxy-chat/create-fluxy-chat@latest my-chat --template react -y
|
|
30
|
+
npx @fluxy-chat/create-fluxy-chat@latest my-bot --adapter slack --pm pnpm
|
|
39
31
|
```
|
|
40
32
|
|
|
41
33
|
## Options
|
package/templates/full/README.md
CHANGED
|
@@ -16,23 +16,29 @@ pnpm dev
|
|
|
16
16
|
|
|
17
17
|
Localhost opens a 3-step tour. Last step is sign in. After Clerk you come back to a simple chat. Open a second tab to try realtime. Use Open dashboard for rooms and agents.
|
|
18
18
|
|
|
19
|
-
**
|
|
19
|
+
**Self-host (your Worker):**
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
|
+
# In the FluxyChat repo
|
|
23
|
+
pnpm run self-host
|
|
22
24
|
pnpm --filter @fluxy-chat/worker dev
|
|
23
|
-
|
|
25
|
+
|
|
26
|
+
# In another terminal
|
|
27
|
+
npx @fluxy-chat/create-fluxy-chat@latest my-app --mode self-host
|
|
24
28
|
cd my-app
|
|
25
29
|
pnpm install
|
|
26
|
-
pnpm setup
|
|
30
|
+
pnpm setup:local
|
|
27
31
|
pnpm dev
|
|
28
32
|
```
|
|
29
33
|
|
|
34
|
+
If the Worker is down, `setup:local` asks for the URL. Merge `.fluxy/worker.dev.vars` into `apps/worker/.dev.vars` (Groq key + `ALLOW_DEV_PROVISION=true`).
|
|
35
|
+
|
|
30
36
|
## Scripts
|
|
31
37
|
|
|
32
38
|
| Command | Description |
|
|
33
39
|
|---------|-------------|
|
|
34
40
|
| `pnpm setup:hosted` | Writes worker + console URLs. Auth happens in the browser via Clerk. |
|
|
35
|
-
| `pnpm setup:local` |
|
|
41
|
+
| `pnpm setup:local` / `pnpm setup:self-host` | `POST /dev/provision` on your Worker |
|
|
36
42
|
| `pnpm doctor` | Health check |
|
|
37
43
|
| `pnpm dev` | Start Vite |
|
|
38
44
|
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
"setup": "node scripts/fluxy-setup.mjs",
|
|
8
8
|
"setup:hosted": "node scripts/fluxy-setup.mjs --mode hosted",
|
|
9
9
|
"setup:local": "node scripts/fluxy-setup.mjs --mode local",
|
|
10
|
+
"setup:self-host": "node scripts/fluxy-setup.mjs --mode self-host",
|
|
10
11
|
"doctor": "node scripts/fluxy-doctor.mjs",
|
|
11
12
|
"dev": "node scripts/fluxy-dev.mjs",
|
|
12
13
|
"dev:app": "vite",
|
|
@@ -3,22 +3,25 @@
|
|
|
3
3
|
* Provision credentials and write .env for the full template.
|
|
4
4
|
*
|
|
5
5
|
* Modes:
|
|
6
|
-
* local
|
|
7
|
-
* hosted
|
|
6
|
+
* local / self-host — POST /dev/provision on your worker (ALLOW_DEV_PROVISION=true)
|
|
7
|
+
* hosted — Clerk on fluxychat.com (no wrangler)
|
|
8
8
|
*
|
|
9
9
|
* Usage:
|
|
10
10
|
* pnpm setup
|
|
11
11
|
* pnpm setup -- --mode hosted
|
|
12
|
+
* pnpm setup -- --mode self-host
|
|
12
13
|
* FLUXY_SETUP_MODE=hosted pnpm setup
|
|
13
14
|
*/
|
|
14
15
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
15
16
|
import { dirname, join, resolve } from "node:path";
|
|
17
|
+
import { createInterface } from "node:readline";
|
|
16
18
|
import { fileURLToPath } from "node:url";
|
|
17
19
|
|
|
18
20
|
const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
19
21
|
const envPath = join(root, ".env");
|
|
20
22
|
const metaPath = join(root, ".fluxy", "setup.json");
|
|
21
23
|
const modePath = join(root, ".fluxy", "mode");
|
|
24
|
+
const answersPath = join(root, ".fluxy", "answers.json");
|
|
22
25
|
|
|
23
26
|
const HOSTED_WORKER_DEFAULT = "https://api.fluxychat.com";
|
|
24
27
|
const HOSTED_CONSOLE_DEFAULT = "https://fluxychat.com";
|
|
@@ -41,11 +44,40 @@ function fail(msg) {
|
|
|
41
44
|
process.exit(1);
|
|
42
45
|
}
|
|
43
46
|
|
|
47
|
+
function parseSetupMode(raw) {
|
|
48
|
+
const m = String(raw || "").trim().toLowerCase();
|
|
49
|
+
if (m === "hosted") return "hosted";
|
|
50
|
+
if (m === "local" || m === "self-host") return "local";
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function readAnswers() {
|
|
55
|
+
if (!existsSync(answersPath)) return {};
|
|
56
|
+
try {
|
|
57
|
+
return JSON.parse(readFileSync(answersPath, "utf8"));
|
|
58
|
+
} catch {
|
|
59
|
+
return {};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function promptLine(question, fallback) {
|
|
64
|
+
if (!process.stdin.isTTY) return Promise.resolve(fallback);
|
|
65
|
+
return new Promise((resolveAnswer) => {
|
|
66
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
67
|
+
rl.question(`${question} [${fallback}]: `, (answer) => {
|
|
68
|
+
rl.close();
|
|
69
|
+
resolveAnswer(String(answer || "").trim() || fallback);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
|
|
44
74
|
function readDefaultMode() {
|
|
45
75
|
if (existsSync(modePath)) {
|
|
46
|
-
const
|
|
47
|
-
if (
|
|
76
|
+
const parsed = parseSetupMode(readFileSync(modePath, "utf8"));
|
|
77
|
+
if (parsed) return parsed;
|
|
48
78
|
}
|
|
79
|
+
const fromAnswers = parseSetupMode(readAnswers().mode);
|
|
80
|
+
if (fromAnswers) return fromAnswers;
|
|
49
81
|
return "local";
|
|
50
82
|
}
|
|
51
83
|
|
|
@@ -53,12 +85,12 @@ function resolveMode() {
|
|
|
53
85
|
const argv = process.argv.slice(2);
|
|
54
86
|
const flagIdx = argv.indexOf("--mode");
|
|
55
87
|
if (flagIdx >= 0 && argv[flagIdx + 1]) {
|
|
56
|
-
const
|
|
57
|
-
if (
|
|
58
|
-
fail(`Unknown mode "${argv[flagIdx + 1]}". Use: local | hosted`);
|
|
88
|
+
const parsed = parseSetupMode(argv[flagIdx + 1]);
|
|
89
|
+
if (parsed) return parsed;
|
|
90
|
+
fail(`Unknown mode "${argv[flagIdx + 1]}". Use: local | self-host | hosted`);
|
|
59
91
|
}
|
|
60
|
-
const fromEnv =
|
|
61
|
-
if (fromEnv
|
|
92
|
+
const fromEnv = parseSetupMode(process.env.FLUXY_SETUP_MODE);
|
|
93
|
+
if (fromEnv) return fromEnv;
|
|
62
94
|
return readDefaultMode();
|
|
63
95
|
}
|
|
64
96
|
|
|
@@ -218,20 +250,43 @@ async function setupHosted() {
|
|
|
218
250
|
}
|
|
219
251
|
|
|
220
252
|
async function setupLocal() {
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
|
|
253
|
+
const answers = readAnswers();
|
|
254
|
+
let workerUrl =
|
|
255
|
+
process.env.FLUXY_WORKER_URL ||
|
|
256
|
+
process.env.FLUXYCHAT_WORKER_URL ||
|
|
257
|
+
answers.workerUrl ||
|
|
258
|
+
LOCAL_WORKER_DEFAULT;
|
|
259
|
+
const consoleUrl =
|
|
260
|
+
process.env.FLUXY_CONSOLE_URL || answers.consoleUrl || LOCAL_CONSOLE_DEFAULT;
|
|
224
261
|
|
|
225
262
|
console.log(dim(` mode: local · worker: ${workerUrl}`));
|
|
226
263
|
|
|
227
264
|
if (!(await isWorkerUp(workerUrl))) {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
265
|
+
console.log(
|
|
266
|
+
dim(
|
|
267
|
+
`\n Worker not reachable at ${workerUrl}.\n` +
|
|
268
|
+
" Clone FluxyChat, then:\n" +
|
|
269
|
+
" pnpm install && pnpm run self-host\n" +
|
|
270
|
+
" pnpm --filter @fluxy-chat/worker dev\n" +
|
|
271
|
+
" Merge this project's .fluxy/worker.dev.vars into apps/worker/.dev.vars\n",
|
|
272
|
+
),
|
|
234
273
|
);
|
|
274
|
+
if (process.stdin.isTTY) {
|
|
275
|
+
for (let i = 0; i < 3; i += 1) {
|
|
276
|
+
workerUrl = await promptLine("Worker URL", workerUrl);
|
|
277
|
+
if (await isWorkerUp(workerUrl)) break;
|
|
278
|
+
console.log(dim(` Still down at ${workerUrl}`));
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
if (!(await isWorkerUp(workerUrl))) {
|
|
282
|
+
fail(
|
|
283
|
+
`Worker not reachable at ${workerUrl}\n` +
|
|
284
|
+
" Start it from the FluxyChat monorepo:\n" +
|
|
285
|
+
" pnpm run self-host && pnpm --filter @fluxy-chat/worker dev\n" +
|
|
286
|
+
" Or use hosted mode:\n" +
|
|
287
|
+
" pnpm setup -- --mode hosted",
|
|
288
|
+
);
|
|
289
|
+
}
|
|
235
290
|
}
|
|
236
291
|
ok(`worker healthy at ${workerUrl}`);
|
|
237
292
|
|
|
@@ -8,7 +8,9 @@ const publicRoomId = import.meta.env.VITE_FLUXYCHAT_PUBLIC_ROOM_ID?.trim();
|
|
|
8
8
|
const configuredRoomId = import.meta.env.VITE_FLUXYCHAT_ROOM_ID?.trim() || "demo";
|
|
9
9
|
|
|
10
10
|
interface FluxySession {
|
|
11
|
-
|
|
11
|
+
workerUrl: string;
|
|
12
|
+
token: string;
|
|
13
|
+
userId: string;
|
|
12
14
|
roomId: string;
|
|
13
15
|
mode: "member" | "guest";
|
|
14
16
|
}
|
|
@@ -30,11 +32,9 @@ function useFluxySession(): {
|
|
|
30
32
|
|
|
31
33
|
if (memberJwt) {
|
|
32
34
|
setSession({
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
token: memberJwt,
|
|
37
|
-
}),
|
|
35
|
+
workerUrl,
|
|
36
|
+
token: memberJwt,
|
|
37
|
+
userId: "demo-user",
|
|
38
38
|
roomId: configuredRoomId,
|
|
39
39
|
mode: "member",
|
|
40
40
|
});
|
|
@@ -50,11 +50,9 @@ function useFluxySession(): {
|
|
|
50
50
|
.then((guest) => {
|
|
51
51
|
if (cancelled) return;
|
|
52
52
|
setSession({
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
token: guest.token,
|
|
57
|
-
}),
|
|
53
|
+
workerUrl,
|
|
54
|
+
token: guest.token,
|
|
55
|
+
userId: guest.userId,
|
|
58
56
|
roomId: guest.roomId,
|
|
59
57
|
mode: "guest",
|
|
60
58
|
});
|
|
@@ -180,7 +178,11 @@ export function App() {
|
|
|
180
178
|
<input value={roomId} onChange={(e) => setRoomId(e.target.value)} />
|
|
181
179
|
</label>
|
|
182
180
|
) : null}
|
|
183
|
-
<FluxyRealtimeProvider
|
|
181
|
+
<FluxyRealtimeProvider
|
|
182
|
+
workerUrl={session.workerUrl}
|
|
183
|
+
authTokenProvider={session.token}
|
|
184
|
+
userId={session.userId}
|
|
185
|
+
>
|
|
184
186
|
<ChatPanel roomId={activeRoomId} />
|
|
185
187
|
</FluxyRealtimeProvider>
|
|
186
188
|
</main>
|