@fluxy-chat/create-fluxy-chat 0.5.13 → 0.5.19

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 (52) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/dist/index.js +9 -4
  3. package/package.json +1 -1
  4. package/readme.md +2 -1
  5. package/templates/basic/package.json +1 -1
  6. package/templates/comments-board/package.json +1 -1
  7. package/templates/deal-room/package.json +1 -1
  8. package/templates/deal-room/src/App.tsx +17 -1
  9. package/templates/discord/package.json +1 -1
  10. package/templates/draw/package.json +1 -1
  11. package/templates/fleet-panel/package.json +1 -1
  12. package/templates/full/README.md +14 -4
  13. package/templates/full/fluxy.hosted.json +15 -0
  14. package/templates/full/package.json +4 -3
  15. package/templates/full/scripts/fluxy-deploy.mjs +53 -0
  16. package/templates/full/scripts/fluxy-setup.mjs +31 -7
  17. package/templates/full/src/App.tsx +14 -0
  18. package/templates/full/src/session.ts +7 -2
  19. package/templates/full/src/vite-env.d.ts +2 -1
  20. package/templates/game-tick/package.json +1 -1
  21. package/templates/iot-panel/package.json +1 -1
  22. package/templates/javascript-live-cursors/package.json +1 -1
  23. package/templates/live-cursors/.env.example +8 -5
  24. package/templates/live-cursors/package.json +2 -2
  25. package/templates/live-cursors/src/App.tsx +19 -2
  26. package/templates/live-cursors/src/vite-env.d.ts +1 -0
  27. package/templates/live-cursors-chat/package.json +1 -1
  28. package/templates/minimal/.env.example +2 -1
  29. package/templates/minimal/package.json +2 -2
  30. package/templates/minimal/src/App.tsx +1 -0
  31. package/templates/polls/.env.example +13 -0
  32. package/templates/polls/README.md +13 -0
  33. package/templates/polls/index.html +12 -0
  34. package/templates/polls/package.json +24 -0
  35. package/templates/polls/src/App.tsx +135 -0
  36. package/templates/polls/src/index.css +85 -0
  37. package/templates/polls/src/main.tsx +10 -0
  38. package/templates/polls/src/session.ts +73 -0
  39. package/templates/polls/src/vite-env.d.ts +13 -0
  40. package/templates/polls/tsconfig.json +21 -0
  41. package/templates/polls/vite.config.ts +7 -0
  42. package/templates/react/.env.example +9 -5
  43. package/templates/react/README.md +7 -32
  44. package/templates/react/package.json +2 -2
  45. package/templates/react/src/App.tsx +31 -23
  46. package/templates/react/src/vite-env.d.ts +1 -0
  47. package/templates/slack/package.json +1 -1
  48. package/templates/telegram/package.json +1 -1
  49. package/templates/tiptap-room/package.json +1 -1
  50. package/templates/voice-stage/package.json +1 -1
  51. package/templates/war-room/package.json +1 -1
  52. package/templates/whiteboard/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,44 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.5.19] - 2026-09-02
4
+
5
+ ### Added
6
+
7
+ - Full template `fluxy.hosted.json` deploys `rooms` templates and extension slots with `pnpm fluxy:deploy`.
8
+ - Pin `@fluxy-chat/sdk@^0.6.8` (`connectInbox`, room extensions).
9
+
10
+ ## [0.5.18] - 2026-09-01
11
+
12
+ ### Added
13
+
14
+ - Hosted `setup:hosted` reads `GET /public/demo-credentials` and writes `VITE_FLUXYCHAT_PUBLISHABLE_KEY`.
15
+ - `pnpm fluxy:deploy` on the full template: `fluxy.hosted.json` → `PUT /admin/projects/:id/publish-config`.
16
+ - React and live-cursors templates prefer `publishableKey` (pin sdk `^0.6.7` / react `^0.1.6`).
17
+
18
+ ## [0.5.17] - 2026-09-01
19
+
20
+ ### Added
21
+
22
+ - Gallery `--example polls`: `createPoll` / `votePoll`. Prefers `VITE_FLUXYCHAT_PUBLISHABLE_KEY`. Documents `POST /polls` `isAnonymous`.
23
+
24
+ ## [0.5.16] - 2026-08-31
25
+
26
+ ### Added
27
+
28
+ - `deal-room` template: Ask agent when `VITE_FLUXYCHAT_AGENT_ID` is set (`invokeAgent`).
29
+
30
+ ## [0.5.15] - 2026-08-31
31
+
32
+ ### Changed
33
+
34
+ - Help text states the scoped package: `npx @fluxy-chat/create-fluxy-chat@latest`. Unscoped `create-fluxy-chat` is not ours.
35
+
36
+ ## [0.5.14] - 2026-08-27
37
+
38
+ ### Changed
39
+
40
+ - Templates pin `@fluxy-chat/sdk@^0.6.5`. Minimal template pins `@fluxy-chat/ui-kit@^0.1.3` and uses guest mode when no member JWT is set. Publish protocol `0.1.5`, sdk `0.6.5`, and ui-kit `0.1.3` **before** this CLI.
41
+
3
42
  ## [0.5.13] - 2026-08-27
