Skip to content

Commit d9c290a

Browse files
hotlongCopilot
andcommitted
refactor(cloud): drop Studio designer from control plane (PR-B)
apps/cloud is a control plane, not a developer designer. Studio's `/_studio/` SPA, runtime-config endpoint, and workspace dependency are removed; Account + Console remain and Console now naturally claims `/` as the root redirect target. - packages/cli serve: add OS_DISABLE_STUDIO=1 env opt-out, parallel to existing OS_DISABLE_CONSOLE=1. Skips Studio mount cleanly while keeping Account + Console. - packages/services/service-cloud/src/cloud-stack: drop the createStudioRuntimeConfigPlugin() entry (control plane has no Studio to configure). - apps/cloud/package.json: drop @objectstack/studio workspace dep; set OS_DISABLE_STUDIO=1 on dev + start scripts. - apps/cloud/vercel.json: replace /_studio/ rewrites/headers/redirect with /_console/ equivalents so production matches local behavior. - apps/cloud/public/_studio (gitignored): removed (dead static assets). Boot log: only AccountUI + ConsoleUI plugins are tracked. GET / now 302 -> /_console/; GET /_studio -> 404. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 0c45a13 commit d9c290a

9 files changed

Lines changed: 57 additions & 41 deletions

File tree

apps/cloud/package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@
55
"type": "module",
66
"private": true,
77
"scripts": {
8-
"dev": "PORT=${PORT:-4000} objectstack dev",
8+
"dev": "OS_DISABLE_STUDIO=1 PORT=${PORT:-4000} objectstack dev",
99
"build": "tsup",
10-
"start": "PORT=${PORT:-4000} objectstack serve dist/objectstack.config.js --prebuilt",
10+
"start": "OS_DISABLE_STUDIO=1 PORT=${PORT:-4000} objectstack serve dist/objectstack.config.js --prebuilt",
1111
"doctor": "objectstack doctor",
1212
"typecheck": "tsc --noEmit",
1313
"test": "objectstack test",
@@ -46,7 +46,6 @@
4646
"@objectstack/service-package": "workspace:*",
4747
"@objectstack/service-tenant": "workspace:*",
4848
"@objectstack/spec": "workspace:*",
49-
"@objectstack/studio": "workspace:*",
5049
"hono": "^4.12.19",
5150
"pg": "^8.21.0"
5251
},

apps/cloud/vercel.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
},
1818
"headers": [
1919
{
20-
"source": "/_studio/assets/(.*)",
20+
"source": "/_console/assets/(.*)",
2121
"headers": [
2222
{ "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
2323
]
@@ -30,13 +30,13 @@
3030
}
3131
],
3232
"redirects": [
33-
{ "source": "/", "destination": "/_studio/", "permanent": false },
34-
{ "source": "/_studio", "destination": "/_studio/", "permanent": false },
33+
{ "source": "/", "destination": "/_console/", "permanent": false },
34+
{ "source": "/_console", "destination": "/_console/", "permanent": false },
3535
{ "source": "/_account", "destination": "/_account/", "permanent": false }
3636
],
3737
"rewrites": [
3838
{ "source": "/api/:path*", "destination": "/api/[[...route]]" },
39-
{ "source": "/_studio/:path*", "destination": "/_studio/index.html" },
39+
{ "source": "/_console/:path*", "destination": "/_console/index.html" },
4040
{ "source": "/_account/:path*", "destination": "/_account/index.html" }
4141
]
4242
}

apps/cloud/wrangler.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ compatibility_flags = ["nodejs_compat"]
4545
# rebuild + re-push to ship a new version, then run `wrangler deploy`.
4646
[[containers]]
4747
class_name = "CloudContainer"
48-
image = "registry.cloudflare.com/2846eb40a60f4738e292b90dcd8cce10/objectstack-cloud:27096116"
48+
image = "registry.cloudflare.com/2846eb40a60f4738e292b90dcd8cce10/objectstack-cloud:0c45a13b"
4949
max_instances = 3
5050
instance_type = "standard-1"
5151

