@objectstack/cli 1.1.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
 
2
- > @objectstack/cli@1.1.0 build /home/runner/work/spec/spec/packages/cli
2
+ > @objectstack/cli@2.0.0 build /home/runner/work/spec/spec/packages/cli
3
3
  > tsup
4
4
 
5
5
  CLI Building entry: src/bin.ts
@@ -15,10 +15,10 @@
15
15
  ESM Build start
16
16
  CLI Cleaning output folder
17
17
  ESM Build start
18
- ESM dist/index.js 61.35 KB
19
- ESM ⚡️ Build success in 69ms
20
- ESM dist/bin.js 63.96 KB
21
- ESM ⚡️ Build success in 68ms
18
+ ESM dist/index.js 57.98 KB
19
+ ESM ⚡️ Build success in 106ms
20
+ ESM dist/bin.js 60.60 KB
21
+ ESM ⚡️ Build success in 119ms
22
22
  DTS Build start
23
- DTS ⚡️ Build success in 4303ms
23
+ DTS ⚡️ Build success in 9596ms
24
24
  DTS dist/index.d.ts 2.93 KB
package/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # @objectstack/cli
2
2
 
3
+ ## 2.0.0
4
+
5
+ ### Patch Changes
6
+
7
+ - Updated dependencies [38e5dd5]
8
+ - Updated dependencies [38e5dd5]
9
+ - @objectstack/spec@2.0.0
10
+ - @objectstack/core@2.0.0
11
+ - @objectstack/objectql@2.0.0
12
+ - @objectstack/driver-memory@2.0.0
13
+ - @objectstack/plugin-hono-server@2.0.0
14
+ - @objectstack/rest@2.0.0
15
+ - @objectstack/runtime@2.0.0
16
+
3
17
  ## 1.0.12
4
18
 
5
19
  ### Patch Changes