4
43
 
5
44
  ### Changed
package/dist/index.js CHANGED
@@ -888,6 +888,7 @@ var GALLERY_EXAMPLES = [
888
888
  "game-tick",
889
889
  "voice-stage",
890
890
  "comments-board",
891
+ "polls",
891
892
  "whiteboard"
892
893
  ];
893
894
  function parseArgs(argv) {
@@ -983,6 +984,9 @@ function parseArgs(argv) {
983
984
  var HELP_TEXT = `
984
985
  ${pc.bold("create-fluxy-chat")} \u2014 Scaffold a FluxyChat app or bot worker
985
986
 
987
+ ${pc.yellow("Use the scoped package:")} npx @fluxy-chat/create-fluxy-chat@latest
988
+ Unscoped ${pc.bold("npx create-fluxy-chat")} is not ours and will 404.
989
+
986
990
  ${pc.bold("Usage:")}
987
991
  npx @fluxy-chat/create-fluxy-chat [project-name] [options]
988
992
 
@@ -1008,7 +1012,7 @@ ${pc.bold("Examples:")}
1008
1012
  ${pc.cyan("npx @fluxy-chat/create-fluxy-chat@latest my-cursors --example live-cursors")}
1009
1013
  ${pc.cyan("npx @fluxy-chat/create-fluxy-chat@latest my-doc --example tiptap-room")}
1010
1014
  ${pc.cyan("npx @fluxy-chat/create-fluxy-chat@latest my-war --example war-room")}
1011
- ${pc.cyan("npx @fluxy-chat/create-fluxy-chat@latest my-iot --example iot-panel")}
1015
+ ${pc.cyan("npx @fluxy-chat/create-fluxy-chat@latest my-polls --example polls")}
1012
1016
  ${pc.cyan("npx @fluxy-chat/create-fluxy-chat@latest my-chat --minimal")}
1013
1017
  ${pc.cyan("npx @fluxy-chat/create-fluxy-chat@latest my-bot --adapter slack")}
1014
1018
  `;
@@ -1084,6 +1088,7 @@ async function main() {
1084
1088
  "game-tick": "# Matchmake + start, then Submit input (not netcode)",
1085
1089
  "voice-stage": "# Open two tabs \u2014 join speaker/listener (signaling, not WebRTC)",
1086
1090
  "comments-board": "# Click the canvas to pin a thread",
1091
+ polls: "# Create a poll, open a second tab, vote",
1087
1092
  "live-cursors-chat": "# Open two tabs \u2014 move and chat",
1088
1093
  whiteboard: "# Open two tabs \u2014 draw strokes (Yjs, not a second CRDT)"
1089
1094
  };
@@ -1092,7 +1097,7 @@ async function main() {
1092
1097
  [
1093
1098
  `cd ${projectName}`,
1094
1099
  "cp .env.example .env",
1095
- "# Set VITE_FLUXYCHAT_WORKER_URL + public room ID or member JWT",
1100
+ "# Set VITE_FLUXYCHAT_WORKER_URL + pk_ (or public room ID / member JWT)",
1096
1101
  `${devCmd2} dev`,
1097
1102
  tryHint
1098
1103
  ].join("\n"),
@@ -1253,7 +1258,7 @@ async function main() {
1253
1258
  const devCmd = config.packageManager === "npm" ? "npm run" : config.packageManager;
1254
1259
  const nextSteps = config.full || config.adapter === "full" ? config.mode === "hosted" ? [
1255
1260
  `cd ${config.name}`,
1256
- `${devCmd} setup:hosted # guest JWT from fluxychat.com`,
1261
+ `${devCmd} setup:hosted # writes pk_ when /public/demo-credentials is on`,
1257
1262
  `${devCmd} dev # http://localhost:5173`,
1258
1263
  `# Keep this project: https://fluxychat.com/onboarding?from=cli`
1259
1264
  ].join("\n") : [
@@ -1266,7 +1271,7 @@ async function main() {
1266
1271
  ].join("\n") : config.adapter === "react" || config.minimal ? [
1267
1272
  `cd ${config.name}`,
1268
1273
  "cp .env.example .env",
1269
- "# Set VITE_FLUXYCHAT_WORKER_URL + JWT or public room ID",
1274
+ "# Set VITE_FLUXYCHAT_WORKER_URL + VITE_FLUXYCHAT_PUBLISHABLE_KEY",
1270
1275
  `${devCmd} dev`,
1271
1276
  "# In the room, send: @assistant hello"
1272
1277
  ].join("\n") : [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluxy-chat/create-fluxy-chat",
3
- "version": "0.5.13",
3
+ "version": "0.5.19",
4
4
  "description": "Scaffold a new FluxyChat bot project with a single command",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/readme.md CHANGED
@@ -36,6 +36,7 @@ npx @fluxy-chat/create-fluxy-chat@latest my-fleet --example fleet-panel
36
36
  npx @fluxy-chat/create-fluxy-chat@latest my-game --example game-tick
37
37
  npx @fluxy-chat/create-fluxy-chat@latest my-stage --example voice-stage
38
38
  npx @fluxy-chat/create-fluxy-chat@latest my-comments --example comments-board
39
+ npx @fluxy-chat/create-fluxy-chat@latest my-polls --example polls
39
40
  npx @fluxy-chat/create-fluxy-chat@latest my-chat --template react -y
40
41
  npx @fluxy-chat/create-fluxy-chat@latest my-bot --adapter slack --pm pnpm
41
42
  ```
@@ -51,7 +52,7 @@ npx @fluxy-chat/create-fluxy-chat@latest my-bot --adapter slack --pm pnpm
51
52
  | `--yes` | `-y` | Skip prompts and accept defaults |
52
53
  | `--full` | | Full stack template: chat + `@assistant` + `pnpm setup` / `pnpm dev` |
53
54
  | `--minimal` | | Chat-only widget (`@fluxy-chat/ui-kit`), no platform modules |
54
- | `--example <name>` | | Gallery: `live-cursors`, `live-cursors-chat`, `javascript-live-cursors`, `tiptap-room`, `war-room`, `iot-panel`, `draw`, `deal-room`, `fleet-panel`, `game-tick`, `voice-stage`, `comments-board`, `whiteboard` |
55
+ | `--example <name>` | | Gallery: `live-cursors`, `live-cursors-chat`, `javascript-live-cursors`, `tiptap-room`, `war-room`, `iot-panel`, `draw`, `deal-room`, `fleet-panel`, `game-tick`, `voice-stage`, `comments-board`, `polls`, `whiteboard` |
55
56
  | `--skip-install` | | Skip dependency installation |
56
57
  | `--no-git` | | Skip git repository initialization |
57
58
  | `--help` | `-h` | Show help |
@@ -9,7 +9,7 @@
9
9
  "type-check": "tsc --noEmit"
10
10
  },
11
11
  "dependencies": {
12
- "@fluxy-chat/sdk": "^0.6.4"
12
+ "@fluxy-chat/sdk": "^0.6.5"
13
13
  },
14
14
  "devDependencies": {
15
15
  "@cloudflare/workers-types": "^4.0.0",
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "@fluxy-chat/react": "^0.1.4",
13
- "@fluxy-chat/sdk": "^0.6.4",
13
+ "@fluxy-chat/sdk": "^0.6.5",
14
14
  "@fluxy-chat/ui": "^0.1.4",
15
15
  "react": "^19.0.0",
16
16
  "react-dom": "^19.0.0",
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "@fluxy-chat/react": "^0.1.4",
13
- "@fluxy-chat/sdk": "^0.6.4",
13
+ "@fluxy-chat/sdk": "^0.6.5",
14
14
  "react": "^19.0.0",
15
15
  "react-dom": "^19.0.0",
16
16
  "zustand": "^5.0.0"
@@ -4,9 +4,10 @@ import { useFluxySession, workerUrl } from "./session";
4
4
 
5
5
  function DealBoard({ roomId }: { roomId: string }) {
6
6
  const { client } = useFluxyChat();
7
- const { messages, sendMessage, connected } = useChat({ roomId, replay: "request" });
7
+ const { messages, sendMessage, connected, invokeAgent } = useChat({ roomId, replay: "request" });
8
8
  const [draft, setDraft] = useState("Approve term sheet?");
9
9
  const [busy, setBusy] = useState(false);
10
+ const agentId = import.meta.env.VITE_FLUXYCHAT_AGENT_ID?.trim();
10
11
 
11
12
  async function propose() {
12
13
  const content = draft.trim();
@@ -45,6 +46,16 @@ function DealBoard({ roomId }: { roomId: string }) {
45
46
  URL.revokeObjectURL(url);
46
47
  }
47
48
 
49
+ async function askAgent() {
50
+ if (!agentId) return;
51
+ setBusy(true);
52
+ try {
53
+ await invokeAgent(agentId, "Summarize this deal thread and list open decisions.");
54
+ } finally {
55
+ setBusy(false);
56
+ }
57
+ }
58
+
48
59
  return (
49
60
  <section className="panel">
50
61
  <header className="chat-header">
@@ -87,6 +98,11 @@ function DealBoard({ roomId }: { roomId: string }) {
87
98
  <button type="button" className="primary" onClick={() => void exportMarkdown()}>
88
99
  Export .md
89
100
  </button>
101
+ {agentId ? (
102
+ <button type="button" disabled={busy} onClick={() => void askAgent()}>
103
+ Ask agent
104
+ </button>
105
+ ) : null}
90
106
  </form>
91
107
  </section>
92
108
  );
@@ -9,7 +9,7 @@
9
9
  "type-check": "tsc --noEmit"
10
10
  },
11
11
  "dependencies": {
12
- "@fluxy-chat/sdk": "^0.6.4",
12
+ "@fluxy-chat/sdk": "^0.6.5",
13
13
  "discord.js": "^14.0.0"
14
14
  },
15
15
  "devDependencies": {
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "@fluxy-chat/react": "^0.1.4",
13
- "@fluxy-chat/sdk": "^0.6.4",
13
+ "@fluxy-chat/sdk": "^0.6.5",
14
14
  "react": "^19.0.0",
15
15
  "react-dom": "^19.0.0",
16
16
  "zustand": "^5.0.0"
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "@fluxy-chat/react": "^0.1.4",
13
- "@fluxy-chat/sdk": "^0.6.4",
13
+ "@fluxy-chat/sdk": "^0.6.5",
14
14
  "react": "^19.0.0",
15
15
  "react-dom": "^19.0.0",
16
16
  "zustand": "^5.0.0"
@@ -1,6 +1,6 @@
1
1
  # FluxyChat full stack starter
2
2
 
3
- Chat + AI agent in a Vite app. Hosted mode starts with a short local tour, then Clerk, then your own room.
3
+ Chat + AI agent in a Vite app. Hosted: `pnpm setup:hosted` writes a public `pk_` when the Worker exposes `GET /public/demo-credentials`. Otherwise copy a `pk_` from the console, or sign in for a private assistant room.
4
4
 
5
5
  ## Quick start
6
6
 
@@ -14,7 +14,16 @@ pnpm setup:hosted
14
14
  pnpm dev
15
15
  ```
16
16
 
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.
17
+ If demo credentials are configured on the Worker, chat starts with `publishableKey` (no Clerk). Sign in later for a private assistant room.
18
+
19
+ **Hosted overlay (one file deploy for deny / rooms / extension slots):**
20
+
21
+ ```bash
22
+ # fluxy.hosted.json → PUT /admin/projects/:id/publish-config
23
+ FLUXY_WORKER_URL=https://api.fluxychat.com FLUXY_ADMIN_JWT=eyJ... FLUXY_PROJECT_ID=prj_... pnpm fluxy:deploy
24
+ ```
25
+
26
+ This is not Worker-bundled `onPublish` callbacks. Those run in `fluxy.config.ts` on self-host. Hosted extension slots are declared `kv` / `counter` on the room Durable Object (`GET /rooms/:id/extensions`).
18
27
 
19
28
  **Self-host (your Worker):**
20
29
 
@@ -37,14 +46,15 @@ If the Worker is down, `setup:local` asks for the URL. Merge `.fluxy/worker.dev.
37
46
 
38
47
  | Command | Description |
39
48
  |---------|-------------|
40
- | `pnpm setup:hosted` | Writes worker + console URLs. Auth happens in the browser via Clerk. |
49
+ | `pnpm setup:hosted` | Worker URL + public `pk_` when `/public/demo-credentials` is configured |
50
+ | `pnpm fluxy:deploy` | PUT hosted publish-config from `fluxy.hosted.json` |
41
51
  | `pnpm setup:local` / `pnpm setup:self-host` | `POST /dev/provision` on your Worker |
42
52
  | `pnpm doctor` | Health check |
43
53
  | `pnpm dev` | Start Vite |
44
54
 
45
55
  ## Environment
46
56
 
47
- `.env` from `pnpm setup:hosted` only needs worker and console URLs. Member JWT, room, and agent come from Clerk after sign in.
57
+ `.env` from `pnpm setup:hosted` includes worker URL and, when configured, `VITE_FLUXYCHAT_PUBLISHABLE_KEY`. Clerk still creates a private assistant room.
48
58
 
49
59
  Dashboard: [fluxychat.com/dashboard](https://fluxychat.com/dashboard)
50
60
 
@@ -0,0 +1,15 @@
1
+ {
2
+ "denySubstrings": [],
3
+ "guestCanPublish": true,
4
+ "iotAutoAgentId": null,
5
+ "rooms": {
6
+ "support-*": {
7
+ "anonymous": false,
8
+ "guestCanPublish": false
9
+ },
10
+ "room-*": {
11
+ "anonymous": true,
12
+ "extensions": [{ "id": "state", "kind": "kv" }]
13
+ }
14
+ }
15
+ }
@@ -8,6 +8,7 @@
8
8
  "setup:hosted": "node scripts/fluxy-setup.mjs --mode hosted",
9
9
  "setup:local": "node scripts/fluxy-setup.mjs --mode local",
10
10
  "setup:self-host": "node scripts/fluxy-setup.mjs --mode self-host",
11
+ "fluxy:deploy": "node scripts/fluxy-deploy.mjs",
11
12
  "doctor": "node scripts/fluxy-doctor.mjs",
12
13
  "dev": "node scripts/fluxy-dev.mjs",
13
14
  "dev:app": "vite",
@@ -16,8 +17,8 @@
16
17
  "postinstall": "node scripts/hoist-fluxy-sdk.mjs"
17
18
  },
18
19
  "dependencies": {
19
- "@fluxy-chat/react": "^0.1.4",
20
- "@fluxy-chat/sdk": "^0.6.4",
20
+ "@fluxy-chat/react": "^0.1.6",
21
+ "@fluxy-chat/sdk": "^0.6.8",
21
22
  "@fluxy-chat/ui": "^0.1.4",
22
23
  "class-variance-authority": "^0.7.1",
23
24
  "clsx": "^2.1.1",
@@ -40,7 +41,7 @@
40
41
  },
41
42
  "pnpm": {
42
43
  "overrides": {
43
- "@fluxy-chat/sdk": "^0.6.4"
44
+ "@fluxy-chat/sdk": "^0.6.8"
44
45
  }
45
46
  }
46
47
  }
@@ -0,0 +1,53 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Hosted overlay: PUT /admin/projects/:id/publish-config
4
+ * Serializes one fluxy.hosted.json (deny, guest publish, iotAutoAgentId, rooms
5
+ * templates + extension slots). Does not upload Worker callbacks.
6
+ *
7
+ * FLUXY_WORKER_URL=https://api.fluxychat.com \
8
+ * FLUXY_ADMIN_JWT=eyJ... \
9
+ * FLUXY_PROJECT_ID=prj_... \
10
+ * pnpm fluxy:deploy
11
+ */
12
+ import { existsSync, readFileSync } from "node:fs";
13
+ import { dirname, join, resolve } from "node:path";
14
+ import { fileURLToPath } from "node:url";
15
+
16
+ const root = resolve(dirname(fileURLToPath(import.meta.url)), "..");
17
+ const configPath = join(root, "fluxy.hosted.json");
18
+
19
+ const workerUrl = (process.env.FLUXY_WORKER_URL || process.env.VITE_FLUXYCHAT_WORKER_URL || "").replace(/\/$/, "");
20
+ const adminJwt = process.env.FLUXY_ADMIN_JWT || "";
21
+ const projectId = process.env.FLUXY_PROJECT_ID || process.env.VITE_FLUXYCHAT_PROJECT_ID || "";
22
+
23
+ function fail(msg) {
24
+ console.error(`fluxy deploy: ${msg}`);
25
+ process.exit(1);
26
+ }
27
+
28
+ if (!workerUrl) fail("set FLUXY_WORKER_URL");
29
+ if (!adminJwt) fail("set FLUXY_ADMIN_JWT (owner/admin JWT, not pk_)");
30
+ if (!projectId) fail("set FLUXY_PROJECT_ID");
31
+ if (!existsSync(configPath)) fail(`missing ${configPath}`);
32
+
33
+ const hosted = JSON.parse(readFileSync(configPath, "utf8"));
34
+ const body = {
35
+ denySubstrings: Array.isArray(hosted.denySubstrings) ? hosted.denySubstrings : [],
36
+ guestCanPublish: hosted.guestCanPublish !== false,
37
+ iotAutoAgentId: hosted.iotAutoAgentId ?? null,
38
+ rooms: hosted.rooms && typeof hosted.rooms === "object" ? hosted.rooms : {},
39
+ };
40
+
41
+ const url = `${workerUrl}/admin/projects/${encodeURIComponent(projectId)}/publish-config`;
42
+ const res = await fetch(url, {
43
+ method: "PUT",
44
+ headers: {
45
+ Authorization: `Bearer ${adminJwt}`,
46
+ "Content-Type": "application/json",
47
+ },
48
+ body: JSON.stringify(body),
49
+ });
50
+ const text = await res.text();
51
+ if (!res.ok) fail(`${res.status} ${text}`);
52
+ console.log("publish-config updated");
53
+ console.log(text);
@@ -194,6 +194,7 @@ function writeEnv(vars) {
194
194
  const lines = [
195
195
  "# Auto-generated by pnpm setup — do not commit secrets",
196
196
  `VITE_FLUXYCHAT_WORKER_URL=${vars.workerUrl}`,
197
+ `VITE_FLUXYCHAT_PUBLISHABLE_KEY=${vars.publishableKey || ""}`,
197
198
  `VITE_FLUXYCHAT_MEMBER_JWT=${vars.token}`,
198
199
  `VITE_FLUXYCHAT_ROOM_ID=${vars.roomId}`,
199
200
  `VITE_FLUXYCHAT_AGENT_ID=${vars.agentId}`,
@@ -218,13 +219,30 @@ async function setupHosted() {
218
219
  process.env.FLUXY_WORKER_URL ||
219
220
  HOSTED_WORKER_DEFAULT;
220
221
  const consoleUrl = process.env.FLUXY_CONSOLE_URL || HOSTED_CONSOLE_DEFAULT;
222
+ const fromEnvPk = (process.env.FLUXY_PUBLISHABLE_KEY || process.env.VITE_FLUXYCHAT_PUBLISHABLE_KEY || "").trim();
221
223
 
222
224
  console.log(dim(` mode: hosted · worker: ${workerUrl}`));
223
225
 
226
+ let publishableKey = fromEnvPk.startsWith("pk_") ? fromEnvPk : "";
227
+ let roomId = process.env.VITE_FLUXYCHAT_ROOM_ID?.trim() || "";
228
+ if (!publishableKey) {
229
+ try {
230
+ const demo = await getJson(workerUrl, "/public/demo-credentials");
231
+ if (demo?.publishableKey?.startsWith("pk_")) {
232
+ publishableKey = demo.publishableKey;
233
+ roomId = demo.roomId || roomId || "general";
234
+ ok("wrote public demo pk_ (GET /public/demo-credentials)");
235
+ }
236
+ } catch {
237
+ /* demo not configured on this Worker */
238
+ }
239
+ }
240
+
224
241
  writeEnv({
225
242
  workerUrl,
243
+ publishableKey,
226
244
  token: "",
227
- roomId: "",
245
+ roomId: roomId || (publishableKey ? "general" : ""),
228
246
  agentId: "",
229
247
  agentHandle: "@assistant",
230
248
  projectId: "",
@@ -236,17 +254,22 @@ async function setupHosted() {
236
254
  mode: "hosted",
237
255
  workerUrl,
238
256
  projectId: null,
239
- roomId: null,
257
+ roomId: roomId || null,
240
258
  agentId: null,
241
259
  agentHandle: "@assistant",
242
260
  setupAt: new Date().toISOString(),
243
- auth: "clerk",
261
+ auth: publishableKey ? "publishableKey" : "clerk",
244
262
  });
245
263
 
246
- ok("wrote hosted endpoints (sign in with Clerk on pnpm dev)");
247
- console.log(dim(" Run: pnpm dev"));
248
- console.log(dim(" Sign in with Clerk. We create your project and assistant room."));
249
- console.log(dim(` Console: ${consoleUrl}/onboarding`));
264
+ if (publishableKey) {
265
+ ok("public room is ready. pnpm dev — no Clerk required");
266
+ console.log(dim(" Sign in later for a private assistant room: Open dashboard"));
267
+ } else {
268
+ ok("wrote hosted endpoints (copy a pk_ from the console, or sign in on pnpm dev)");
269
+ console.log(dim(" Run: pnpm dev"));
270
+ console.log(dim(` Console: ${consoleUrl}/onboarding`));
271
+ console.log(dim(" Hosted ops: set PUBLIC_DEMO_PUBLISHABLE_KEY on the Worker for a no-account path"));
272
+ }
250
273
  }
251
274
 
252
275
  async function setupLocal() {
@@ -323,6 +346,7 @@ async function setupLocal() {
323
346
 
324
347
  writeEnv({
325
348
  workerUrl,
349
+ publishableKey: provision.publishableKey || "",
326
350
  token: auth.token,
327
351
  roomId,
328
352
  agentId: agent.id,
@@ -4,6 +4,7 @@ import { ChatWindow } from "@fluxy-chat/ui";
4
4
  import { loadCliSession, type CliSession } from "./session";
5
5
 
6
6
  const envWorkerUrl = import.meta.env.VITE_FLUXYCHAT_WORKER_URL?.trim() ?? "";
7
+ const envPublishableKey = import.meta.env.VITE_FLUXYCHAT_PUBLISHABLE_KEY?.trim() ?? "";
7
8
  const envJwt = import.meta.env.VITE_FLUXYCHAT_MEMBER_JWT?.trim() ?? "";
8
9
  const envRoomId = import.meta.env.VITE_FLUXYCHAT_ROOM_ID?.trim() ?? "";
9
10
  const envAgentId = import.meta.env.VITE_FLUXYCHAT_AGENT_ID?.trim() ?? "";
@@ -31,6 +32,18 @@ function dashboardHref(): string {
31
32
  }
32
33
 
33
34
  function sessionFromEnv(): CliSession | null {
35
+ if (envWorkerUrl && envPublishableKey.startsWith("pk_") && envRoomId) {
36
+ return {
37
+ workerUrl: envWorkerUrl,
38
+ publishableKey: envPublishableKey,
39
+ memberJwt: envJwt || undefined,
40
+ roomId: envRoomId,
41
+ agentId: envAgentId,
42
+ agentHandle: envAgentHandle,
43
+ projectId: envProjectId,
44
+ userId: envUserId || "guest",
45
+ };
46
+ }
34
47
  if (!envWorkerUrl || !envJwt || !envRoomId) return null;
35
48
  return {
36
49
  workerUrl: envWorkerUrl,
@@ -226,6 +239,7 @@ export function App() {
226
239
  <div className="page">
227
240
  <FluxyRealtimeProvider
228
241
  workerUrl={session.workerUrl}
242
+ publishableKey={session.memberJwt ? undefined : session.publishableKey}
229
243
  authTokenProvider={session.memberJwt}
230
244
  userId={session.userId}
231
245
  >
@@ -2,7 +2,8 @@ const STORAGE_KEY = "fluxy-cli-session";
2
2
 
3
3
  export interface CliSession {
4
4
  workerUrl: string;
5
- memberJwt: string;
5
+ memberJwt?: string;
6
+ publishableKey?: string;
6
7
  roomId: string;
7
8
  agentId: string;
8
9
  agentHandle: string;
@@ -15,7 +16,11 @@ export interface CliSession {
15
16
  function isCliSession(value: unknown): value is CliSession {
16
17
  if (!value || typeof value !== "object") return false;
17
18
  const v = value as Record<string, unknown>;
18
- return typeof v.memberJwt === "string" && v.memberJwt.length > 0 && typeof v.workerUrl === "string";
19
+ if (typeof v.workerUrl !== "string" || v.workerUrl.length === 0) return false;
20
+ if (typeof v.roomId !== "string" || v.roomId.length === 0) return false;
21
+ const hasMember = typeof v.memberJwt === "string" && v.memberJwt.length > 0;
22
+ const hasPk = typeof v.publishableKey === "string" && v.publishableKey.startsWith("pk_");
23
+ return hasMember || hasPk;
19
24
  }
20
25
 
21
26
  export function readCliSessionFromHash(): CliSession | null {
@@ -2,7 +2,8 @@
2
2
 
3
3
  interface ImportMetaEnv {
4
4
  readonly VITE_FLUXYCHAT_WORKER_URL: string;
5
- readonly VITE_FLUXYCHAT_MEMBER_JWT: string;
5
+ readonly VITE_FLUXYCHAT_PUBLISHABLE_KEY?: string;
6
+ readonly VITE_FLUXYCHAT_MEMBER_JWT?: string;
6
7
  readonly VITE_FLUXYCHAT_ROOM_ID: string;
7
8
  readonly VITE_FLUXYCHAT_AGENT_ID: string;
8
9
  readonly VITE_FLUXYCHAT_AGENT_HANDLE: string;
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "@fluxy-chat/react": "^0.1.4",
13
- "@fluxy-chat/sdk": "^0.6.4",
13
+ "@fluxy-chat/sdk": "^0.6.5",
14
14
  "react": "^19.0.0",
15
15
  "react-dom": "^19.0.0",
16
16
  "zustand": "^5.0.0"
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "@fluxy-chat/react": "^0.1.4",
13
- "@fluxy-chat/sdk": "^0.6.4",
13
+ "@fluxy-chat/sdk": "^0.6.5",
14
14
  "react": "^19.0.0",
15
15
  "react-dom": "^19.0.0",
16
16
  "zustand": "^5.0.0"
@@ -9,7 +9,7 @@
9
9
  "preview": "vite preview"
10
10
  },
11
11
  "dependencies": {
12
- "@fluxy-chat/sdk": "^0.6.4"
12
+ "@fluxy-chat/sdk": "^0.6.5"
13
13
  },
14
14
  "devDependencies": {
15
15
  "typescript": "^5.6.0",
@@ -1,9 +1,12 @@
1
1
  # FluxyChat worker URL (hosted or self-hosted)
2
- VITE_FLUXYCHAT_WORKER_URL=https://your-worker.example.workers.dev
2
+ VITE_FLUXYCHAT_WORKER_URL=https://api.fluxychat.com
3
3
 
4
- # Option A — member JWT (~2 min via onboarding)
5
- VITE_FLUXYCHAT_MEMBER_JWT=eyJ...
6
- VITE_FLUXYCHAT_ROOM_ID=demo
4
+ # Option A — public room (pin sdk 0.6.7+)
5
+ VITE_FLUXYCHAT_PUBLISHABLE_KEY=pk_...
6
+ VITE_FLUXYCHAT_ROOM_ID=general
7
7
 
8
- # Option B — public guest room (~60s, no signup)
8
+ # Option B — member JWT
9
+ # VITE_FLUXYCHAT_MEMBER_JWT=eyJ...
10
+
11
+ # Option C — guest session without pk_
9
12
  # VITE_FLUXYCHAT_PUBLIC_ROOM_ID=your-public-room-id
@@ -9,8 +9,8 @@
9
9
  "preview": "vite preview"
10
10
  },
11
11
  "dependencies": {
12
- "@fluxy-chat/react": "^0.1.4",
13
- "@fluxy-chat/sdk": "^0.6.4",
12
+ "@fluxy-chat/react": "^0.1.6",
13
+ "@fluxy-chat/sdk": "^0.6.7",
14
14
  "react": "^19.0.0",
15
15
  "react-dom": "^19.0.0",
16
16
  "zustand": "^5.0.0"
@@ -3,6 +3,7 @@ import { FluxyChatClient } from "@fluxy-chat/sdk";
3
3
  import { FluxyRealtimeProvider, useChat } from "@fluxy-chat/react";
4
4
 
5
5
  const workerUrl = import.meta.env.VITE_FLUXYCHAT_WORKER_URL?.trim();
6
+ const publishableKey = import.meta.env.VITE_FLUXYCHAT_PUBLISHABLE_KEY?.trim();
6
7
  const memberJwt = import.meta.env.VITE_FLUXYCHAT_MEMBER_JWT?.trim();
7
8
  const publicRoomId = import.meta.env.VITE_FLUXYCHAT_PUBLIC_ROOM_ID?.trim();
8
9
  const configuredRoomId = import.meta.env.VITE_FLUXYCHAT_ROOM_ID?.trim() || "demo";
@@ -149,6 +150,23 @@ function hashCode(value: string): number {
149
150
  }
150
151
 
151
152
  export function App() {
153
+ const pkRoomId = publicRoomId || configuredRoomId;
154
+ if (workerUrl && publishableKey) {
155
+ return (
156
+ <main className="shell">
157
+ <h1>Live cursors</h1>
158
+ <p className="mode-badge">publishableKey · two tabs, same room</p>
159
+ <FluxyRealtimeProvider workerUrl={workerUrl} publishableKey={publishableKey}>
160
+ <CursorCanvas roomId={pkRoomId} selfUserId="guest" />
161
+ </FluxyRealtimeProvider>
162
+ </main>
163
+ );
164
+ }
165
+
166
+ return <MemberOrGuestCursors />;
167
+ }
168
+
169
+ function MemberOrGuestCursors() {
152
170
  const { session, loading, error } = useFluxySession();
153
171
  const [roomId, setRoomId] = useState(configuredRoomId);
154
172
 
@@ -166,8 +184,7 @@ export function App() {
166
184
  <code>VITE_FLUXYCHAT_WORKER_URL</code>.
167
185
  </p>
168
186
  <p>
169
- Then either <code>VITE_FLUXYCHAT_MEMBER_JWT</code> or{" "}
170
- <code>VITE_FLUXYCHAT_PUBLIC_ROOM_ID</code>.
187
+ Then <code>VITE_FLUXYCHAT_PUBLISHABLE_KEY</code>, a member JWT, or a public room id.
171
188
  </p>
172
189
  </main>
173
190
  );
@@ -2,6 +2,7 @@
2
2
 
3
3
  interface ImportMetaEnv {
4
4
  readonly VITE_FLUXYCHAT_WORKER_URL: string;
5
+ readonly VITE_FLUXYCHAT_PUBLISHABLE_KEY?: string;
5
6
  readonly VITE_FLUXYCHAT_MEMBER_JWT?: string;
6
7
  readonly VITE_FLUXYCHAT_ROOM_ID?: string;
7
8
  readonly VITE_FLUXYCHAT_PUBLIC_ROOM_ID?: string;
@@ -10,7 +10,7 @@
10
10
  },
11
11
  "dependencies": {
12
12
  "@fluxy-chat/react": "^0.1.4",
13
- "@fluxy-chat/sdk": "^0.6.4",
13
+ "@fluxy-chat/sdk": "^0.6.5",
14
14
  "react": "^19.0.0",
15
15
  "react-dom": "^19.0.0",
16
16
  "zustand": "^5.0.0"
@@ -1,3 +1,4 @@
1
1
  VITE_FLUXYCHAT_WORKER_URL=https://your-worker.example.com
2
- VITE_FLUXYCHAT_MEMBER_JWT=your-member-jwt
2
+ # Optional: omit to join the public room as a guest (no JWT mint).
3
+ # VITE_FLUXYCHAT_MEMBER_JWT=
3
4
  VITE_FLUXYCHAT_ROOM_ID=general