phibelle-kit 1.0.13 → 1.0.15

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 (59) hide show
  1. package/dist/commands/clone-scene.js +2 -4
  2. package/dist/commands/watch-scene.js +3 -7
  3. package/dist/index.js +1 -1
  4. package/dist/lib/{convex-client.js → api/convex-client.js} +2 -2
  5. package/dist/lib/api/index.d.ts +2 -0
  6. package/dist/lib/api/index.js +2 -0
  7. package/dist/lib/{conf.js → auth/conf.js} +5 -3
  8. package/dist/lib/auth/index.d.ts +2 -0
  9. package/dist/lib/auth/index.js +2 -0
  10. package/dist/lib/{wait-for-token.js → auth/wait-for-token.js} +1 -1
  11. package/dist/lib/{first-run-setup.d.ts → scene/first-run-setup.d.ts} +1 -6
  12. package/dist/lib/{first-run-setup.js → scene/first-run-setup.js} +3 -3
  13. package/dist/lib/scene/index.d.ts +4 -0
  14. package/dist/lib/scene/index.js +4 -0
  15. package/dist/lib/{scene-packaging.js → scene/scene-packaging.js} +3 -3
  16. package/dist/lib/{script-watcher.d.ts → scene/script-watcher.d.ts} +0 -5
  17. package/dist/lib/types.d.ts +6 -0
  18. package/dist/scene-setup/npm-package.d.ts +1 -1
  19. package/dist/scene-setup/npm-package.js +7 -4
  20. package/dist/scene-setup/setup.d.ts +1 -12
  21. package/dist/scene-setup/setup.js +14 -15
  22. package/dist/scene-setup/tsconfig-file.d.ts +1 -0
  23. package/dist/scene-setup/tsconfig-file.js +9 -0
  24. package/package.json +1 -1
  25. package/dist/clone-scene.d.ts +0 -5
  26. package/dist/clone-scene.js +0 -39
  27. package/dist/commands/hello.d.ts +0 -1
  28. package/dist/commands/hello.js +0 -10
  29. package/dist/commands/login.d.ts +0 -1
  30. package/dist/commands/login.js +0 -75
  31. package/dist/commands/logout.d.ts +0 -1
  32. package/dist/commands/logout.js +0 -16
  33. package/dist/constants.d.ts +0 -4
  34. package/dist/constants.js +0 -6
  35. package/dist/lib/constants.d.ts +0 -4
  36. package/dist/lib/constants.js +0 -6
  37. package/dist/lib/folder.d.ts +0 -3
  38. package/dist/lib/folder.js +0 -33
  39. package/dist/package/global-module.d.ts +0 -1
  40. package/dist/package/global-module.js +0 -117
  41. package/dist/package/npm-package.d.ts +0 -1
  42. package/dist/package/npm-package.js +0 -16
  43. package/dist/package/phibelle-module.d.ts +0 -1
  44. package/dist/package/phibelle-module.js +0 -122
  45. package/dist/package/setup.d.ts +0 -4
  46. package/dist/package/setup.js +0 -45
  47. package/dist/watch-scene.d.ts +0 -1
  48. package/dist/watch-scene.js +0 -103
  49. /package/dist/lib/{convex-client.d.ts → api/convex-client.d.ts} +0 -0
  50. /package/dist/lib/{public-env.d.ts → api/public-env.d.ts} +0 -0
  51. /package/dist/lib/{public-env.js → api/public-env.js} +0 -0
  52. /package/dist/lib/{conf.d.ts → auth/conf.d.ts} +0 -0
  53. /package/dist/lib/{wait-for-token.d.ts → auth/wait-for-token.d.ts} +0 -0
  54. /package/dist/lib/{file-system.d.ts → scene/file-system.d.ts} +0 -0
  55. /package/dist/lib/{file-system.js → scene/file-system.js} +0 -0
  56. /package/dist/lib/{scene-packaging.d.ts → scene/scene-packaging.d.ts} +0 -0
  57. /package/dist/lib/{script-watcher.js → scene/script-watcher.js} +0 -0
  58. /package/dist/{package → scene-setup}/agents-md.d.ts +0 -0
  59. /package/dist/{package → scene-setup}/agents-md.js +0 -0
@@ -1,10 +1,8 @@
1
1
  import chalk from "chalk";
2
2
  import * as path from "path";
3
- import { queryOnce } from "../lib/convex-client.js";
3
+ import { queryOnce, BASE_URL } from "../lib/api/index.js";
4
4
  import { api } from "../convex/_generated/api.js";
5
- import { BASE_URL } from "../lib/public-env.js";
6
- import { sceneNameToFolder } from "../lib/file-system.js";
7
- import { setupSceneFiles, unpackageWithLogging } from "../lib/first-run-setup.js";
5
+ import { sceneNameToFolder, setupSceneFiles, unpackageWithLogging } from "../lib/scene/index.js";
8
6
  /**
9
7
  * Clone: one-time fetch of scene data. Creates scene dir, writes scene.json,
10
8
  * sets up package.json/global.d.ts, unpacks scripts. No watcher, no opening folder.
@@ -1,15 +1,11 @@
1
1
  import chalk from "chalk";
2
2
  import * as path from "path";
3
3
  import * as fs from "fs";
4
- import { subscribeToQuery } from "../lib/convex-client.js";
4
+ import { subscribeToQuery, BASE_URL } from "../lib/api/index.js";
5
5
  import { api } from "../convex/_generated/api.js";
6
- import { getSessionId } from "../lib/conf.js";
7
- import { findSceneDirBySceneId, sceneNameToFolder } from "../lib/file-system.js";
8
- import { unpackageScene } from "../lib/scene-packaging.js";
9
- import { createScriptWatcher } from "../lib/script-watcher.js";
10
- import { runFirstUpdateSetup } from "../lib/first-run-setup.js";
6
+ import { getSessionId } from "../lib/auth/index.js";
7
+ import { findSceneDirBySceneId, sceneNameToFolder, unpackageScene, createScriptWatcher, runFirstUpdateSetup, } from "../lib/scene/index.js";
11
8
  import { toErrorMessage } from "../lib/utils.js";
12
- import { BASE_URL } from "../lib/public-env.js";
13
9
  function createWatchCallbacks() {
14
10
  return {
15
11
  onPushed: (file) => console.log(chalk.green(" ✓ Pushed " + file + " to Convex")),
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import chalk from "chalk";
3
3
  import { watchSceneCommand } from "./commands/watch-scene.js";
4
4
  import { cloneSceneCommand } from "./commands/clone-scene.js";
5
- import { waitForToken } from "./lib/wait-for-token.js";
5
+ import { waitForToken } from "./lib/auth/wait-for-token.js";
6
6
  const USAGE = " Usage: phibelle-kit clone <sceneId> | phibelle-kit watch <sceneId>";
7
7
  async function main() {
8
8
  const command = process.argv[2];
@@ -1,6 +1,6 @@
1
1
  import { ConvexClient } from "convex/browser";
2
2
  import { CONVEX_URL } from "./public-env.js";
3
- import { getStoredToken } from "./conf.js";
3
+ import { getStoredToken } from "../auth/conf.js";
4
4
  if (!CONVEX_URL) {
5
5
  throw new Error("Missing CONVEX_URL environment variable. " +
6
6
  "Set it to your Convex deployment URL (e.g., from NEXT_PUBLIC_CONVEX_URL in your phibelle project)");
@@ -8,7 +8,7 @@ if (!CONVEX_URL) {
8
8
  export function createConvexClient() {
9
9
  const token = getStoredToken();
10
10
  if (!token) {
11
- throw new Error("Not authenticated. Please run 'phibelle login' first.");
11
+ throw new Error("Not authenticated. Run your command again and complete the browser sign-in when prompted.");
12
12
  }
13
13
  const client = new ConvexClient(CONVEX_URL);
14
14
  client.setAuth(() => Promise.resolve(token));
@@ -0,0 +1,2 @@
1
+ export { createConvexClient, subscribeToQuery, queryOnce, mutateOnce } from "./convex-client.js";
2
+ export { CONVEX_URL, BASE_URL } from "./public-env.js";
@@ -0,0 +1,2 @@
1
+ export { createConvexClient, subscribeToQuery, queryOnce, mutateOnce } from "./convex-client.js";
2
+ export { CONVEX_URL, BASE_URL } from "./public-env.js";
@@ -3,14 +3,16 @@ import Conf from "conf";
3
3
  const CONFIG = new Conf({
4
4
  projectName: "phibelle-kit",
5
5
  });
6
+ /** Auth token kept in memory only (session); never persisted for security. */
7
+ let sessionToken;
6
8
  export function setToken(token) {
7
- CONFIG.set("authToken", token);
9
+ sessionToken = token;
8
10
  }