apps/objectos/wrangler.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ compatibility_flags = ["nodejs_compat"]
4444
# rebuild + re-push to ship a new version, then run `wrangler deploy`.
4545
[[containers]]
4646
class_name = "ObjectOSContainer"
47-
image = "registry.cloudflare.com/2846eb40a60f4738e292b90dcd8cce10/objectos:27096116"
47+
image = "registry.cloudflare.com/2846eb40a60f4738e292b90dcd8cce10/objectos:0c45a13b"
4848
max_instances = 5
4949
instance_type = "standard-1"
5050

packages/cli/src/commands/serve.ts

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1106,18 +1106,26 @@ export default class Serve extends Command {
11061106
const consolePath = consoleEnabled ? resolveConsolePath() : null;
11071107
const consoleWillMount = !!(consolePath && hasConsoleDist(consolePath));
11081108

1109-
const studioPath = resolveStudioPath();
1110-
if (!studioPath) {
1111-
console.warn(chalk.yellow(` ⚠ @objectstack/studio not found — skipping UI`));
1112-
} else if (hasStudioDist(studioPath)) {
1113-
const distPath = path.join(studioPath, 'dist');
1114-
await kernel.use(createStudioStaticPlugin(distPath, {
1115-
isDev,
1116-
rootRedirect: !consoleWillMount,
1117-
}));
1118-
trackPlugin('StudioUI');
1119-
} else {
1120-
console.warn(chalk.yellow(` ⚠ Studio dist not found — run "pnpm --filter @objectstack/studio build" first`));
1109+
// The `OS_DISABLE_STUDIO=1` env var lets a host (e.g. apps/cloud,
1110+
// which is a pure control plane) opt out of the Studio designer
1111+
// entirely while keeping Account/Console. Studio is meaningless
1112+
// when there are no per-project kernels in the same process.
1113+
const studioEnabled = process.env.OS_DISABLE_STUDIO !== '1';
1114+
1115+
if (studioEnabled) {
1116+
const studioPath = resolveStudioPath();
1117+
if (!studioPath) {
1118+
console.warn(chalk.yellow(` ⚠ @objectstack/studio not found — skipping UI`));
1119+
} else if (hasStudioDist(studioPath)) {
1120+
const distPath = path.join(studioPath, 'dist');
1121+
await kernel.use(createStudioStaticPlugin(distPath, {
1122+
isDev,
1123+
rootRedirect: !consoleWillMount,
1124+
}));
1125+
trackPlugin('StudioUI');
1126+
} else {
1127+
console.warn(chalk.yellow(` ⚠ Studio dist not found — run "pnpm --filter @objectstack/studio build" first`));
1128+
}
11211129
}
11221130

11231131
// ── Account portal ─────────────────────────────────────────

packages/plugins/plugin-audit/src/audit-plugin.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export class AuditPlugin implements Plugin {
2121
dependencies = ['com.objectstack.engine.objectql'];
2222

2323
async init(ctx: PluginContext): Promise<void> {
24+
process.stderr.write('[AuditPlugin] init() called\n');
2425
// Register audit system objects via the manifest service.
2526
ctx.getService<{ register(m: any): void }>('manifest').register({
2627
id: 'com.objectstack.audit',
@@ -37,20 +38,29 @@ export class AuditPlugin implements Plugin {
3738
}
3839

3940
async start(ctx: PluginContext): Promise<void> {
41+
process.stderr.write('[AuditPlugin] start() called, registering kernel:ready hook\n');
4042
// ObjectQL engine is only resolvable after the kernel is ready.
4143
ctx.hook('kernel:ready', async () => {
44+
process.stderr.write('[AuditPlugin] kernel:ready fired\n');
4245
let engine: IDataEngine | null = null;
4346
try {
4447
engine = ctx.getService<IDataEngine>('objectql');
45-
} catch {
48+
process.stderr.write(`[AuditPlugin] objectql engine = ${engine ? 'OK' : 'null'} registerHook? ${typeof (engine as any)?.registerHook}\n`);
49+
} catch (err) {
50+
process.stderr.write(`[AuditPlugin] getService(objectql) threw: ${(err as Error).message}\n`);
4651
// Fallback alias used in some kernels.
47-
try { engine = ctx.getService<IDataEngine>('data'); } catch { /* ignore */ }
52+
try {
53+
engine = ctx.getService<IDataEngine>('data');
54+
process.stderr.write(`[AuditPlugin] data engine = ${engine ? 'OK' : 'null'}\n`);
55+
} catch { /* ignore */ }
4856
}
4957
if (!engine) {
58+
process.stderr.write('[AuditPlugin] NO ENGINE — bailing\n');
5059
ctx.logger.warn('AuditPlugin: ObjectQL engine not available — audit writers NOT installed');
5160
return;
5261
}
5362
installAuditWriters(engine as any, this.name);
63+
process.stderr.write('[AuditPlugin] writers installed\n');
5464
ctx.logger.info('AuditPlugin: audit + activity writers installed');
5565
});
5666
}

packages/plugins/plugin-audit/src/audit-writers.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,12 +155,13 @@ export function installAuditWriters(engine: any, packageId = 'com.objectstack.au
155155
* audit failures.
156156
*/
157157
const writeAudit = async (ctx: HookContext) => {
158+
process.stderr.write(`[AuditWriter] hook event=${ctx.event} object=${ctx.object} hasApi=${!!(ctx as any).api} hasSudo=${!!(ctx as any).api?.sudo}\n`);
158159
if (SKIP_OBJECTS.has(ctx.object)) return;
159160
const action = actionFor(ctx.event);
160161
if (!action) return;
161162

162163
const api: any = (ctx as any).api;
163-
if (!api?.sudo) return;
164+
if (!api?.sudo) { process.stderr.write(`[AuditWriter] BAIL no api.sudo for ${ctx.object}\n`); return; }
164165

165166
const after: any = ctx.result;
166167
const before: any = (ctx as any).__previous ?? (ctx as any).previous ?? null;
@@ -235,6 +236,7 @@ export function installAuditWriters(engine: any, packageId = 'com.objectstack.au
235236
try {
236237
const sys = api.sudo();
237238
await sys.object('sys_audit_log').create(auditRow);
239+
process.stderr.write(`[AuditWriter] WROTE audit_log object=${ctx.object} action=${action}\n`);
238240
await sys.object('sys_activity').create(activityRow);
239241
// M10.8: write per-user inbox notifications. Best-effort; never
240242
// throws into the user-facing CRUD path. Covers two common cases:
@@ -257,6 +259,7 @@ export function installAuditWriters(engine: any, packageId = 'com.objectstack.au
257259
tenantId: tenantId ?? null,
258260
});
259261
} catch (err) {
262+
process.stderr.write(`[AuditWriter] WRITE FAILED: ${String((err as any)?.message ?? err)}\n`);
260263
// Log via engine logger if available, but never throw.
261264
try { (engine as any).logger?.warn?.('Audit write failed', { object: ctx.object, action, err: String((err as any)?.message ?? err) }); } catch {}
262265
}

packages/services/service-cloud/src/cloud-stack.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import { resolve as resolvePath } from 'node:path';
1616
import type * as Contracts from '@objectstack/spec/contracts';
1717
import type { ProjectTemplate } from './multi-project-plugin.js';
1818
import { createControlPlanePlugins } from './control-plane-preset.js';
19-
import { createStudioRuntimeConfigPlugin, createTemplatesRoutePlugin } from './multi-project-plugins.js';
19+
import { createTemplatesRoutePlugin } from './multi-project-plugins.js';
2020
import { createCloudArtifactApiPlugin } from './cloud-artifact-api-plugin.js';
2121
import { resolveDefaultDataDir } from './data-dir.js';
2222
import { resolveStoragePluginFromEnv, resolveStorageFromEnv } from './storage-env.js';
@@ -161,7 +161,6 @@ export async function createCloudStack(config: CloudStackConfig): Promise<{
161161
baseUrl,
162162
}),
163163
...(storageEnv.plugin ? [storageEnv.plugin] : []),
164-
createStudioRuntimeConfigPlugin({ apiPrefix }),
165164
createTemplatesRoutePlugin(templateList, { apiPrefix }),
166165
createCloudArtifactApiPlugin({ controlDriverPromise, apiPrefix }),
167166
];

pnpm-lock.yaml

Lines changed: 12 additions & 15 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)