package/dist/bin.js CHANGED
@@ -132,7 +132,7 @@ function printServerReady(opts) {
132
132
  console.log("");
133
133
  console.log(chalk.cyan(" \u279C") + chalk.bold(" API: ") + chalk.cyan(base + "/"));
134
134
  if (opts.uiEnabled && opts.studioPath) {
135
- console.log(chalk.cyan(" \u279C") + chalk.bold(" Console: ") + chalk.cyan(base + opts.studioPath + "/"));
135
+ console.log(chalk.cyan(" \u279C") + chalk.bold(" Studio: ") + chalk.cyan(base + opts.studioPath + "/"));
136
136
  }
137
137
  console.log("");
138
138
  console.log(chalk.dim(` Config: ${opts.configFile}`));
@@ -314,7 +314,7 @@ import chalk4 from "chalk";
314
314
  import { execSync, spawn } from "child_process";
315
315
  import fs3 from "fs";
316
316
  import path3 from "path";
317
- var devCommand = new Command2("dev").description("Start development mode with hot-reload").argument("[package]", "Package name or filter pattern", "all").option("-w, --watch", "Enable watch mode (default)", true).option("--ui", "Enable Console UI at /_studio/").option("-v, --verbose", "Verbose output").action(async (packageName, options) => {
317
+ var devCommand = new Command2("dev").description("Start development mode with hot-reload").argument("[package]", "Package name or filter pattern", "all").option("-w, --watch", "Enable watch mode (default)", true).option("--ui", "Enable Studio UI at /_studio/").option("-v, --verbose", "Verbose output").action(async (packageName, options) => {
318
318
  printHeader("Development Mode");
319
319
  const configPath = path3.resolve(process.cwd(), "objectstack.config.ts");
320
320
  if (packageName === "all" && fs3.existsSync(configPath)) {
@@ -740,30 +740,27 @@ var createCommand = new Command4("create").description("Create a new package, pl
740
740
  import { Command as Command5 } from "commander";
741
741
  import path7 from "path";
742
742
  import fs7 from "fs";
743
- import net2 from "net";
743
+ import net from "net";
744
744
  import chalk7 from "chalk";
745
745
  import { bundleRequire as bundleRequire2 } from "bundle-require";
746
746
 
747
- // src/utils/console.ts
747
+ // src/utils/studio.ts
748
748
  import path6 from "path";
749
749
  import fs6 from "fs";
750
- import net from "net";
751
- import { spawn as spawn2 } from "child_process";
752
750
  var STUDIO_PATH = "/_studio";
753
- var VITE_PORT_START = 24678;
754
- function resolveConsolePath() {
751
+ function resolveStudioPath() {
755
752
  const cwd = process.cwd();
756
753
  const candidates = [
757
- path6.resolve(cwd, "apps/console"),
758
- path6.resolve(cwd, "../../apps/console"),
759
- path6.resolve(cwd, "../apps/console")
754
+ path6.resolve(cwd, "apps/studio"),
755
+ path6.resolve(cwd, "../../apps/studio"),
756
+ path6.resolve(cwd, "../apps/studio")
760
757
  ];
761
758
  for (const candidate of candidates) {
762
759
  const pkgPath = path6.join(candidate, "package.json");
763
760
  if (fs6.existsSync(pkgPath)) {
764
761
  try {
765
762
  const pkg2 = JSON.parse(fs6.readFileSync(pkgPath, "utf-8"));
766
- if (pkg2.name === "@objectstack/console") return candidate;
763
+ if (pkg2.name === "@objectstack/studio") return candidate;
767
764
  } catch {
768
765
  }
769
766
  }
@@ -771,142 +768,41 @@ function resolveConsolePath() {
771
768
  try {
772
769
  const { createRequire: createRequire2 } = __require("module");
773
770
  const req = createRequire2(import.meta.url);
774
- const resolved = req.resolve("@objectstack/console/package.json");
771
+ const resolved = req.resolve("@objectstack/studio/package.json");
775
772
  return path6.dirname(resolved);
776
773
  } catch {
777
774
  return null;
778
775
  }
779
776
  }
780
- function hasConsoleDist(consolePath) {
781
- return fs6.existsSync(path6.join(consolePath, "dist", "index.html"));
782
- }
783
- function findAvailablePort(start = VITE_PORT_START) {
784
- return new Promise((resolve, reject) => {
785
- const server = net.createServer();
786
- server.once("error", () => {
787
- findAvailablePort(start + 1).then(resolve, reject);
788
- });
789
- server.once("listening", () => {
790
- server.close(() => resolve(start));
791
- });
792
- server.listen(start);
793
- });
794
- }
795
- async function spawnViteDevServer(consolePath, options = {}) {
796
- const vitePort = await findAvailablePort(VITE_PORT_START);
797
- const viteBinCandidates = [
798
- path6.join(consolePath, "node_modules", ".bin", "vite"),
799
- path6.join(consolePath, "..", "..", "node_modules", ".bin", "vite")
800
- ];
801
- let viteBin = null;
802
- for (const candidate of viteBinCandidates) {
803
- if (fs6.existsSync(candidate)) {
804
- viteBin = candidate;
805
- break;
806
- }
807
- }
808
- const command = viteBin || "npx";
809
- const args = viteBin ? ["--port", String(vitePort), "--strictPort"] : ["vite", "--port", String(vitePort), "--strictPort"];
810
- const child = spawn2(command, args, {
811
- cwd: consolePath,
812
- env: {
813
- ...process.env,
814
- VITE_BASE: `${STUDIO_PATH}/`,
815
- VITE_PORT: String(vitePort),
816
- VITE_HMR_PORT: String(vitePort),
817
- VITE_RUNTIME_MODE: "server",
818
- VITE_SERVER_URL: "",
819
- // Same-origin API
820
- NODE_ENV: "development"
821
- },
822
- stdio: ["ignore", "pipe", "pipe"]
823
- });
824
- let stderr = "";
825
- child.stderr?.on("data", (data) => {
826
- stderr += data.toString();
827
- });
828
- await new Promise((resolve, reject) => {
829
- const timeout = setTimeout(() => {
830
- child.kill();
831
- reject(new Error(`Vite dev server timed out after 30 s.
832
- ${stderr}`));
833
- }, 3e4);
834
- child.stdout?.on("data", (data) => {
835
- const output = data.toString();
836
- if (output.includes("Local:") || output.includes("ready in")) {
837
- clearTimeout(timeout);
838
- resolve();
839
- }
840
- });
841
- child.on("error", (err) => {
842
- clearTimeout(timeout);
843
- reject(err);
844
- });
845
- child.on("exit", (code) => {
846
- if (code !== 0 && code !== null) {
847
- clearTimeout(timeout);
848
- reject(new Error(`Vite exited with code ${code}.
849
- ${stderr}`));
850
- }
851
- });
852
- });
853
- return { port: vitePort, process: child };
854
- }
855
- function createConsoleProxyPlugin(vitePort) {
856
- return {
857
- name: "com.objectstack.console-proxy",
858
- init: async () => {
859
- },
860
- start: async (ctx) => {
861
- const httpServer = ctx.getService?.("http.server");
862
- if (!httpServer?.getRawApp) {
863
- ctx.logger?.warn?.("Console proxy: http.server service not found \u2014 skipping");
864
- return;
865
- }
866
- const app = httpServer.getRawApp();
867
- app.get(STUDIO_PATH, (c) => c.redirect(`${STUDIO_PATH}/`));
868
- app.all(`${STUDIO_PATH}/*`, async (c) => {
869
- const targetUrl = `http://localhost:${vitePort}${c.req.path}`;
870
- try {
871
- const headers = new Headers(c.req.raw.headers);
872
- headers.delete("host");
873
- const isBodyAllowed = !["GET", "HEAD"].includes(c.req.method);
874
- const resp = await fetch(targetUrl, {
875
- method: c.req.method,
876
- headers,
877
- body: isBodyAllowed ? c.req.raw.body : void 0,
878
- // @ts-expect-error — duplex required for streaming request body
879
- duplex: isBodyAllowed ? "half" : void 0
880
- });
881
- return new Response(resp.body, {
882
- status: resp.status,
883
- headers: resp.headers
884
- });
885
- } catch {
886
- return c.text("Console dev server is starting\u2026", 502);
887
- }
888
- });
889
- }
890
- };
777
+ function hasStudioDist(studioPath) {
778
+ return fs6.existsSync(path6.join(studioPath, "dist", "index.html"));
891
779
  }
892
- function createConsoleStaticPlugin(distPath) {
780
+ function createStudioStaticPlugin(distPath, options) {
893
781
  return {
894
- name: "com.objectstack.console-static",
782
+ name: "com.objectstack.studio-static",
895
783
  init: async () => {
896
784
  },
897
785
  start: async (ctx) => {
898
786
  const httpServer = ctx.getService?.("http.server");
899
787
  if (!httpServer?.getRawApp) {
900
- ctx.logger?.warn?.("Console static: http.server service not found \u2014 skipping");
788
+ ctx.logger?.warn?.("Studio static: http.server service not found \u2014 skipping");
901
789
  return;
902
790
  }
903
791
  const app = httpServer.getRawApp();
904
792
  const absoluteDist = path6.resolve(distPath);
905
793
  const indexPath = path6.join(absoluteDist, "index.html");
906
794
  if (!fs6.existsSync(indexPath)) {
907
- ctx.logger?.warn?.(`Console static: dist not found at ${absoluteDist}`);
795
+ ctx.logger?.warn?.(`Studio static: dist not found at ${absoluteDist}`);
908
796
  return;
909
797
  }
798
+ const rawHtml = fs6.readFileSync(indexPath, "utf-8");
799
+ const rewrittenHtml = rawHtml.replace(
800
+ /(\s(?:href|src))="\/(?!\/)/g,
801
+ `$1="${STUDIO_PATH}/`
802
+ );
803
+ if (options?.isDev) {
804
+ app.get("/", (c) => c.redirect(`${STUDIO_PATH}/`));
805
+ }
910
806
  app.get(STUDIO_PATH, (c) => c.redirect(`${STUDIO_PATH}/`));
911
807
  app.get(`${STUDIO_PATH}/*`, async (c) => {
912
808
  const reqPath = c.req.path.substring(STUDIO_PATH.length) || "/";
@@ -920,8 +816,7 @@ function createConsoleStaticPlugin(distPath) {
920
816
  headers: { "content-type": mimeType(filePath) }
921
817
  });
922
818
  }
923
- const html = fs6.readFileSync(indexPath);
924
- return new Response(html, {
819
+ return new Response(rewrittenHtml, {
925
820
  headers: { "content-type": "text/html; charset=utf-8" }
926
821
  });
927
822
  });
@@ -954,7 +849,7 @@ function mimeType(filePath) {
954
849
  var getAvailablePort = async (startPort) => {
955
850
  const isPortAvailable = (port2) => {
956
851
  return new Promise((resolve) => {
957
- const server = net2.createServer();
852
+ const server = net.createServer();
958
853
  server.once("error", (err) => {
959
854
  resolve(false);
960
855
  });
@@ -973,7 +868,7 @@ var getAvailablePort = async (startPort) => {
973
868
  }
974
869
  return port;
975
870
  };
976
- var serveCommand = new Command5("serve").description("Start ObjectStack server with plugins from configuration").argument("[config]", "Configuration file path", "objectstack.config.ts").option("-p, --port <port>", "Server port", "3000").option("--dev", "Run in development mode (load devPlugins)").option("--ui", "Enable Console UI at /_studio/").option("--no-server", "Skip starting HTTP server plugin").action(async (configPath, options) => {
871
+ var serveCommand = new Command5("serve").description("Start ObjectStack server with plugins from configuration").argument("[config]", "Configuration file path", "objectstack.config.ts").option("-p, --port <port>", "Server port", "3000").option("--dev", "Run in development mode (load devPlugins)").option("--ui", "Enable Studio UI at /_studio/ (default: true in dev mode)").option("--no-server", "Skip starting HTTP server plugin").action(async (configPath, options) => {
977
872
  let port = parseInt(options.port);
978
873
  try {
979
874
  const availablePort = await getAvailablePort(port);
@@ -1101,29 +996,30 @@ var serveCommand = new Command5("serve").description("Start ObjectStack server w
1101
996
  } catch (e) {
1102
997
  console.warn(chalk7.yellow(` \u26A0 HTTP server plugin not available: ${e.message}`));
1103
998
  }
999
+ try {
1000
+ const { createRestApiPlugin } = await import("@objectstack/rest");
1001
+ await kernel.use(createRestApiPlugin());
1002
+ trackPlugin("RestAPI");
1003
+ } catch (e) {
1004
+ }
1005
+ try {
1006
+ const { createDispatcherPlugin } = await import("@objectstack/runtime");
1007
+ await kernel.use(createDispatcherPlugin());
1008
+ trackPlugin("Dispatcher");
1009
+ } catch (e) {
1010
+ }
1104
1011
  }
1105
- let viteProcess = null;
1106
- if (options.ui) {
1107
- const consolePath = resolveConsolePath();
1108
- if (!consolePath) {
1109
- console.warn(chalk7.yellow(` \u26A0 @objectstack/console not found \u2014 skipping UI`));
1110
- } else if (isDev) {
1111
- try {
1112
- const result = await spawnViteDevServer(consolePath, { serverPort: port });
1113
- viteProcess = result.process;
1114
- await kernel.use(createConsoleProxyPlugin(result.port));
1115
- trackPlugin("ConsoleUI");
1116
- } catch (e) {
1117
- console.warn(chalk7.yellow(` \u26A0 Console UI failed to start: ${e.message}`));
1118
- }
1012
+ const enableUI = options.ui ?? isDev;
1013
+ if (enableUI) {
1014
+ const studioPath = resolveStudioPath();
1015
+ if (!studioPath) {
1016
+ console.warn(chalk7.yellow(` \u26A0 @objectstack/studio not found \u2014 skipping UI`));
1017
+ } else if (hasStudioDist(studioPath)) {
1018
+ const distPath = path7.join(studioPath, "dist");
1019
+ await kernel.use(createStudioStaticPlugin(distPath, { isDev }));
1020
+ trackPlugin("StudioUI");
1119
1021
  } else {
1120
- const distPath = path7.join(consolePath, "dist");
1121
- if (hasConsoleDist(consolePath)) {
1122
- await kernel.use(createConsoleStaticPlugin(distPath));
1123
- trackPlugin("ConsoleUI");
1124
- } else {
1125
- console.warn(chalk7.yellow(` \u26A0 Console dist not found \u2014 run "pnpm --filter @objectstack/console build" first`));
1126
- }
1022
+ console.warn(chalk7.yellow(` \u26A0 Studio dist not found \u2014 run "pnpm --filter @objectstack/studio build" first`));
1127
1023
  }
1128
1024
  }
1129
1025
  await runtime.start();
@@ -1135,17 +1031,13 @@ var serveCommand = new Command5("serve").description("Start ObjectStack server w
1135
1031
  isDev,
1136
1032
  pluginCount: loadedPlugins.length,
1137
1033
  pluginNames: loadedPlugins,
1138
- uiEnabled: !!options.ui,
1034
+ uiEnabled: enableUI,
1139
1035
  studioPath: STUDIO_PATH
1140
1036
  });
1141
1037
  process.on("SIGINT", async () => {
1142
1038
  console.warn(chalk7.yellow(`
1143
1039
 
1144
1040
  \u23F9 Stopping server...`));
1145
- if (viteProcess) {
1146
- viteProcess.kill();
1147
- viteProcess = null;
1148
- }
1149
1041
  await runtime.getKernel().shutdown();
1150
1042
  console.log(chalk7.green(`\u2705 Server stopped`));
1151
1043
  process.exit(0);
@@ -1161,8 +1053,8 @@ var serveCommand = new Command5("serve").description("Start ObjectStack server w
1161
1053
 
1162
1054
  // src/commands/studio.ts
1163
1055
  import { Command as Command6 } from "commander";
1164
- import { spawn as spawn3 } from "child_process";
1165
- var studioCommand = new Command6("studio").description("Launch Console UI with development server").argument("[config]", "Configuration file path", "objectstack.config.ts").option("-p, --port <port>", "Server port", "3000").action(async (configPath, options) => {
1056
+ import { spawn as spawn2 } from "child_process";
1057
+ var studioCommand = new Command6("studio").description("Launch Studio UI with development server").argument("[config]", "Configuration file path", "objectstack.config.ts").option("-p, --port <port>", "Server port", "3000").action(async (configPath, options) => {
1166
1058
  printHeader("Studio");
1167
1059
  printKV("Mode", "dev + ui", "\u{1F3A8}");
1168
1060
  printStep("Delegating to serve --dev --ui \u2026");
@@ -1177,7 +1069,7 @@ var studioCommand = new Command6("studio").description("Launch Console UI with d
1177
1069
  "--port",
1178
1070
  options.port
1179
1071
  ];
1180
- const child = spawn3(process.execPath, args, {
1072
+ const child = spawn2(process.execPath, args, {
1181
1073
  stdio: "inherit",
1182
1074
  env: { ...process.env, NODE_ENV: "development" }
1183
1075
  });
@@ -1979,7 +1871,7 @@ ${chalk13.bold("Workflow:")}
1979
1871
  ${chalk13.dim("$")} os generate object task ${chalk13.dim("# Add metadata")}
1980
1872
  ${chalk13.dim("$")} os validate ${chalk13.dim("# Check configuration")}
1981
1873
  ${chalk13.dim("$")} os dev ${chalk13.dim("# Start dev server")}
1982
- ${chalk13.dim("$")} os studio ${chalk13.dim("# Dev server + Console UI")}
1874
+ ${chalk13.dim("$")} os studio ${chalk13.dim("# Dev server + Studio UI")}
1983
1875
  ${chalk13.dim("$")} os compile ${chalk13.dim("# Build for production")}
1984
1876
 
1985
1877
  ${chalk13.dim("Aliases: objectstack | os")}
package/dist/index.js CHANGED
@@ -126,7 +126,7 @@ function printServerReady(opts) {
126
126
  console.log("");
127
127
  console.log(chalk.cyan(" \u279C") + chalk.bold(" API: ") + chalk.cyan(base + "/"));
128
128
  if (opts.uiEnabled && opts.studioPath) {
129
- console.log(chalk.cyan(" \u279C") + chalk.bold(" Console: ") + chalk.cyan(base + opts.studioPath + "/"));
129
+ console.log(chalk.cyan(" \u279C") + chalk.bold(" Studio: ") + chalk.cyan(base + opts.studioPath + "/"));
130
130
  }
131
131
  console.log("");
132
132
  console.log(chalk.dim(` Config: ${opts.configFile}`));
@@ -1251,7 +1251,7 @@ import chalk9 from "chalk";
1251
1251
  import { execSync, spawn } from "child_process";
1252
1252
  import fs6 from "fs";
1253
1253
  import path6 from "path";
1254
- var devCommand = new Command7("dev").description("Start development mode with hot-reload").argument("[package]", "Package name or filter pattern", "all").option("-w, --watch", "Enable watch mode (default)", true).option("--ui", "Enable Console UI at /_studio/").option("-v, --verbose", "Verbose output").action(async (packageName, options) => {
1254
+ var devCommand = new Command7("dev").description("Start development mode with hot-reload").argument("[package]", "Package name or filter pattern", "all").option("-w, --watch", "Enable watch mode (default)", true).option("--ui", "Enable Studio UI at /_studio/").option("-v, --verbose", "Verbose output").action(async (packageName, options) => {
1255
1255
  printHeader("Development Mode");
1256
1256
  const configPath = path6.resolve(process.cwd(), "objectstack.config.ts");
1257
1257
  if (packageName === "all" && fs6.existsSync(configPath)) {
@@ -1293,30 +1293,27 @@ var devCommand = new Command7("dev").description("Start development mode with ho
1293
1293
  import { Command as Command8 } from "commander";
1294
1294
  import path8 from "path";
1295
1295
  import fs8 from "fs";
1296
- import net2 from "net";
1296
+ import net from "net";
1297
1297
  import chalk10 from "chalk";
1298
1298
  import { bundleRequire as bundleRequire2 } from "bundle-require";
1299
1299
 
1300
- // src/utils/console.ts
1300
+ // src/utils/studio.ts
1301
1301
  import path7 from "path";
1302
1302
  import fs7 from "fs";
1303
- import net from "net";
1304
- import { spawn as spawn2 } from "child_process";
1305
1303
  var STUDIO_PATH = "/_studio";
1306
- var VITE_PORT_START = 24678;
1307
- function resolveConsolePath() {
1304
+ function resolveStudioPath() {
1308
1305
  const cwd = process.cwd();
1309
1306
  const candidates = [
1310
- path7.resolve(cwd, "apps/console"),
1311
- path7.resolve(cwd, "../../apps/console"),
1312
- path7.resolve(cwd, "../apps/console")
1307
+ path7.resolve(cwd, "apps/studio"),
1308
+ path7.resolve(cwd, "../../apps/studio"),
1309
+ path7.resolve(cwd, "../apps/studio")
1313
1310
  ];
1314
1311
  for (const candidate of candidates) {
1315
1312
  const pkgPath = path7.join(candidate, "package.json");
1316
1313
  if (fs7.existsSync(pkgPath)) {
1317
1314
  try {
1318
1315
  const pkg = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
1319
- if (pkg.name === "@objectstack/console") return candidate;
1316
+ if (pkg.name === "@objectstack/studio") return candidate;
1320
1317
  } catch {
1321
1318
  }
1322
1319
  }
@@ -1324,142 +1321,41 @@ function resolveConsolePath() {
1324
1321
  try {
1325
1322
  const { createRequire } = __require("module");
1326
1323
  const req = createRequire(import.meta.url);
1327
- const resolved = req.resolve("@objectstack/console/package.json");
1324
+ const resolved = req.resolve("@objectstack/studio/package.json");
1328
1325
  return path7.dirname(resolved);
1329
1326
  } catch {
1330
1327
  return null;
1331
1328
  }
1332
1329
  }
1333
- function hasConsoleDist(consolePath) {
1334
- return fs7.existsSync(path7.join(consolePath, "dist", "index.html"));
1335
- }
1336
- function findAvailablePort(start = VITE_PORT_START) {
1337
- return new Promise((resolve, reject) => {
1338
- const server = net.createServer();
1339
- server.once("error", () => {
1340
- findAvailablePort(start + 1).then(resolve, reject);
1341
- });
1342
- server.once("listening", () => {
1343
- server.close(() => resolve(start));
1344
- });
1345
- server.listen(start);
1346
- });
1347
- }
1348
- async function spawnViteDevServer(consolePath, options = {}) {
1349
- const vitePort = await findAvailablePort(VITE_PORT_START);
1350
- const viteBinCandidates = [
1351
- path7.join(consolePath, "node_modules", ".bin", "vite"),
1352
- path7.join(consolePath, "..", "..", "node_modules", ".bin", "vite")
1353
- ];
1354
- let viteBin = null;
1355
- for (const candidate of viteBinCandidates) {
1356
- if (fs7.existsSync(candidate)) {
1357
- viteBin = candidate;
1358
- break;
1359
- }
1360
- }
1361
- const command = viteBin || "npx";
1362
- const args = viteBin ? ["--port", String(vitePort), "--strictPort"] : ["vite", "--port", String(vitePort), "--strictPort"];
1363
- const child = spawn2(command, args, {
1364
- cwd: consolePath,
1365
- env: {
1366
- ...process.env,
1367
- VITE_BASE: `${STUDIO_PATH}/`,
1368
- VITE_PORT: String(vitePort),
1369
- VITE_HMR_PORT: String(vitePort),
1370
- VITE_RUNTIME_MODE: "server",
1371
- VITE_SERVER_URL: "",
1372
- // Same-origin API
1373
- NODE_ENV: "development"
1374
- },
1375
- stdio: ["ignore", "pipe", "pipe"]
1376
- });
1377
- let stderr = "";
1378
- child.stderr?.on("data", (data) => {
1379
- stderr += data.toString();
1380
- });
1381
- await new Promise((resolve, reject) => {
1382
- const timeout = setTimeout(() => {
1383
- child.kill();
1384
- reject(new Error(`Vite dev server timed out after 30 s.
1385
- ${stderr}`));
1386
- }, 3e4);
1387
- child.stdout?.on("data", (data) => {
1388
- const output = data.toString();
1389
- if (output.includes("Local:") || output.includes("ready in")) {
1390
- clearTimeout(timeout);
1391
- resolve();
1392
- }
1393
- });
1394
- child.on("error", (err) => {
1395
- clearTimeout(timeout);
1396
- reject(err);
1397
- });
1398
- child.on("exit", (code) => {
1399
- if (code !== 0 && code !== null) {
1400
- clearTimeout(timeout);
1401
- reject(new Error(`Vite exited with code ${code}.
1402
- ${stderr}`));
1403
- }
1404
- });
1405
- });
1406
- return { port: vitePort, process: child };
1407
- }
1408
- function createConsoleProxyPlugin(vitePort) {
1409
- return {
1410
- name: "com.objectstack.console-proxy",
1411
- init: async () => {
1412
- },
1413
- start: async (ctx) => {
1414
- const httpServer = ctx.getService?.("http.server");
1415
- if (!httpServer?.getRawApp) {
1416
- ctx.logger?.warn?.("Console proxy: http.server service not found \u2014 skipping");
1417
- return;
1418
- }
1419
- const app = httpServer.getRawApp();
1420
- app.get(STUDIO_PATH, (c) => c.redirect(`${STUDIO_PATH}/`));
1421
- app.all(`${STUDIO_PATH}/*`, async (c) => {
1422
- const targetUrl = `http://localhost:${vitePort}${c.req.path}`;
1423
- try {
1424
- const headers = new Headers(c.req.raw.headers);
1425
- headers.delete("host");
1426
- const isBodyAllowed = !["GET", "HEAD"].includes(c.req.method);
1427
- const resp = await fetch(targetUrl, {
1428
- method: c.req.method,
1429
- headers,
1430
- body: isBodyAllowed ? c.req.raw.body : void 0,
1431
- // @ts-expect-error — duplex required for streaming request body
1432
- duplex: isBodyAllowed ? "half" : void 0
1433
- });
1434
- return new Response(resp.body, {
1435
- status: resp.status,
1436
- headers: resp.headers
1437
- });
1438
- } catch {
1439
- return c.text("Console dev server is starting\u2026", 502);
1440
- }
1441
- });
1442
- }
1443
- };
1330
+ function hasStudioDist(studioPath) {
1331
+ return fs7.existsSync(path7.join(studioPath, "dist", "index.html"));
1444
1332
  }
1445
- function createConsoleStaticPlugin(distPath) {
1333
+ function createStudioStaticPlugin(distPath, options) {
1446
1334
  return {
1447
- name: "com.objectstack.console-static",
1335
+ name: "com.objectstack.studio-static",
1448
1336
  init: async () => {
1449
1337
  },
1450
1338
  start: async (ctx) => {
1451
1339
  const httpServer = ctx.getService?.("http.server");
1452
1340
  if (!httpServer?.getRawApp) {
1453
- ctx.logger?.warn?.("Console static: http.server service not found \u2014 skipping");
1341
+ ctx.logger?.warn?.("Studio static: http.server service not found \u2014 skipping");
1454
1342
  return;
1455
1343
  }
1456
1344
  const app = httpServer.getRawApp();
1457
1345
  const absoluteDist = path7.resolve(distPath);
1458
1346
  const indexPath = path7.join(absoluteDist, "index.html");
1459
1347
  if (!fs7.existsSync(indexPath)) {
1460
- ctx.logger?.warn?.(`Console static: dist not found at ${absoluteDist}`);
1348
+ ctx.logger?.warn?.(`Studio static: dist not found at ${absoluteDist}`);
1461
1349
  return;
1462
1350
  }
1351
+ const rawHtml = fs7.readFileSync(indexPath, "utf-8");
1352
+ const rewrittenHtml = rawHtml.replace(
1353
+ /(\s(?:href|src))="\/(?!\/)/g,
1354
+ `$1="${STUDIO_PATH}/`
1355
+ );
1356
+ if (options?.isDev) {
1357
+ app.get("/", (c) => c.redirect(`${STUDIO_PATH}/`));
1358
+ }
1463
1359
  app.get(STUDIO_PATH, (c) => c.redirect(`${STUDIO_PATH}/`));
1464
1360
  app.get(`${STUDIO_PATH}/*`, async (c) => {
1465
1361
  const reqPath = c.req.path.substring(STUDIO_PATH.length) || "/";
@@ -1473,8 +1369,7 @@ function createConsoleStaticPlugin(distPath) {
1473
1369
  headers: { "content-type": mimeType(filePath) }
1474
1370
  });
1475
1371
  }
1476
- const html = fs7.readFileSync(indexPath);
1477
- return new Response(html, {
1372
+ return new Response(rewrittenHtml, {
1478
1373
  headers: { "content-type": "text/html; charset=utf-8" }
1479
1374
  });
1480
1375
  });
@@ -1507,7 +1402,7 @@ function mimeType(filePath) {
1507
1402
  var getAvailablePort = async (startPort) => {
1508
1403
  const isPortAvailable = (port2) => {
1509
1404
  return new Promise((resolve) => {
1510
- const server = net2.createServer();
1405
+ const server = net.createServer();
1511
1406
  server.once("error", (err) => {
1512
1407
  resolve(false);
1513
1408
  });
@@ -1526,7 +1421,7 @@ var getAvailablePort = async (startPort) => {
1526
1421
  }
1527
1422
  return port;
1528
1423
  };
1529
- var serveCommand = new Command8("serve").description("Start ObjectStack server with plugins from configuration").argument("[config]", "Configuration file path", "objectstack.config.ts").option("-p, --port <port>", "Server port", "3000").option("--dev", "Run in development mode (load devPlugins)").option("--ui", "Enable Console UI at /_studio/").option("--no-server", "Skip starting HTTP server plugin").action(async (configPath, options) => {
1424
+ var serveCommand = new Command8("serve").description("Start ObjectStack server with plugins from configuration").argument("[config]", "Configuration file path", "objectstack.config.ts").option("-p, --port <port>", "Server port", "3000").option("--dev", "Run in development mode (load devPlugins)").option("--ui", "Enable Studio UI at /_studio/ (default: true in dev mode)").option("--no-server", "Skip starting HTTP server plugin").action(async (configPath, options) => {
1530
1425
  let port = parseInt(options.port);
1531
1426
  try {
1532
1427
  const availablePort = await getAvailablePort(port);
@@ -1654,29 +1549,30 @@ var serveCommand = new Command8("serve").description("Start ObjectStack server w
1654
1549
  } catch (e) {
1655
1550
  console.warn(chalk10.yellow(` \u26A0 HTTP server plugin not available: ${e.message}`));
1656
1551
  }
1552
+ try {
1553
+ const { createRestApiPlugin } = await import("@objectstack/rest");
1554
+ await kernel.use(createRestApiPlugin());
1555
+ trackPlugin("RestAPI");
1556
+ } catch (e) {
1557
+ }
1558
+ try {
1559
+ const { createDispatcherPlugin } = await import("@objectstack/runtime");
1560
+ await kernel.use(createDispatcherPlugin());
1561
+ trackPlugin("Dispatcher");
1562
+ } catch (e) {
1563
+ }
1657
1564
  }
1658
- let viteProcess = null;
1659
- if (options.ui) {
1660
- const consolePath = resolveConsolePath();
1661
- if (!consolePath) {
1662
- console.warn(chalk10.yellow(` \u26A0 @objectstack/console not found \u2014 skipping UI`));
1663
- } else if (isDev) {
1664
- try {
1665
- const result = await spawnViteDevServer(consolePath, { serverPort: port });
1666
- viteProcess = result.process;
1667
- await kernel.use(createConsoleProxyPlugin(result.port));
1668
- trackPlugin("ConsoleUI");
1669
- } catch (e) {
1670
- console.warn(chalk10.yellow(` \u26A0 Console UI failed to start: ${e.message}`));
1671
- }
1565
+ const enableUI = options.ui ?? isDev;
1566
+ if (enableUI) {
1567
+ const studioPath = resolveStudioPath();
1568
+ if (!studioPath) {
1569
+ console.warn(chalk10.yellow(` \u26A0 @objectstack/studio not found \u2014 skipping UI`));
1570
+ } else if (hasStudioDist(studioPath)) {
1571
+ const distPath = path8.join(studioPath, "dist");
1572
+ await kernel.use(createStudioStaticPlugin(distPath, { isDev }));
1573
+ trackPlugin("StudioUI");
1672
1574
  } else {
1673
- const distPath = path8.join(consolePath, "dist");
1674
- if (hasConsoleDist(consolePath)) {
1675
- await kernel.use(createConsoleStaticPlugin(distPath));
1676
- trackPlugin("ConsoleUI");
1677
- } else {
1678
- console.warn(chalk10.yellow(` \u26A0 Console dist not found \u2014 run "pnpm --filter @objectstack/console build" first`));
1679
- }
1575
+ console.warn(chalk10.yellow(` \u26A0 Studio dist not found \u2014 run "pnpm --filter @objectstack/studio build" first`));
1680
1576
  }
1681
1577
  }
1682
1578
  await runtime.start();
@@ -1688,17 +1584,13 @@ var serveCommand = new Command8("serve").description("Start ObjectStack server w
1688
1584
  isDev,
1689
1585
  pluginCount: loadedPlugins.length,
1690
1586
  pluginNames: loadedPlugins,
1691
- uiEnabled: !!options.ui,
1587
+ uiEnabled: enableUI,
1692
1588
  studioPath: STUDIO_PATH
1693
1589
  });
1694
1590
  process.on("SIGINT", async () => {
1695
1591
  console.warn(chalk10.yellow(`
1696
1592
 
1697
1593
  \u23F9 Stopping server...`));
1698
- if (viteProcess) {
1699
- viteProcess.kill();
1700
- viteProcess = null;
1701
- }
1702
1594
  await runtime.getKernel().shutdown();
1703
1595
  console.log(chalk10.green(`\u2705 Server stopped`));
1704
1596
  process.exit(0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@objectstack/cli",
3
- "version": "1.1.0",
3
+ "version": "2.0.0",
4
4
  "description": "Command Line Interface for ObjectStack Protocol",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -22,15 +22,16 @@
22
22
  "commander": "^14.0.3",
23
23
  "tsx": "^4.7.1",
24
24
  "zod": "^3.24.1",
25
- "@objectstack/core": "1.1.0",
26
- "@objectstack/driver-memory": "^1.1.0",
27
- "@objectstack/objectql": "^1.1.0",
28
- "@objectstack/plugin-hono-server": "1.1.0",
29
- "@objectstack/runtime": "^1.1.0",
30
- "@objectstack/spec": "1.1.0"
25
+ "@objectstack/core": "2.0.0",
26
+ "@objectstack/driver-memory": "^2.0.0",
27
+ "@objectstack/objectql": "^2.0.0",
28
+ "@objectstack/plugin-hono-server": "2.0.0",
29
+ "@objectstack/rest": "2.0.0",
30
+ "@objectstack/runtime": "^2.0.0",
31
+ "@objectstack/spec": "2.0.0"
31
32
  },
32
33
  "peerDependencies": {
33
- "@objectstack/core": "^1.0.12"
34
+ "@objectstack/core": "2.0.0"
34
35
  },
35
36
  "devDependencies": {
36
37
  "@types/node": "^25.1.0",
package/src/bin.ts CHANGED
@@ -1,3 +1,5 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
1
3
  import { createRequire } from 'module';
2
4
  import { Command } from 'commander';
3
5
  import chalk from 'chalk';
@@ -46,7 +48,7 @@ ${chalk.bold('Workflow:')}
46
48
  ${chalk.dim('$')} os generate object task ${chalk.dim('# Add metadata')}
47
49
  ${chalk.dim('$')} os validate ${chalk.dim('# Check configuration')}
48
50
  ${chalk.dim('$')} os dev ${chalk.dim('# Start dev server')}
49
- ${chalk.dim('$')} os studio ${chalk.dim('# Dev server + Console UI')}
51
+ ${chalk.dim('$')} os studio ${chalk.dim('# Dev server + Studio UI')}
50
52
  ${chalk.dim('$')} os compile ${chalk.dim('# Build for production')}
51
53
 
52
54
  ${chalk.dim('Aliases: objectstack | os')}
@@ -1,3 +1,5 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
1
3
  import { Command } from 'commander';
2
4
  import path from 'path';
3
5
  import fs from 'fs';
@@ -1,3 +1,5 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
1
3
  import { Command } from 'commander';
2
4
  import chalk from 'chalk';
3
5
  import fs from 'fs';
@@ -1,3 +1,5 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
1
3
  import { Command } from 'commander';
2
4
  import chalk from 'chalk';
3
5
  import { execSync, spawn } from 'child_process';
@@ -9,7 +11,7 @@ export const devCommand = new Command('dev')
9
11
  .description('Start development mode with hot-reload')
10
12
  .argument('[package]', 'Package name or filter pattern', 'all')
11
13
  .option('-w, --watch', 'Enable watch mode (default)', true)
12
- .option('--ui', 'Enable Console UI at /_studio/')
14
+ .option('--ui', 'Enable Studio UI at /_studio/')
13
15
  .option('-v, --verbose', 'Verbose output')
14
16
  .action(async (packageName, options) => {
15
17
  printHeader('Development Mode');
@@ -1,3 +1,5 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
1
3
  import { Command } from 'commander';
2
4
  import chalk from 'chalk';
3
5
  import { execSync } from 'child_process';
@@ -1,3 +1,5 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
1
3
  import { Command } from 'commander';
2
4
  import chalk from 'chalk';
3
5
  import fs from 'fs';
@@ -1,3 +1,5 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
1
3
  import { Command } from 'commander';
2
4
  import chalk from 'chalk';
3
5
  import { loadConfig } from '../utils/config.js';
@@ -1,3 +1,5 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
1
3
  import { Command } from 'commander';
2
4
  import chalk from 'chalk';
3
5
  import fs from 'fs';
@@ -1,3 +1,5 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
1
3
  import { Command } from 'commander';
2
4
  import path from 'path';
3
5
  import fs from 'fs';
@@ -16,12 +18,10 @@ import {
16
18
  } from '../utils/format.js';
17
19
  import {
18
20
  STUDIO_PATH,
19
- resolveConsolePath,
20
- hasConsoleDist,
21
- spawnViteDevServer,
22
- createConsoleProxyPlugin,
23
- createConsoleStaticPlugin,
24
- } from '../utils/console.js';
21
+ resolveStudioPath,
22
+ hasStudioDist,
23
+ createStudioStaticPlugin,
24
+ } from '../utils/studio.js';
25
25
 
26
26
  // Helper to find available port
27
27
  const getAvailablePort = async (startPort: number): Promise<number> => {
@@ -53,7 +53,7 @@ export const serveCommand = new Command('serve')
53
53
  .argument('[config]', 'Configuration file path', 'objectstack.config.ts')
54
54
  .option('-p, --port <port>', 'Server port', '3000')
55
55
  .option('--dev', 'Run in development mode (load devPlugins)')
56
- .option('--ui', 'Enable Console UI at /_studio/')
56
+ .option('--ui', 'Enable Studio UI at /_studio/ (default: true in dev mode)')
57
57
  .option('--no-server', 'Skip starting HTTP server plugin')
58
58
  .action(async (configPath, options) => {
59
59
  let port = parseInt(options.port);
@@ -226,34 +226,41 @@ export const serveCommand = new Command('serve')
226
226
  } catch (e: any) {
227
227
  console.warn(chalk.yellow(` ⚠ HTTP server plugin not available: ${e.message}`));
228
228
  }
229
- }
230
229
 
231
- // ── Console UI (--ui) ───────────────────────────────────────────
232
- let viteProcess: import('child_process').ChildProcess | null = null;
230
+ // Register REST API plugin (consumes http.server + protocol services)
231
+ try {
232
+ const { createRestApiPlugin } = await import('@objectstack/rest');
233
+ await kernel.use(createRestApiPlugin());
234
+ trackPlugin('RestAPI');
235
+ } catch (e: any) {
236
+ // @objectstack/rest is optional
237
+ }
233
238
 
234
- if (options.ui) {
235
- const consolePath = resolveConsolePath();
236
- if (!consolePath) {
237
- console.warn(chalk.yellow(` ⚠ @objectstack/console not found — skipping UI`));
238
- } else if (isDev) {
239
- // Dev mode spawn Vite dev server & proxy through Hono
240
- try {
241
- const result = await spawnViteDevServer(consolePath, { serverPort: port });
242
- viteProcess = result.process;
243
- await kernel.use(createConsoleProxyPlugin(result.port));
244
- trackPlugin('ConsoleUI');
245
- } catch (e: any) {
246
- console.warn(chalk.yellow(` ⚠ Console UI failed to start: ${e.message}`));
247
- }
239
+ // Register Dispatcher plugin (auth, graphql, analytics, packages, hub, storage, automation)
240
+ try {
241
+ const { createDispatcherPlugin } = await import('@objectstack/runtime');
242
+ await kernel.use(createDispatcherPlugin());
243
+ trackPlugin('Dispatcher');
244
+ } catch (e: any) {
245
+ // optional
246
+ }
247
+ }
248
+
249
+ // ── Studio UI ─────────────────────────────────────────────────
250
+ // In dev mode, Studio UI is enabled by default (use --no-ui to disable).
251
+ // Always serves the pre-built dist/ — no Vite dev server, no extra port.
252
+ const enableUI = options.ui ?? isDev;
253
+
254
+ if (enableUI) {
255
+ const studioPath = resolveStudioPath();
256
+ if (!studioPath) {
257
+ console.warn(chalk.yellow(` ⚠ @objectstack/studio not found — skipping UI`));
258
+ } else if (hasStudioDist(studioPath)) {
259
+ const distPath = path.join(studioPath, 'dist');
260
+ await kernel.use(createStudioStaticPlugin(distPath, { isDev }));
261
+ trackPlugin('StudioUI');
248
262
  } else {
249
- // Production mode serve pre-built static files
250
- const distPath = path.join(consolePath, 'dist');
251
- if (hasConsoleDist(consolePath)) {
252
- await kernel.use(createConsoleStaticPlugin(distPath));
253
- trackPlugin('ConsoleUI');
254
- } else {
255
- console.warn(chalk.yellow(` ⚠ Console dist not found — run "pnpm --filter @objectstack/console build" first`));
256
- }
263
+ console.warn(chalk.yellow(` ⚠ Studio dist not found run "pnpm --filter @objectstack/studio build" first`));
257
264
  }
258
265
  }
259
266
 
@@ -271,17 +278,13 @@ export const serveCommand = new Command('serve')
271
278
  isDev,
272
279
  pluginCount: loadedPlugins.length,
273
280
  pluginNames: loadedPlugins,
274
- uiEnabled: !!options.ui,
281
+ uiEnabled: enableUI,
275
282
  studioPath: STUDIO_PATH,
276
283
  });
277
284
 
278
285
  // Keep process alive
279
286
  process.on('SIGINT', async () => {
280
287
  console.warn(chalk.yellow(`\n\n⏹ Stopping server...`));
281
- if (viteProcess) {
282
- viteProcess.kill();
283
- viteProcess = null;
284
- }
285
288
  await runtime.getKernel().shutdown();
286
289
  console.log(chalk.green(`✅ Server stopped`));
287
290
  process.exit(0);
@@ -1,17 +1,19 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
1
3
  import { Command } from 'commander';
2
4
  import chalk from 'chalk';
3
5
  import { spawn } from 'child_process';
4
6
  import { printHeader, printKV, printStep } from '../utils/format.js';
5
7
 
6
8
  /**
7
- * `objectstack studio` — Launch the ObjectStack Console UI.
9
+ * `objectstack studio` — Launch the ObjectStack Studio UI.
8
10
  *
9
11
  * Alias for `objectstack serve --dev --ui`.
10
- * Starts the ObjectStack server in development mode with the Console
12
+ * Starts the ObjectStack server in development mode with the Studio
11
13
  * UI available at http://localhost:<port>/_studio/
12
14
  */
13
15
  export const studioCommand = new Command('studio')
14
- .description('Launch Console UI with development server')
16
+ .description('Launch Studio UI with development server')
15
17
  .argument('[config]', 'Configuration file path', 'objectstack.config.ts')
16
18
  .option('-p, --port <port>', 'Server port', '3000')
17
19
  .action(async (configPath, options) => {
@@ -1,3 +1,5 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
1
3
  import { Command } from 'commander';
2
4
  import chalk from 'chalk';
3
5
  import path from 'path';
@@ -1,3 +1,5 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
1
3
  import { Command } from 'commander';
2
4
  import chalk from 'chalk';
3
5
  import { ZodError } from 'zod';
package/src/index.ts CHANGED
@@ -1,3 +1,5 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
1
3
  export * from './commands/compile.js';
2
4
  export * from './commands/validate.js';
3
5
  export * from './commands/info.js';
@@ -1,3 +1,5 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
1
3
  import path from 'path';
2
4
  import fs from 'fs';
3
5
  import chalk from 'chalk';
@@ -1,3 +1,5 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
1
3
  import chalk from 'chalk';
2
4
  import type { ZodError } from 'zod';
3
5
 
@@ -188,7 +190,7 @@ export function printServerReady(opts: ServerReadyOptions) {
188
190
  console.log('');
189
191
  console.log(chalk.cyan(' ➜') + chalk.bold(' API: ') + chalk.cyan(base + '/'));
190
192
  if (opts.uiEnabled && opts.studioPath) {
191
- console.log(chalk.cyan(' ➜') + chalk.bold(' Console: ') + chalk.cyan(base + opts.studioPath + '/'));
193
+ console.log(chalk.cyan(' ➜') + chalk.bold(' Studio: ') + chalk.cyan(base + opts.studioPath + '/'));
192
194
  }
193
195
  console.log('');
194
196
  console.log(chalk.dim(` Config: ${opts.configFile}`));
@@ -1,7 +1,9 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
1
3
  /**
2
- * Console UI Integration Utilities
4
+ * Studio UI Integration Utilities
3
5
  *
4
- * Handles resolving, spawning, and proxying the @objectstack/console
6
+ * Handles resolving, spawning, and proxying the @objectstack/studio
5
7
  * frontend when the CLI is started with --ui or via the `studio` command.
6
8
  */
7
9
  import path from 'path';
@@ -21,17 +23,17 @@ const VITE_PORT_START = 24678;
21
23
  // ─── Path Resolution ────────────────────────────────────────────────
22
24
 
23
25
  /**
24
- * Resolve the filesystem path to the @objectstack/console package.
26
+ * Resolve the filesystem path to the @objectstack/studio package.
25
27
  * Searches workspace locations first, then falls back to node_modules.
26
28
  */
27
- export function resolveConsolePath(): string | null {
29
+ export function resolveStudioPath(): string | null {
28
30
  const cwd = process.cwd();
29
31
 
30
32
  // Workspace candidates (monorepo layouts)
31
33
  const candidates = [
32
- path.resolve(cwd, 'apps/console'),
33
- path.resolve(cwd, '../../apps/console'),
34
- path.resolve(cwd, '../apps/console'),
34
+ path.resolve(cwd, 'apps/studio'),
35
+ path.resolve(cwd, '../../apps/studio'),
36
+ path.resolve(cwd, '../apps/studio'),
35
37
  ];
36
38
 
37
39
  for (const candidate of candidates) {
@@ -39,7 +41,7 @@ export function resolveConsolePath(): string | null {
39
41
  if (fs.existsSync(pkgPath)) {
40
42
  try {
41
43
  const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
42
- if (pkg.name === '@objectstack/console') return candidate;
44
+ if (pkg.name === '@objectstack/studio') return candidate;
43
45
  } catch {
44
46
  // Skip invalid package.json
45
47
  }
@@ -50,7 +52,7 @@ export function resolveConsolePath(): string | null {
50
52
  try {
51
53
  const { createRequire } = require('module');
52
54
  const req = createRequire(import.meta.url);
53
- const resolved = req.resolve('@objectstack/console/package.json');
55
+ const resolved = req.resolve('@objectstack/studio/package.json');
54
56
  return path.dirname(resolved);
55
57
  } catch {
56
58
  return null;
@@ -58,10 +60,10 @@ export function resolveConsolePath(): string | null {
58
60
  }
59
61
 
60
62
  /**
61
- * Check whether the Console has a pre-built `dist/` directory.
63
+ * Check whether the Studio has a pre-built `dist/` directory.
62
64
  */
63
- export function hasConsoleDist(consolePath: string): boolean {
64
- return fs.existsSync(path.join(consolePath, 'dist', 'index.html'));
65
+ export function hasStudioDist(studioPath: string): boolean {
66
+ return fs.existsSync(path.join(studioPath, 'dist', 'index.html'));
65
67
  }
66
68
 
67
69
  // ─── Port Utilities ─────────────────────────────────────────────────
@@ -98,19 +100,19 @@ export interface ViteDevResult {
98
100
  * Sets environment variables so the Console runs in server mode and
99
101
  * connects to the ObjectStack API on the same origin.
100
102
  *
101
- * @param consolePath - Absolute path to the @objectstack/console package
103
+ * @param studioPath - Absolute path to the @objectstack/studio package
102
104
  * @param options.serverPort - The main ObjectStack server port (for display only)
103
105
  */
104
106
  export async function spawnViteDevServer(
105
- consolePath: string,
107
+ studioPath: string,
106
108
  options: { serverPort?: number } = {},
107
109
  ): Promise<ViteDevResult> {
108
110
  const vitePort = await findAvailablePort(VITE_PORT_START);
109
111
 
110
- // Resolve the Vite binary from the Console's own dependencies
112
+ // Resolve the Vite binary from the Studio's own dependencies
111
113
  const viteBinCandidates = [
112
- path.join(consolePath, 'node_modules', '.bin', 'vite'),
113
- path.join(consolePath, '..', '..', 'node_modules', '.bin', 'vite'),
114
+ path.join(studioPath, 'node_modules', '.bin', 'vite'),
115
+ path.join(studioPath, '..', '..', 'node_modules', '.bin', 'vite'),
114
116
  ];
115
117
 
116
118
  let viteBin: string | null = null;
@@ -127,7 +129,7 @@ export async function spawnViteDevServer(
127
129
  : ['vite', '--port', String(vitePort), '--strictPort'];
128
130
 
129
131
  const child = spawn(command, args, {
130
- cwd: consolePath,
132
+ cwd: studioPath,
131
133
  env: {
132
134
  ...process.env,
133
135
  VITE_BASE: `${STUDIO_PATH}/`,
@@ -184,16 +186,16 @@ export async function spawnViteDevServer(
184
186
  * Create a lightweight kernel plugin that proxies `/_studio/*` requests
185
187
  * to the Vite dev server. Used in development mode.
186
188
  */
187
- export function createConsoleProxyPlugin(vitePort: number) {
189
+ export function createStudioProxyPlugin(vitePort: number) {
188
190
  return {
189
- name: 'com.objectstack.console-proxy',
191
+ name: 'com.objectstack.studio-proxy',
190
192
 
191
193
  init: async () => {},
192
194
 
193
195
  start: async (ctx: any) => {
194
196
  const httpServer = ctx.getService?.('http.server');
195
197
  if (!httpServer?.getRawApp) {
196
- ctx.logger?.warn?.('Console proxy: http.server service not found — skipping');
198
+ ctx.logger?.warn?.('Studio proxy: http.server service not found — skipping');
197
199
  return;
198
200
  }
199
201
 
@@ -234,22 +236,22 @@ export function createConsoleProxyPlugin(vitePort: number) {
234
236
  }
235
237
 
236
238
  /**
237
- * Create a lightweight kernel plugin that serves the pre-built Console
239
+ * Create a lightweight kernel plugin that serves the pre-built Studio
238
240
  * static files at `/_studio/*`. Used in production mode.
239
241
  *
240
242
  * Uses Node.js built-in fs for static file serving to avoid external
241
243
  * bundling dependencies.
242
244
  */
243
- export function createConsoleStaticPlugin(distPath: string) {
245
+ export function createStudioStaticPlugin(distPath: string, options?: { isDev?: boolean }) {
244
246
  return {
245
- name: 'com.objectstack.console-static',
247
+ name: 'com.objectstack.studio-static',
246
248
 
247
249
  init: async () => {},
248
250
 
249
251
  start: async (ctx: any) => {
250
252
  const httpServer = ctx.getService?.('http.server');
251
253
  if (!httpServer?.getRawApp) {
252
- ctx.logger?.warn?.('Console static: http.server service not found — skipping');
254
+ ctx.logger?.warn?.('Studio static: http.server service not found — skipping');
253
255
  return;
254
256
  }
255
257
 
@@ -258,10 +260,23 @@ export function createConsoleStaticPlugin(distPath: string) {
258
260
 
259
261
  const indexPath = path.join(absoluteDist, 'index.html');
260
262
  if (!fs.existsSync(indexPath)) {
261
- ctx.logger?.warn?.(`Console static: dist not found at ${absoluteDist}`);
263
+ ctx.logger?.warn?.(`Studio static: dist not found at ${absoluteDist}`);
262
264
  return;
263
265
  }
264
266
 
267
+ // Read and rewrite index.html so asset paths respect the mount path.
268
+ // The dist may have been built with base '/' (absolute paths like
269
+ // /assets/...) which won't resolve when mounted under /_studio/.
270
+ const rawHtml = fs.readFileSync(indexPath, 'utf-8');
271
+ const rewrittenHtml = rawHtml.replace(
272
+ /(\s(?:href|src))="\/(?!\/)/g,
273
+ `$1="${STUDIO_PATH}/`,
274
+ );
275
+
276
+ // In dev mode, redirect root to Studio for convenience
277
+ if (options?.isDev) {
278
+ app.get('/', (c: any) => c.redirect(`${STUDIO_PATH}/`));
279
+ }
265
280
  // Redirect bare path
266
281
  app.get(STUDIO_PATH, (c: any) => c.redirect(`${STUDIO_PATH}/`));
267
282
 
@@ -283,9 +298,8 @@ export function createConsoleStaticPlugin(distPath: string) {
283
298
  });
284
299
  }
285
300
 
286
- // SPA fallback: serve index.html for non-file routes
287
- const html = fs.readFileSync(indexPath);
288
- return new Response(html, {
301
+ // SPA fallback: serve rewritten index.html for non-file routes
302
+ return new Response(rewrittenHtml, {
289
303
  headers: { 'content-type': 'text/html; charset=utf-8' },
290
304
  });
291
305
  });
package/tsup.config.ts CHANGED
@@ -1,3 +1,5 @@
1
+ // Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
2
+
1
3
  import { defineConfig } from 'tsup';
2
4
 
3
5
  export default defineConfig([