9
11
  export function getStoredToken() {
10
- return CONFIG.get("authToken");
12
+ return sessionToken;
11
13
  }
12
14
  export function clearToken() {
13
- CONFIG.delete("authToken");
15
+ sessionToken = undefined;
14
16
  }
15
17
  /** Stable session ID for this CLI instance; used so we can skip re-unpack when we were the ones who saved. */
16
18
  export function getSessionId() {
@@ -0,0 +1,2 @@
1
+ export { getStoredToken, setToken, clearToken, getSessionId } from "./conf.js";
2
+ export { waitForToken } from "./wait-for-token.js";
@@ -0,0 +1,2 @@
1
+ export { getStoredToken, setToken, clearToken, getSessionId } from "./conf.js";
2
+ export { waitForToken } from "./wait-for-token.js";
@@ -1,6 +1,6 @@
1
1
  import http from "node:http";
2
2
  import { setToken } from "./conf.js";
3
- import { BASE_URL } from "./public-env.js";
3
+ import { BASE_URL } from "../api/public-env.js";
4
4
  import open from "open";
5
5
  const PORT = 13131;
6
6
  export function waitForToken() {
@@ -1,10 +1,5 @@
1
1
  import { type ScriptWatcher } from "./script-watcher.js";
2
- import type { Scene } from "./types.js";
3
- export type WatchCallbacks = {
4
- onPushed: (file: string) => void;
5
- onNewEntity: (engineId: number, fileName?: string) => void;
6
- onError: (file: string, err: Error) => void;
7
- };
2
+ import type { Scene, WatchCallbacks } from "../types.js";
8
3
  /** Create scene dir, write scene.json, setup package.json/global.d.ts; log install hint if needed. */
9
4
  export declare function setupSceneFiles(scene: Scene, sceneDir: string, ignoreHint?: boolean): void;
10
5
  /** Unpackage scene scripts; log success or hint on failure. */
@@ -1,12 +1,12 @@
1
1
  import chalk from "chalk";
2
2
  import * as path from "path";
3
3
  import * as fs from "fs";
4
- import { BASE_URL } from "./public-env.js";
4
+ import { BASE_URL } from "../api/public-env.js";
5
5
  import { createFolder } from "./file-system.js";
6
6
  import { unpackageScene } from "./scene-packaging.js";
7
7
  import { createScriptWatcher } from "./script-watcher.js";
8
- import { setupSceneDirectory, getInstallHint } from "../package/setup.js";
9
- import { toErrorMessage } from "./utils.js";
8
+ import { setupSceneDirectory, getInstallHint } from "../../scene-setup/setup.js";
9
+ import { toErrorMessage } from "../utils.js";
10
10
  const INSTALL_HINT_LABEL = " ⚠ Install dependencies for types and intellisense:";
11
11
  /** Create scene dir, write scene.json, setup package.json/global.d.ts; log install hint if needed. */
12
12
  export function setupSceneFiles(scene, sceneDir, ignoreHint = false) {
@@ -0,0 +1,4 @@
1
+ export { setupSceneFiles, unpackageWithLogging, runFirstUpdateSetup } from "./first-run-setup.js";
2
+ export { createScriptWatcher, type ScriptWatcher } from "./script-watcher.js";
3
+ export { findSceneDirBySceneId, sceneNameToFolder, getSceneAppDir, createFolder, folderExists, SCENE_APP_DIR, } from "./file-system.js";
4
+ export { unpackageScene, entityNameToSlug, slugToEntityName, ENTITY_FOLDER_RE, ENTITY_SCRIPT_FILE, ENTITY_PROPERTIES_FILE, ENTITY_TRANSFORMS_FILE, SCENE_SCRIPT_FILE, SCENE_PROPERTIES_FILE, } from "./scene-packaging.js";
@@ -0,0 +1,4 @@
1
+ export { setupSceneFiles, unpackageWithLogging, runFirstUpdateSetup } from "./first-run-setup.js";
2
+ export { createScriptWatcher } from "./script-watcher.js";
3
+ export { findSceneDirBySceneId, sceneNameToFolder, getSceneAppDir, createFolder, folderExists, SCENE_APP_DIR, } from "./file-system.js";
4
+ export { unpackageScene, entityNameToSlug, slugToEntityName, ENTITY_FOLDER_RE, ENTITY_SCRIPT_FILE, ENTITY_PROPERTIES_FILE, ENTITY_TRANSFORMS_FILE, SCENE_SCRIPT_FILE, SCENE_PROPERTIES_FILE, } from "./scene-packaging.js";
@@ -1,9 +1,9 @@
1
1
  import * as fs from "fs/promises";
2
2
  import * as path from "path";
3
3
  import { getSceneAppDir } from "./file-system.js";
4
- import { queryOnce, mutateOnce } from "./convex-client.js";
5
- import { api } from "../convex/_generated/api.js";
6
- import { getSessionId } from "./conf.js";
4
+ import { queryOnce, mutateOnce } from "../api/convex-client.js";
5
+ import { api } from "../../convex/_generated/api.js";
6
+ import { getSessionId } from "../auth/conf.js";
7
7
  /** Scene-level file names in app/. */
8
8
  export const SCENE_SCRIPT_FILE = "scene-script.tsx";
9
9
  export const SCENE_PROPERTIES_FILE = "scene-properties.json";
@@ -1,9 +1,4 @@
1
1
  export type ScriptWatcher = {
2
2
  close: () => void;
3
3
  };
4
- export type WatchCallbacks = {
5
- onPushed: (file: string) => void;
6
- onNewEntity: (engineId: number, fileName?: string) => void;
7
- onError: (file: string, err: Error) => void;
8
- };
9
4
  export declare function createScriptWatcher(sceneId: string, sceneDir: string, onPushed: (file: string) => void, onNewEntity: (engineId: number, fileName?: string) => void, onError: (file: string, err: Error) => void): ScriptWatcher;
@@ -60,3 +60,9 @@ export type PhibelleScene = {
60
60
  };
61
61
  version: string;
62
62
  };
63
+ /** Callbacks used by script watcher and first-run setup. */
64
+ export type WatchCallbacks = {
65
+ onPushed: (file: string) => void;
66
+ onNewEntity: (engineId: number, fileName?: string) => void;
67
+ onError: (file: string, err: Error) => void;
68
+ };
@@ -1 +1 @@
1
- export declare const NPM_PACKAGE = "\n{\n \"dependencies\": {\n \"@react-three/drei\": \"^10.7.7\",\n \"@react-three/fiber\": \"^9.5.0\",\n \"@react-three/rapier\": \"^2.2.0\",\n \"@react-three/uikit\": \"^1.0.61\",\n \"react\": \"^19.1.1\",\n \"react-dom\": \"19.1.1\",\n \"three\": \"^0.179.1\"\n }\n}\n";
1
+ export declare const getNpmPackage: (sceneId: string) => string;
@@ -1,5 +1,8 @@
1
- export const NPM_PACKAGE = `
2
- {
1
+ export const getNpmPackage = (sceneId) => {
2
+ return `{
3
+ "scripts": {
4
+ "watch": "npx phibelle-kit watch ${sceneId}"
5
+ },
3
6
  "dependencies": {
4
7
  "@react-three/drei": "^10.7.7",
5
8
  "@react-three/fiber": "^9.5.0",
@@ -9,5 +12,5 @@ export const NPM_PACKAGE = `
9
12
  "react-dom": "19.1.1",
10
13
  "three": "^0.179.1"
11
14
  }
12
- }
13
- `;
15
+ }`;
16
+ };
@@ -1,15 +1,4 @@
1
- export type SetupResult = {
2
- /** True if we created/updated package.json and user should run install */
1
+ export declare function setupSceneDirectory(sceneDir: string, sceneId: string): {
3
2
  shouldPromptInstall: boolean;
4
3
  };
5
- /**
6
- * Ensures the scene directory has package.json, global.d.ts, and
7
- * node_modules/phibelle/index.d.ts so the IDE can resolve types.
8
- * Call this when the user watches a scene (e.g. from runFirstUpdateSetup).
9
- */
10
- export declare function setupSceneDirectory(sceneDir: string): SetupResult;
11
- /**
12
- * Returns the preferred package manager hint for the scene directory
13
- * (e.g. "pnpm install") based on lockfiles, or a generic message.
14
- */
15
4
  export declare function getInstallHint(sceneDir: string): string;
@@ -1,39 +1,38 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import { NPM_PACKAGE } from "./npm-package.js";
3
+ import { getNpmPackage } from "./npm-package.js";
4
4
  import { GLOBAL_MODULE } from "./global-module.js";
5
+ import { AGENTS_MD } from "./agents-md.js";
6
+ import { TSCONFIG_FILE } from "./tsconfig-file.js";
5
7
  const PACKAGE_JSON = "package.json";
6
8
  const GLOBAL_D_TS = "global.d.ts";
7
- /** One dependency we expect after install to detect if install was run */
9
+ const AGENTS_MD_FILE = "AGENTS.md";
8
10
  const PROBE_DEP = "react";
9
- /**
10
- * Ensures the scene directory has package.json, global.d.ts, and
11
- * node_modules/phibelle/index.d.ts so the IDE can resolve types.
12
- * Call this when the user watches a scene (e.g. from runFirstUpdateSetup).
13
- */
14
- export function setupSceneDirectory(sceneDir) {
11
+ export function setupSceneDirectory(sceneDir, sceneId) {
15
12
  const packagePath = path.join(sceneDir, PACKAGE_JSON);
16
13
  const globalDtsPath = path.join(sceneDir, GLOBAL_D_TS);
17
14
  let shouldPromptInstall = false;
18
- // 1. Create package.json
19
- const pkgJson = `${NPM_PACKAGE}\n`;
15
+ // 1. Create package.json and tscconfig.json
16
+ const pkgJson = `${getNpmPackage(sceneId)}\n`;
20
17
  if (!fs.existsSync(packagePath) || fs.readFileSync(packagePath, "utf8") !== pkgJson) {
21
18
  fs.writeFileSync(packagePath, pkgJson);
22
19
  shouldPromptInstall = true;
23
20
  }
21
+ const tscConfigPath = path.join(sceneDir, "tsconfig.json");
22
+ if (!fs.existsSync(tscConfigPath) || fs.readFileSync(tscConfigPath, "utf8") !== TSCONFIG_FILE) {
23
+ fs.writeFileSync(tscConfigPath, TSCONFIG_FILE);
24
+ }
24
25
  // 2. Write global.d.ts
25
26
  fs.writeFileSync(globalDtsPath, GLOBAL_MODULE.trimStart());
26
- // 3. If node_modules doesn't have real deps yet, prompt install
27
+ // 3. Write AGENTS.md (engine documentation for AI agents and developers)
28
+ fs.writeFileSync(path.join(sceneDir, AGENTS_MD_FILE), AGENTS_MD.trimStart(), "utf8");
29
+ // 4. If node_modules doesn't have real deps yet, prompt install
27
30
  const probePath = path.join(sceneDir, "node_modules", PROBE_DEP, "package.json");
28
31
  if (!fs.existsSync(probePath)) {
29
32
  shouldPromptInstall = true;
30
33
  }
31
34
  return { shouldPromptInstall };
32
35
  }
33
- /**
34
- * Returns the preferred package manager hint for the scene directory
35
- * (e.g. "pnpm install") based on lockfiles, or a generic message.
36
- */
37
36
  export function getInstallHint(sceneDir) {
38
37
  const hasPnpm = fs.existsSync(path.join(sceneDir, "pnpm-lock.yaml"));
39
38
  const hasYarn = fs.existsSync(path.join(sceneDir, "yarn.lock"));
@@ -0,0 +1 @@
1
+ export declare const TSCONFIG_FILE = "{\n \"compilerOptions\": {\n \"jsx\": \"react-jsx\"\n },\n \"include\": [\n \"app/**/*.tsx\",\n \"global.d.ts\"\n ]\n}";
@@ -0,0 +1,9 @@
1
+ export const TSCONFIG_FILE = `{
2
+ "compilerOptions": {
3
+ "jsx": "react-jsx"
4
+ },
5
+ "include": [
6
+ "app/**/*.tsx",
7
+ "global.d.ts"
8
+ ]
9
+ }`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "phibelle-kit",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "description": "CLI tool for interacting with the Phibelle engine",
5
5
  "type": "module",
6
6
  "bin": {
@@ -1,5 +0,0 @@
1
- /**
2
- * Clone: one-time fetch of scene data. Creates scene dir, writes scene.json,
3
- * sets up package.json/global.d.ts, unpacks scripts. No watcher, no opening folder.
4
- */
5
- export declare function cloneSceneCommand(sceneId: string): Promise<void>;
@@ -1,39 +0,0 @@
1
- import chalk from "chalk";
2
- import * as path from "path";
3
- import { queryOnce } from "./lib/convex-client.js";
4
- import { api } from "./convex/_generated/api.js";
5
- import { BASE_URL } from "./lib/public-env.js";
6
- import { sceneNameToFolder } from "./lib/file-system.js";
7
- import { setupSceneFiles, unpackageWithLogging } from "./lib/first-run-setup.js";
8
- /**
9
- * Clone: one-time fetch of scene data. Creates scene dir, writes scene.json,
10
- * sets up package.json/global.d.ts, unpacks scripts. No watcher, no opening folder.
11
- */
12
- export async function cloneSceneCommand(sceneId) {
13
- console.log();
14
- console.log(chalk.bold.cyan(" 📥 Cloning scene..."));
15
- console.log(chalk.gray(` Scene ID: ${sceneId}`));
16
- console.log();
17
- const scene = await queryOnce(api.scenes.getById, {
18
- id: sceneId,
19
- });
20
- if (scene === null) {
21
- console.log(chalk.red(" ✖ Scene not found"));
22
- console.log();
23
- process.exit(1);
24
- }
25
- const sceneFolderName = sceneNameToFolder(scene.name);
26
- const sceneDir = path.join(process.cwd(), sceneFolderName);
27
- setupSceneFiles(scene, sceneDir, true);
28
- await unpackageWithLogging(scene._id, sceneDir, "Edit the scene in the app once to create a scene file, then run clone again.");
29
- console.log(chalk.green(" ✓ Cloned scene"));
30
- console.log(chalk.white(` Name: ${scene.name}`));
31
- console.log(chalk.white(` Path: ${sceneDir}`));
32
- console.log(chalk.white(" Scene link: ") + chalk.cyan(`${BASE_URL}/editor?sceneId=${scene._id}`));
33
- console.log();
34
- console.log(chalk.gray(" Next steps:"));
35
- console.log(chalk.gray(" 1. ") + chalk.blueBright("cd ") + chalk.cyan(sceneFolderName) + chalk.gray(" to open the scene directory."));
36
- console.log(chalk.gray(" 2. ") + chalk.blueBright("npm install") + chalk.gray(" to install the dependencies."));
37
- console.log(chalk.gray(" 3. ") + chalk.blueBright("phibelle watch ") + chalk.cyan(sceneId) + chalk.gray(" to watch for changes and push edits."));
38
- console.log();
39
- }
@@ -1 +0,0 @@
1
- export declare function helloCommand(): Promise<void>;
@@ -1,10 +0,0 @@
1
- import chalk from "chalk";
2
- import { getStoredUsername } from "../lib/conf.js";
3
- export async function helloCommand() {
4
- const username = getStoredUsername();
5
- const greeting = username ? `Hello ${username}! 👋` : "Hello World! 👋";
6
- console.log();
7
- console.log(chalk.bold.magenta(` ${greeting}`));
8
- console.log(chalk.gray(" Phibelle CLI is working correctly."));
9
- console.log();
10
- }
@@ -1 +0,0 @@
1
- export declare function loginCommand(): Promise<void>;
@@ -1,75 +0,0 @@
1
- import chalk from "chalk";
2
- import * as readline from "readline/promises";
3
- import { BASE_URL } from "../lib/constants.js";
4
- import { CONFIG, getStoredToken } from "../lib/conf.js";
5
- async function fetchUsernameFromAPI(token) {
6
- try {
7
- const response = await fetch(`${BASE_URL}/api/cli/user-info`, {
8
- method: "GET",
9
- headers: {
10
- Authorization: `Bearer ${token}`,
11
- },
12
- });
13
- if (!response.ok) {
14
- return null;
15
- }
16
- const data = await response.json();
17
- return data.username || null;
18
- }
19
- catch (error) {
20
- return null;
21
- }
22
- }
23
- export async function loginCommand() {
24
- const rl = readline.createInterface({
25
- input: process.stdin,
26
- output: process.stdout,
27
- });
28
- if (getStoredToken() !== undefined) {
29
- console.log();
30
- console.log(chalk.yellow(" You are already logged in. Please logout first."));
31
- console.log();
32
- rl.close();
33
- return;
34
- }
35
- console.log();
36
- console.log(chalk.bold.cyan(" 🔐 Phibelle CLI Login"));
37
- console.log();
38
- console.log(chalk.gray(" To authenticate, follow these steps:"));
39
- console.log();
40
- console.log(chalk.white(" 1. Open this URL in your browser:"));
41
- console.log();
42
- console.log(chalk.cyan(` ${BASE_URL}/cli-token`));
43
- console.log();
44
- console.log(chalk.white(" 2. Sign in if prompted"));
45
- console.log(chalk.white(" 3. Copy the token displayed on the page"));
46
- console.log(chalk.white(" 4. Paste it below"));
47
- console.log();
48
- const token = await rl.question(chalk.yellow(" Enter your token: "));
49
- if (!token || token.trim() === "") {
50
- console.log();
51
- console.log(chalk.red(" ✖ No token provided. Login cancelled."));
52
- console.log();
53
- rl.close();
54
- throw new Error("No token provided");
55
- }
56
- const trimmedToken = token.trim();
57
- console.log(chalk.gray(" Fetching user information..."));
58
- const username = await fetchUsernameFromAPI(trimmedToken);
59
- if (username) {
60
- CONFIG.set("username", username);
61
- }
62
- if (!username) {
63
- console.log();
64
- console.log(chalk.red(" ✖ Failed to fetch user information. Login cancelled."));
65
- console.log();
66
- rl.close();
67
- throw new Error("Failed to fetch user information");
68
- }
69
- CONFIG.set("authToken", trimmedToken);
70
- console.log();
71
- console.log(chalk.green(" ✔ Successfully logged in!"));
72
- console.log(chalk.gray(" Your token has been saved."));
73
- console.log();
74
- rl.close();
75
- }
@@ -1 +0,0 @@
1
- export declare function logoutCommand(): Promise<void>;
@@ -1,16 +0,0 @@
1
- import chalk from "chalk";
2
- import { clearToken, getStoredToken } from "../lib/conf.js";
3
- export async function logoutCommand() {
4
- const token = getStoredToken();
5
- if (!token) {
6
- console.log();
7
- console.log(chalk.yellow(" ℹ You are not currently logged in."));
8
- console.log();
9
- return;
10
- }
11
- clearToken();
12
- console.log();
13
- console.log(chalk.green(" ✔ Successfully logged out!"));
14
- console.log(chalk.gray(" Your token has been cleared."));
15
- console.log();
16
- }
@@ -1,4 +0,0 @@
1
- export declare const isDev: boolean;
2
- export declare const BASE_URL: string;
3
- export declare const CONVEX_URL: string;
4
- export declare const PHIBELLE_MAIN_FOLDER: string;
package/dist/constants.js DELETED
@@ -1,6 +0,0 @@
1
- export const isDev = process.env.NODE_ENV === "development";
2
- import path from "node:path";
3
- import os from "node:os";
4
- export const BASE_URL = isDev ? "http://localhost:3131" : "https://phibelle.studio";
5
- export const CONVEX_URL = isDev ? "https://mellow-toad-676.convex.cloud" : "https://keen-dove-500.convex.cloud";
6
- export const PHIBELLE_MAIN_FOLDER = path.join(os.homedir(), ".phibelle/");
@@ -1,4 +0,0 @@
1
- export declare const isDev: boolean;
2
- export declare const BASE_URL: string;
3
- export declare const CONVEX_URL: string;
4
- export declare const PHIBELLE_MAIN_FOLDER: string;
@@ -1,6 +0,0 @@
1
- import path from "node:path";
2
- import os from "node:os";
3
- export const isDev = process.env.NODE_ENV === "development";
4
- export const BASE_URL = isDev ? "http://localhost:3131" : "https://phibelle.studio";
5
- export const CONVEX_URL = isDev ? "https://mellow-toad-676.convex.cloud" : "https://keen-dove-500.convex.cloud";
6
- export const PHIBELLE_MAIN_FOLDER = path.join(os.homedir(), ".phibelle/");
@@ -1,3 +0,0 @@
1
- export declare function openFolder(folderPath: string): void;
2
- export declare function folderExists(folderPath: string): boolean;
3
- export declare function createFolder(folderPath: string): void;
@@ -1,33 +0,0 @@
1
- import { spawn } from "node:child_process";
2
- import os from "node:os";
3
- import * as fs from "fs";
4
- export function openFolder(folderPath) {
5
- const platform = os.platform();
6
- let command;
7
- let args;
8
- if (platform === "win32") {
9
- command = "explorer.exe";
10
- args = [folderPath];
11
- }
12
- else if (platform === "darwin") {
13
- command = "open";
14
- args = [folderPath];
15
- }
16
- else {
17
- command = "xdg-open";
18
- args = [folderPath];
19
- }
20
- const child = spawn(command, args, {
21
- detached: true,
22
- stdio: "ignore",
23
- });
24
- child.unref();
25
- }
26
- export function folderExists(folderPath) {
27
- return fs.existsSync(folderPath);
28
- }
29
- export function createFolder(folderPath) {
30
- if (!folderExists(folderPath)) {
31
- fs.mkdirSync(folderPath, { recursive: true });
32
- }
33
- }
@@ -1 +0,0 @@
1
- export declare const GLOBAL_MODULE = "\nimport type React from \"react\";\nimport type * as THREE_IMPORT from \"three\";\nimport type * as DREI_IMPORT from \"@react-three/drei\";\nimport type * as R3F_IMPORT from \"@react-three/fiber\";\nimport type * as UIKIT_IMPORT from \"@react-three/uikit\";\nimport type * as RAPIER_IMPORT from \"@react-three/rapier\";\n\ndeclare global {\n const THREE: typeof THREE_IMPORT;\n const DREI: typeof DREI_IMPORT;\n const R3F: typeof R3F_IMPORT;\n const UIKIT: typeof UIKIT_IMPORT;\n const RAPIER: typeof RAPIER_IMPORT;\n\n namespace PHI {\n type Transform = {\n position: THREE_IMPORT.Vector3;\n rotation: THREE_IMPORT.Euler;\n scale: THREE_IMPORT.Vector3;\n };\n\n type PropertyType = string;\n\n type Property = {\n name: string;\n type: PropertyType;\n value: unknown;\n };\n\n type EntityData = {\n name: string;\n engineId: number;\n threeId?: number;\n parentId?: number;\n childrenIds: number[];\n transform: Transform;\n properties: Property[];\n };\n\n type GamepadInfo = {\n connected: boolean;\n buttonA: boolean;\n buttonB: boolean;\n buttonX: boolean;\n buttonY: boolean;\n joystick: [number, number];\n joystickRight: [number, number];\n RB: boolean;\n LB: boolean;\n RT: boolean;\n LT: boolean;\n start: boolean;\n select: boolean;\n up: boolean;\n down: boolean;\n left: boolean;\n right: boolean;\n };\n\n type EngineMode = {\n editMode: boolean;\n playMode: boolean;\n };\n\n type SceneScriptState = {\n sceneProperties: Record<string, unknown>;\n };\n\n type EntityRenderRegistry = Record<\n string,\n React.FC<{ entityData: EntityData }>\n >;\n }\n\n const PHI: {\n globalStore: Record<string, any>;\n\n PROPERTY_TYPES: ReadonlyArray<{\n type: PHI.PropertyType;\n defaultValue: unknown;\n }>;\n\n phibelleResetSelectedEntityIds(): void;\n\n useEntityThreeObject(\n entityData: PHI.EntityData,\n threeScene: THREE_IMPORT.Scene\n ): THREE_IMPORT.Object3D | null;\n\n useGamepad(): PHI.GamepadInfo;\n\n usePlayModeFrame(\n callback: (state: R3F_IMPORT.RootState, delta: number) => void\n ): void;\n\n useEngineMode(): PHI.EngineMode;\n\n useSceneScriptStore(): PHI.SceneScriptState;\n useSceneScriptStore<T>(\n selector: (state: PHI.SceneScriptState) => T\n ): T;\n\n editEntities(\n edits: Array<{ id: number; newData: Partial<PHI.EntityData> }>\n ): void;\n\n useEntity(entityId: number): { entityData: PHI.EntityData };\n\n PhibelleRoot: React.FC<{\n entityRenderRegistry: PHI.EntityRenderRegistry;\n }>;\n };\n}\n\nexport {};\n";
@@ -1,117 +0,0 @@
1
- export const GLOBAL_MODULE = `
2
- import type React from "react";
3
- import type * as THREE_IMPORT from "three";
4
- import type * as DREI_IMPORT from "@react-three/drei";
5
- import type * as R3F_IMPORT from "@react-three/fiber";
6
- import type * as UIKIT_IMPORT from "@react-three/uikit";
7
- import type * as RAPIER_IMPORT from "@react-three/rapier";
8
-
9
- declare global {
10
- const THREE: typeof THREE_IMPORT;
11
- const DREI: typeof DREI_IMPORT;
12
- const R3F: typeof R3F_IMPORT;
13
- const UIKIT: typeof UIKIT_IMPORT;
14
- const RAPIER: typeof RAPIER_IMPORT;
15
-
16
- namespace PHI {
17
- type Transform = {
18
- position: THREE_IMPORT.Vector3;
19
- rotation: THREE_IMPORT.Euler;
20
- scale: THREE_IMPORT.Vector3;
21
- };
22
-
23
- type PropertyType = string;
24
-
25
- type Property = {
26
- name: string;
27
- type: PropertyType;
28
- value: unknown;
29
- };
30
-
31
- type EntityData = {
32
- name: string;
33
- engineId: number;
34
- threeId?: number;
35
- parentId?: number;
36
- childrenIds: number[];
37
- transform: Transform;
38
- properties: Property[];
39
- };
40
-
41
- type GamepadInfo = {
42
- connected: boolean;
43
- buttonA: boolean;
44
- buttonB: boolean;
45
- buttonX: boolean;
46
- buttonY: boolean;
47
- joystick: [number, number];
48
- joystickRight: [number, number];
49
- RB: boolean;
50
- LB: boolean;
51
- RT: boolean;
52
- LT: boolean;
53
- start: boolean;
54
- select: boolean;
55
- up: boolean;
56
- down: boolean;
57
- left: boolean;
58
- right: boolean;
59
- };
60
-
61
- type EngineMode = {
62
- editMode: boolean;
63
- playMode: boolean;
64
- };
65
-
66
- type SceneScriptState = {
67
- sceneProperties: Record<string, unknown>;
68
- };
69
-
70
- type EntityRenderRegistry = Record<
71
- string,
72
- React.FC<{ entityData: EntityData }>
73
- >;
74
- }
75
-
76
- const PHI: {
77
- globalStore: Record<string, any>;
78
-
79
- PROPERTY_TYPES: ReadonlyArray<{
80
- type: PHI.PropertyType;
81
- defaultValue: unknown;
82
- }>;
83
-
84
- phibelleResetSelectedEntityIds(): void;
85
-
86
- useEntityThreeObject(
87
- entityData: PHI.EntityData,
88
- threeScene: THREE_IMPORT.Scene
89
- ): THREE_IMPORT.Object3D | null;
90
-
91
- useGamepad(): PHI.GamepadInfo;
92
-
93
- usePlayModeFrame(
94
- callback: (state: R3F_IMPORT.RootState, delta: number) => void
95
- ): void;
96
-
97
- useEngineMode(): PHI.EngineMode;
98
-
99
- useSceneScriptStore(): PHI.SceneScriptState;
100
- useSceneScriptStore<T>(
101
- selector: (state: PHI.SceneScriptState) => T
102
- ): T;
103
-
104
- editEntities(
105
- edits: Array<{ id: number; newData: Partial<PHI.EntityData> }>
106
- ): void;
107
-
108
- useEntity(entityId: number): { entityData: PHI.EntityData };
109
-
110
- PhibelleRoot: React.FC<{
111
- entityRenderRegistry: PHI.EntityRenderRegistry;
112
- }>;
113
- };
114
- }
115
-
116
- export {};
117
- `;
@@ -1 +0,0 @@
1
- export declare const getNpmPackage: (sceneId: string) => string;
@@ -1,16 +0,0 @@
1
- export const getNpmPackage = (sceneId) => {
2
- return `{
3
- "scripts": {
4
- "watch": "npx phibelle-kit watch ${sceneId}"
5
- },
6
- "dependencies": {
7
- "@react-three/drei": "^10.7.7",
8
- "@react-three/fiber": "^9.5.0",
9
- "@react-three/rapier": "^2.2.0",
10
- "@react-three/uikit": "^1.0.61",
11
- "react": "^19.1.1",
12
- "react-dom": "19.1.1",
13
- "three": "^0.179.1"
14
- }
15
- }`;
16
- };
@@ -1 +0,0 @@
1
- export declare const PHIBELLE_MODULE = "\n// node_modules/phibelle/index.d.ts\nimport { RootState } from \"@react-three/fiber\";\nimport type * as THREE from \"three\";\n\ndeclare module \"phibelle\" {\n // ----------------------------\n // Global window store\n // ----------------------------\n export const globalStore: {\n [key: string]: any;\n };\n\n // ----------------------------\n // Types used by scripts\n // ----------------------------\n export type Transform = {\n position: THREE.Vector3;\n rotation: THREE.Euler;\n scale: THREE.Vector3;\n };\n\n export type PropertyType = string;\n\n export type Property = {\n name: string;\n type: PropertyType;\n value: string | number | boolean | [number, number, number];\n };\n\n export type EntityData = {\n name: string;\n engineId: number;\n threeId?: number;\n parentId?: number;\n childrenIds: number[];\n transform: Transform;\n properties: Property[];\n };\n\n export const PROPERTY_TYPES: ReadonlyArray<{\n type: PropertyType;\n defaultValue: unknown;\n }>;\n\n // ----------------------------\n // Engine / editor functions\n // ----------------------------\n export function phibelleResetSelectedEntityIds(): void;\n\n // ----------------------------\n // Hooks exposed to scripts\n // ----------------------------\n export function useEntityThreeObject(\n entityData: EntityData,\n threeScene: THREE.Scene\n ): THREE.Object3D | null;\n\n export type GamepadInfo = {\n connected: boolean;\n buttonA: boolean;\n buttonB: boolean;\n buttonX: boolean;\n buttonY: boolean;\n joystick: [number, number];\n joystickRight: [number, number];\n RB: boolean;\n LB: boolean;\n RT: boolean;\n LT: boolean;\n start: boolean;\n select: boolean;\n up: boolean;\n down: boolean;\n left: boolean;\n right: boolean;\n };\n\n export function useGamepad(): GamepadInfo;\n\n export function usePlayModeFrame(\n callback: (state: RootState, delta: number) => void\n ): void;\n\n export type EngineMode = {\n editMode: boolean;\n playMode: boolean;\n };\n\n export function useEngineMode(): EngineMode;\n\n // Zustand-like store stub you generate\n export type SceneScriptState = {\n sceneProperties: Record<string, unknown>;\n };\n\n export function useSceneScriptStore(): SceneScriptState;\n export function useSceneScriptStore<T>(\n selector: (state: SceneScriptState) => T\n ): T;\n\n export function editEntities(\n edits: Array<{ id: number; newData: Partial<EntityData> }>\n ): void;\n\n export function useEntity(entityId: number): { entityData: EntityData };\n\n // ----------------------------\n // Components\n // ----------------------------\n export type EntityRenderRegistry = Record<\n string,\n React.FC<{ entityData: EntityData }>\n >;\n\n export const PhibelleRoot: React.FC<{\n entityRenderRegistry: EntityRenderRegistry;\n }>;\n}\n\nexport {};\n";
@@ -1,122 +0,0 @@
1
- export const PHIBELLE_MODULE = `
2
- // node_modules/phibelle/index.d.ts
3
- import { RootState } from "@react-three/fiber";
4
- import type * as THREE from "three";
5
-
6
- declare module "phibelle" {
7
- // ----------------------------
8
- // Global window store
9
- // ----------------------------
10
- export const globalStore: {
11
- [key: string]: any;
12
- };
13
-
14
- // ----------------------------
15
- // Types used by scripts
16
- // ----------------------------
17
- export type Transform = {
18
- position: THREE.Vector3;
19
- rotation: THREE.Euler;
20
- scale: THREE.Vector3;
21
- };
22
-
23
- export type PropertyType = string;
24
-
25
- export type Property = {
26
- name: string;
27
- type: PropertyType;
28
- value: string | number | boolean | [number, number, number];
29
- };
30
-
31
- export type EntityData = {
32
- name: string;
33
- engineId: number;
34
- threeId?: number;
35
- parentId?: number;
36
- childrenIds: number[];
37
- transform: Transform;
38
- properties: Property[];
39
- };
40
-
41
- export const PROPERTY_TYPES: ReadonlyArray<{
42
- type: PropertyType;
43
- defaultValue: unknown;
44
- }>;
45
-
46
- // ----------------------------
47
- // Engine / editor functions
48
- // ----------------------------
49
- export function phibelleResetSelectedEntityIds(): void;
50
-
51
- // ----------------------------
52
- // Hooks exposed to scripts
53
- // ----------------------------
54
- export function useEntityThreeObject(
55
- entityData: EntityData,
56
- threeScene: THREE.Scene
57
- ): THREE.Object3D | null;
58
-
59
- export type GamepadInfo = {
60
- connected: boolean;
61
- buttonA: boolean;
62
- buttonB: boolean;
63
- buttonX: boolean;
64
- buttonY: boolean;
65
- joystick: [number, number];
66
- joystickRight: [number, number];
67
- RB: boolean;
68
- LB: boolean;
69
- RT: boolean;
70
- LT: boolean;
71
- start: boolean;
72
- select: boolean;
73
- up: boolean;
74
- down: boolean;
75
- left: boolean;
76
- right: boolean;
77
- };
78
-
79
- export function useGamepad(): GamepadInfo;
80
-
81
- export function usePlayModeFrame(
82
- callback: (state: RootState, delta: number) => void
83
- ): void;
84
-
85
- export type EngineMode = {
86
- editMode: boolean;
87
- playMode: boolean;
88
- };
89
-
90
- export function useEngineMode(): EngineMode;
91
-
92
- // Zustand-like store stub you generate
93
- export type SceneScriptState = {
94
- sceneProperties: Record<string, unknown>;
95
- };
96
-
97
- export function useSceneScriptStore(): SceneScriptState;
98
- export function useSceneScriptStore<T>(
99
- selector: (state: SceneScriptState) => T
100
- ): T;
101
-
102
- export function editEntities(
103
- edits: Array<{ id: number; newData: Partial<EntityData> }>
104
- ): void;
105
-
106
- export function useEntity(entityId: number): { entityData: EntityData };
107
-
108
- // ----------------------------
109
- // Components
110
- // ----------------------------
111
- export type EntityRenderRegistry = Record<
112
- string,
113
- React.FC<{ entityData: EntityData }>
114
- >;
115
-
116
- export const PhibelleRoot: React.FC<{
117
- entityRenderRegistry: EntityRenderRegistry;
118
- }>;
119
- }
120
-
121
- export {};
122
- `;
@@ -1,4 +0,0 @@
1
- export declare function setupSceneDirectory(sceneDir: string, sceneId: string): {
2
- shouldPromptInstall: boolean;
3
- };
4
- export declare function getInstallHint(sceneDir: string): string;
@@ -1,45 +0,0 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import { getNpmPackage } from "./npm-package.js";
4
- import { GLOBAL_MODULE } from "./global-module.js";
5
- import { AGENTS_MD } from "./agents-md.js";
6
- const PACKAGE_JSON = "package.json";
7
- const GLOBAL_D_TS = "global.d.ts";
8
- const AGENTS_MD_FILE = "AGENTS.md";
9
- const PROBE_DEP = "react";
10
- export function setupSceneDirectory(sceneDir, sceneId) {
11
- const packagePath = path.join(sceneDir, PACKAGE_JSON);
12
- const globalDtsPath = path.join(sceneDir, GLOBAL_D_TS);
13
- let shouldPromptInstall = false;
14
- // 1. Create package.json
15
- const pkgJson = `${getNpmPackage(sceneId)}\n`;
16
- if (!fs.existsSync(packagePath) || fs.readFileSync(packagePath, "utf8") !== pkgJson) {
17
- fs.writeFileSync(packagePath, pkgJson);
18
- shouldPromptInstall = true;
19
- }
20
- // 2. Write global.d.ts
21
- fs.writeFileSync(globalDtsPath, GLOBAL_MODULE.trimStart());
22
- // 3. Write AGENTS.md (engine documentation for AI agents and developers)
23
- fs.writeFileSync(path.join(sceneDir, AGENTS_MD_FILE), AGENTS_MD.trimStart(), "utf8");
24
- // 4. If node_modules doesn't have real deps yet, prompt install
25
- const probePath = path.join(sceneDir, "node_modules", PROBE_DEP, "package.json");
26
- if (!fs.existsSync(probePath)) {
27
- shouldPromptInstall = true;
28
- }
29
- return { shouldPromptInstall };
30
- }
31
- export function getInstallHint(sceneDir) {
32
- const hasPnpm = fs.existsSync(path.join(sceneDir, "pnpm-lock.yaml"));
33
- const hasYarn = fs.existsSync(path.join(sceneDir, "yarn.lock"));
34
- const hasBun = fs.existsSync(path.join(sceneDir, "bun.lockb"));
35
- const hasNpm = fs.existsSync(path.join(sceneDir, "package-lock.json"));
36
- if (hasPnpm)
37
- return "pnpm install";
38
- if (hasYarn)
39
- return "yarn install";
40
- if (hasBun)
41
- return "bun install";
42
- if (hasNpm)
43
- return "npm install";
44
- return "npm install (or pnpm / yarn / bun install)";
45
- }
@@ -1 +0,0 @@
1
- export declare function watchSceneCommand(sceneId: string): Promise<void>;
@@ -1,103 +0,0 @@
1
- import chalk from "chalk";
2
- import * as path from "path";
3
- import * as fs from "fs";
4
- import { subscribeToQuery } from "./lib/convex-client.js";
5
- import { api } from "./convex/_generated/api.js";
6
- import { getSessionId } from "./lib/conf.js";
7
- import { findSceneDirBySceneId, sceneNameToFolder } from "./lib/file-system.js";
8
- import { unpackageScene } from "./lib/scene-packaging.js";
9
- import { createScriptWatcher } from "./lib/script-watcher.js";
10
- import { runFirstUpdateSetup } from "./lib/first-run-setup.js";
11
- import { toErrorMessage } from "./lib/utils.js";
12
- function createWatchCallbacks() {
13
- return {
14
- onPushed: (file) => console.log(chalk.green(" ✓ Pushed " + file + " to Convex")),
15
- onNewEntity: (engineId) => console.log(chalk.green(" ✓ Created new entity " + engineId + " (entity-" + engineId + ".tsx)")),
16
- onError: (file, err) => console.log(chalk.red(" ✖ Failed to push " + file + ": " + err.message)),
17
- };
18
- }
19
- function startScriptWatcher(scene, sceneDir) {
20
- const c = createWatchCallbacks();
21
- return createScriptWatcher(scene._id, sceneDir, c.onPushed, c.onNewEntity, c.onError);
22
- }
23
- export async function watchSceneCommand(sceneId) {
24
- console.log();
25
- console.log(chalk.bold.cyan(" 👀 Watching scene updates..."));
26
- console.log(chalk.gray(` Scene ID: ${sceneId}`));
27
- console.log(chalk.gray(" Press Ctrl+C to stop"));
28
- console.log();
29
- let previousUpdatedAt = null;
30
- let isFirstUpdate = true;
31
- let scriptWatcher = null;
32
- try {
33
- const unsubscribe = subscribeToQuery(api.scenes.getById, { id: sceneId }, async (scene) => {
34
- if (scene === null) {
35
- if (isFirstUpdate) {
36
- console.log(chalk.red(" ✖ Scene not found"));
37
- console.log();
38
- process.exit(1);
39
- }
40
- return;
41
- }
42
- const currentUpdatedAt = scene.updatedAt;
43
- const cwd = process.cwd();
44
- const sceneDir = findSceneDirBySceneId(cwd, sceneId) ?? path.join(cwd, sceneNameToFolder(scene.name));
45
- const manifestPath = path.join(sceneDir, "manifest.json");
46
- const alreadyCloned = fs.existsSync(manifestPath);
47
- if (isFirstUpdate) {
48
- if (alreadyCloned) {
49
- fs.writeFileSync(path.join(sceneDir, "scene.json"), JSON.stringify(scene));
50
- scriptWatcher = startScriptWatcher(scene, sceneDir);
51
- console.log(chalk.green(" ✓ Watching scene (previously cloned)"));
52
- console.log(chalk.white(` Name: ${scene.name}`));
53
- console.log(chalk.white(` Dir: ${sceneDir}`));
54
- console.log(chalk.gray(" Press Ctrl+C to stop"));
55
- console.log();
56
- }
57
- else {
58
- scriptWatcher = await runFirstUpdateSetup(scene, sceneDir, createWatchCallbacks());
59
- }
60
- previousUpdatedAt = currentUpdatedAt;
61
- isFirstUpdate = false;
62
- }
63
- else if (previousUpdatedAt !== null && currentUpdatedAt !== previousUpdatedAt) {
64
- fs.writeFileSync(path.join(sceneDir, "scene.json"), JSON.stringify(scene));
65
- console.log(chalk.green(" ✓ Scene updated"));
66
- console.log(chalk.white(` Timestamp: ${new Date(currentUpdatedAt).toLocaleString()}`));
67
- const ourSessionId = getSessionId();
68
- const weSaved = scene.lastModifiedSessionId != null && scene.lastModifiedSessionId === ourSessionId;
69
- if (!weSaved) {
70
- scriptWatcher?.close();
71
- scriptWatcher = null;
72
- try {
73
- await unpackageScene(scene._id, sceneDir);
74
- console.log(chalk.green(" ✓ Re-unpackaged scene scripts (scene.tsx, entity-*.tsx, manifest.json)"));
75
- scriptWatcher = startScriptWatcher(scene, sceneDir);
76
- }
77
- catch (e) {
78
- console.log(chalk.yellow(" ⚠ Could not re-unpackage scene scripts: " + toErrorMessage(e)));
79
- }
80
- }
81
- console.log();
82
- previousUpdatedAt = currentUpdatedAt;
83
- }
84
- });
85
- process.on("SIGINT", () => {
86
- console.log();
87
- console.log(chalk.gray(" Unsubscribing..."));
88
- scriptWatcher?.close();
89
- scriptWatcher = null;
90
- unsubscribe();
91
- console.log(chalk.gray(" Goodbye! 👋"));
92
- console.log();
93
- process.exit(0);
94
- });
95
- await new Promise(() => { });
96
- }
97
- catch (error) {
98
- console.log();
99
- console.log(chalk.red(` ✖ Error: ${toErrorMessage(error)}`));
100
- console.log();
101
- throw error;
102
- }
103
- }
File without changes
File without changes
File without changes
File without changes