@happyvertical/smrt-template-sveltekit 0.53.3 → 0.53.4

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 (47) hide show
  1. package/AGENTS.md +53 -26
  2. package/README.md +23 -16
  3. package/mcp-apps-template/package.dependencies.json +2 -2
  4. package/mcp-apps-template/src/routes/.well-known/oauth-protected-resource/api/mcp/+server.ts +14 -6
  5. package/mcp-apps-template/src/routes/api/mcp/+server.ts +69 -12
  6. package/package.json +17 -15
  7. package/template/AGENTS.md +16 -5
  8. package/template/Dockerfile +0 -1
  9. package/template/INSTALL_PROMPT.md +2 -2
  10. package/template/README.md +52 -27
  11. package/template/compose.yaml +2 -2
  12. package/template/package.json +28 -27
  13. package/template/smrt.config.ts +17 -4
  14. package/template/src/app.d.ts +4 -11
  15. package/template/src/hooks.server.ts +10 -51
  16. package/template/src/lib/server/smrt.ts +70 -62
  17. package/template/src/routes/+layout.server.ts +4 -12
  18. package/template/src/routes/+layout.svelte +27 -99
  19. package/template/src/routes/api/_runtime/diagnostics/+server.ts +17 -3
  20. package/template/src/routes/api/_runtime/health/+server.ts +4 -28
  21. package/template/src/routes/settings/+page.svelte +3 -34
  22. package/template/src/routes/setup/+page.server.ts +15 -88
  23. package/template/src/routes/setup/+page.svelte +3 -23
  24. package/template/vite.config.ts +5 -43
  25. package/template.config.js +11 -11
  26. package/mcp-apps-template/src/lib/McpAppsBridge.svelte +0 -37
  27. package/mcp-apps-template/src/lib/server/mcp-hosted.ts +0 -28
  28. package/mcp-apps-template/src/lib/server/mcp.ts +0 -85
  29. package/template/scripts/smrt-app.mjs +0 -885
  30. package/template/scripts/smrt-mcp-apps.mjs +0 -18
  31. package/template/scripts/smrt-operation-lock.mjs +0 -102
  32. package/template/scripts/smrt-portability-assets.mjs +0 -707
  33. package/template/scripts/smrt-portability.mjs +0 -470
  34. package/template/scripts/smrt-prepare-migration.mjs +0 -93
  35. package/template/scripts/smrt-process.mjs +0 -82
  36. package/template/scripts/smrt-provider-readiness.mjs +0 -33
  37. package/template/scripts/smrt-runtime-identity.mjs +0 -245
  38. package/template/scripts/smrt-vite.mjs +0 -45
  39. package/template/scripts/smrt-web.mjs +0 -10
  40. package/template/scripts/smrt-worker.mjs +0 -100
  41. package/template/scripts/smrt-writer-lease.mjs +0 -199
  42. package/template/src/lib/RuntimeDiagnosticsWebMcp.svelte +0 -11
  43. package/template/src/lib/runtime-diagnostics-webmcp.ts +0 -108
  44. package/template/src/lib/server/application-runtime.ts +0 -262
  45. package/template/src/lib/server/runtime-diagnostics.ts +0 -114
  46. package/template/src/lib/server/tenancy.ts +0 -114
  47. package/template/src/smrt-webmcp.d.ts +0 -6
@@ -9,25 +9,26 @@
9
9
  "pnpm": "11.25.0"
10
10
  },
11
11
  "scripts": {
12
- "dev": "node --env-file-if-exists=.env scripts/smrt-vite.mjs dev",
13
- "build": "node scripts/smrt-mcp-apps.mjs validate-if-present && node --env-file-if-exists=.env scripts/smrt-vite.mjs build",
14
- "preview": "node scripts/smrt-app.mjs start",
12
+ "dev": "smrt app dev",
13
+ "build": "smrt app build",
14
+ "preview": "smrt app start",
15
15
  "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
16
16
  "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
17
- "db:migrate": "vite build && node scripts/smrt-prepare-migration.mjs",
18
- "app:install": "node scripts/smrt-app.mjs install",
19
- "app:setup": "node scripts/smrt-app.mjs setup",
20
- "app:recover": "node scripts/smrt-app.mjs recover",
21
- "app:start": "node scripts/smrt-app.mjs start",
22
- "app:doctor": "node scripts/smrt-app.mjs doctor",
23
- "app:open": "node scripts/smrt-app.mjs open",
24
- "app:stop": "node scripts/smrt-app.mjs stop",
25
- "app:backup": "node scripts/smrt-app.mjs backup",
26
- "app:export": "node scripts/smrt-app.mjs export",
27
- "app:import": "node scripts/smrt-app.mjs import",
17
+ "typecheck": "svelte-kit sync && tsc --noEmit -p tsconfig.json && svelte-check --tsconfig ./tsconfig.json",
18
+ "db:migrate": "vite build && smrt app migrate",
19
+ "app:install": "smrt app install",
20
+ "app:setup": "smrt app setup",
21
+ "app:recover": "smrt app recover",
22
+ "app:start": "smrt app start",
23
+ "app:doctor": "smrt app doctor",
24
+ "app:open": "smrt app open",
25
+ "app:stop": "smrt app stop",
26
+ "app:backup": "smrt app backup",
27
+ "app:export": "smrt app export",
28
+ "app:import": "smrt app import",
28
29
  "mcp-apps:validate": "pnpm exec smrt mcp-apps validate --output-dir ./mcp-apps",
29
- "worker": "node scripts/smrt-worker.mjs task",
30
- "worker:schedule": "node scripts/smrt-worker.mjs schedule"
30
+ "worker": "smrt app worker task",
31
+ "worker:schedule": "smrt app worker schedule"
31
32
  },
32
33
  "devDependencies": {
33
34
  "@sveltejs/adapter-node": "^5.5.7",
@@ -38,19 +39,19 @@
38
39
  "svelte-check": "^4.7.6",
39
40
  "typescript": "6.0.3",
40
41
  "vite": "8.3.0",
41
- "@happyvertical/smrt-cli": "^0.53.3"
42
+ "@happyvertical/smrt-cli": "^0.53.4"
42
43
  },
43
44
  "dependencies": {
44
- "@happyvertical/smrt-app-runtime": "^0.53.3",
45
- "@happyvertical/smrt-config": "^0.53.3",
46
- "@happyvertical/smrt-core": "^0.53.3",
47
- "@happyvertical/smrt-jobs": "^0.53.3",
48
- "@happyvertical/smrt-profiles": "^0.53.3",
49
- "@happyvertical/smrt-svelte": "^0.53.3",
50
- "@happyvertical/smrt-tenancy": "^0.53.3",
51
- "@happyvertical/smrt-ui": "^0.53.3",
52
- "@happyvertical/smrt-users": "^0.53.3",
53
- "@happyvertical/smrt-web": "^0.53.3",
45
+ "@happyvertical/smrt-app-runtime": "^0.53.4",
46
+ "@happyvertical/smrt-config": "^0.53.4",
47
+ "@happyvertical/smrt-core": "^0.53.4",
48
+ "@happyvertical/smrt-jobs": "^0.53.4",
49
+ "@happyvertical/smrt-profiles": "^0.53.4",
50
+ "@happyvertical/smrt-svelte": "^0.53.4",
51
+ "@happyvertical/smrt-tenancy": "^0.53.4",
52
+ "@happyvertical/smrt-ui": "^0.53.4",
53
+ "@happyvertical/smrt-users": "^0.53.4",
54
+ "@happyvertical/smrt-web": "^0.53.4",
54
55
  "@happyvertical/sql": "^0.89.12",
55
56
  "@modelcontextprotocol/server": "^2.0.0"
56
57
  }
@@ -5,8 +5,10 @@
5
5
  * See: https://github.com/happyvertical/smrt
6
6
  */
7
7
 
8
- import { resolveLocalRuntimePaths } from '@happyvertical/smrt-app-runtime';
9
- import { resolveApplicationId } from './scripts/smrt-runtime-identity.mjs';
8
+ import {
9
+ resolveApplicationId,
10
+ resolveLocalRuntimePaths,
11
+ } from '@happyvertical/smrt-app-runtime';
10
12
 
11
13
  const profile = (process.env.SMRT_RUNTIME_PROFILE || 'local') as
12
14
  | 'local'
@@ -32,7 +34,18 @@ export default {
32
34
  profile,
33
35
  },
34
36
 
35
- // Agent/developer knowledge artifacts are generated by smrtPlugin().
37
+ // SMRT packages whose object manifests this app consumes. Keeping the list
38
+ // explicit avoids treating tooling/UI packages as domain model providers
39
+ // and makes generated runtime registration deterministic.
40
+ consumer: {
41
+ packages: [
42
+ '@happyvertical/smrt-profiles',
43
+ '@happyvertical/smrt-tenancy',
44
+ '@happyvertical/smrt-users',
45
+ ],
46
+ },
47
+
48
+ // Agent/developer knowledge artifacts are generated by the smrt() Vite plugin.
36
49
  // HTTP API routes stay off unless explicitly enabled for a dev/admin-only workflow.
37
50
  knowledge: {
38
51
  enabled: true,
@@ -52,7 +65,7 @@ export default {
52
65
  database: {
53
66
  type:
54
67
  profile === 'local' ? 'sqlite' : 'postgres',
55
- // `smrt-app` sets DATABASE_URL to the profile-owned data path before
68
+ // `smrt app` sets DATABASE_URL to the profile-owned data path before
56
69
  // migrations. Deployed profiles require an operator-owned URL.
57
70
  url:
58
71
  profile === 'local'
@@ -1,21 +1,14 @@
1
1
  // See https://svelte.dev/docs/kit/types#app.d.ts
2
2
  // for information about these interfaces
3
3
 
4
- import type { SessionLocals } from '@happyvertical/smrt-users/sveltekit';
4
+ import type { SmrtRuntimeLocals } from '@happyvertical/smrt-app-runtime/sveltekit';
5
5
 
6
6
  declare global {
7
7
  namespace App {
8
8
  // interface Error {}
9
- interface Locals extends SessionLocals {
10
- /**
11
- * URL-selected tenant candidate. Never use this as authorization; the
12
- * session-authorized tenant remains `tenantId` from SessionLocals.
13
- */
14
- selectedTenantId: string | null;
15
- selectedTenantSlug: string | null;
16
- /** Full context for the authorized session tenant, when present. */
17
- tenantContext?: import('@happyvertical/smrt-tenancy').MinimalTenantContext;
18
- }
9
+ // Session user, permissions, the authorized `tenantId`/`tenantContext`,
10
+ // and the URL-selected tenant candidate (never authorization).
11
+ interface Locals extends SmrtRuntimeLocals {}
19
12
  // interface PageData {}
20
13
  // interface PageState {}
21
14
  // interface Platform {}
@@ -1,56 +1,15 @@
1
1
  /**
2
- * Request foundation, in order:
2
+ * Request foundation from `@happyvertical/smrt-app-runtime/sveltekit`, in
3
+ * order: the fail-closed startup gate (`init`), URL tenant selection
4
+ * (`locals.selectedTenant*`, never authority; tenant headers are ignored), the
5
+ * signed session (the only source of tenant context and permissions), and
6
+ * `locals.tenantContext` published only for the verified session tenant.
3
7
  *
4
- * 1. Resolve a URL tenant candidate into `locals.selectedTenant*`. This is
5
- * selection only and does not enter AsyncLocalStorage tenant context.
6
- * 2. Load the signed session. `enterTenantContext: true` establishes the
7
- * authorized session tenant and its permission set for downstream code.
8
- * 3. Publish the authorized context on locals when it matches the session.
9
- *
10
- * This ordering prevents a spoofed header or hostname from becoming query
11
- * authority. Membership-gated tenant switching belongs in an explicit action
12
- * using `switchSessionTenant()` from `@happyvertical/smrt-users/sveltekit`.
8
+ * Membership-gated tenant switching belongs in an explicit action using
9
+ * `switchSessionTenant()` from `@happyvertical/smrt-users/sveltekit`.
10
+ * Configure the runtime in `$lib/server/smrt`.
13
11
  */
14
12
 
15
- import { getCurrentTenant, enableTenancy } from '@happyvertical/smrt-tenancy';
16
- import { createSessionHandler } from '@happyvertical/smrt-users/sveltekit';
17
- import type { Handle, ServerInit } from '@sveltejs/kit';
18
- import { sequence } from '@sveltejs/kit/hooks';
19
-
20
- import { getSmrtConfig } from '$lib/server/smrt';
21
- import { ensureApplicationRuntimeReady } from '$lib/server/application-runtime';
22
- import { resolveTenant } from '$lib/server/tenancy';
23
-
24
- enableTenancy();
25
-
26
- export const init: ServerInit = ensureApplicationRuntimeReady;
27
-
28
- const tenantSelectionHandle: Handle = async ({ event, resolve }) => {
29
- const selection = await resolveTenant(event);
30
- event.locals.selectedTenantId = selection.tenantId;
31
- event.locals.selectedTenantSlug = selection.tenantSlug;
32
- return resolve(event);
33
- };
34
-
35
- const sessionHandle = createSessionHandler({
36
- ...getSmrtConfig('Session'),
37
- enterTenantContext: true,
38
- }) as unknown as Handle;
39
-
40
- const authorizedTenantLocalsHandle: Handle = async ({ event, resolve }) => {
41
- const activeContext = getCurrentTenant();
42
- if (
43
- event.locals.user &&
44
- event.locals.tenantId &&
45
- activeContext?.tenantId === event.locals.tenantId
46
- ) {
47
- event.locals.tenantContext = activeContext;
48
- }
49
- return resolve(event);
50
- };
13
+ import { runtime } from '$lib/server/smrt';
51
14
 
52
- export const handle: Handle = sequence(
53
- tenantSelectionHandle,
54
- sessionHandle,
55
- authorizedTenantLocalsHandle,
56
- );
15
+ export const { handle, init } = runtime;
@@ -1,103 +1,111 @@
1
1
  /**
2
- * Centralized SMRT Configuration
2
+ * The application's SMRT runtime and collection access.
3
3
  *
4
- * This file provides configuration for all SMRT objects in your project.
5
- * Import this file in your routes to get properly configured collections.
4
+ * `runtime` composes the configured profile (local SQLite or deployed
5
+ * PostgreSQL), storage custody, the owner bootstrap, tenant selection, and the
6
+ * signed session; `src/hooks.server.ts` mounts its `handle` and `init`.
7
+ * Generated API routes import `getCollection` from this file.
6
8
  */
7
9
 
8
10
  import { existsSync } from 'node:fs';
9
11
  import { join } from 'node:path';
10
12
 
13
+ import {
14
+ acquireWriterLease,
15
+ createProviderReadinessProbe,
16
+ prepareApplicationStateRoot,
17
+ } from '@happyvertical/smrt-app-runtime';
18
+ import { createSmrtSvelteKitRuntime } from '@happyvertical/smrt-app-runtime/sveltekit';
11
19
  import {
12
20
  ObjectRegistry,
13
21
  type SmrtClassOptions,
14
22
  type SmrtObject,
15
23
  } from '@happyvertical/smrt-core';
16
24
  import { loadManifestFromPathSync } from '@happyvertical/smrt-core/manifest';
17
- import { getRequestScopedDatabase } from '@happyvertical/smrt-users';
18
-
19
- import { getApplicationDatabaseConfig } from './application-runtime.js';
20
25
 
21
26
  // Import all SMRT objects to register them.
22
27
  import '../objects/index.js';
23
28
 
29
+ /**
30
+ * Whether `error` reports that the generated register module itself is
31
+ * absent. Only the missing module named by the error counts (Node's `url`,
32
+ * or `Cannot find module '<path>'` from Node and Vite's module runner); a
33
+ * package or file that an existing generated module imports is a different
34
+ * missing module and is never matched.
35
+ */
36
+ function isMissingRegisterModule(error: unknown): boolean {
37
+ const { code, url, message } = (error ?? {}) as {
38
+ code?: unknown;
39
+ url?: unknown;
40
+ message?: unknown;
41
+ };
42
+ if (code !== 'ERR_MODULE_NOT_FOUND') return false;
43
+ const missing =
44
+ typeof url === 'string'
45
+ ? url
46
+ : typeof message === 'string'
47
+ ? /^Cannot find module '([^']+)'/.exec(message)?.[1]
48
+ : undefined;
49
+ if (!missing) return false;
50
+ const path = missing.replace(/^file:\/\//, '').replace(/[?#].*$/, '');
51
+ return /\/lib\/server\/smrt-register\.(?:js|ts)$/.test(path);
52
+ }
53
+
24
54
  // Upgrade those registrations to package-qualified identities
25
55
  // (`<package>:<Class>`) — required to link each class to its scanned field
26
- // metadata below. smrtPlugin() generates this module on every dev/build run
27
- // (it is gitignored, like the generated API routes); guard the import so a
28
- // fresh checkout that has not run the plugin yet still boots.
56
+ // metadata below. The `smrt()` Vite plugin generates this module on every
57
+ // dev/build run (it is gitignored, like the generated API routes); guard the
58
+ // import so a fresh checkout that has not run the plugin yet still boots.
29
59
  try {
30
- // @ts-ignore generated by smrtPlugin before a consumer build starts
60
+ // @ts-ignore generated by the smrt() Vite plugin before a build starts
31
61
  await import('./smrt-register.js');
32
62
  } catch (error) {
33
- // Not generated yet — the first `vite dev` / `vite build` creates it. Do
34
- // not hide a real import/runtime error from an existing generated module.
35
- const code = (error as NodeJS.ErrnoException).code;
36
- const message = error instanceof Error ? error.message : '';
37
- if (
38
- code !== 'ERR_MODULE_NOT_FOUND' &&
39
- !message.includes('smrt-register.js')
40
- ) {
41
- throw error;
42
- }
63
+ // Not generated yet — the first `pnpm dev` / `pnpm build` creates it. Any
64
+ // other failure, including a missing dependency of an existing generated
65
+ // module (an uninstalled extension package), must fail startup.
66
+ if (!isMissingRegisterModule(error)) throw error;
43
67
  }
44
68
 
45
69
  // Hydrate field metadata for this app's own objects from the local manifest
46
- // written by smrtPlugin() (`.smrt/manifest.json`). The decorator import above
47
- // registers the classes, but their scanned field metadata (plain properties
48
- // like `title: string = ''`) lives in the manifest — without seeding it, the
49
- // runtime only knows the framework base fields and server-side writes would
50
- // silently drop your domain columns.
70
+ // (`.smrt/manifest.json`). Without it the runtime only knows the framework
71
+ // base fields and server-side writes would silently drop your domain columns.
51
72
  const localManifestPath = join(process.cwd(), '.smrt', 'manifest.json');
52
73
  if (existsSync(localManifestPath)) {
53
74
  loadManifestFromPathSync(localManifestPath);
54
75
  }
55
76
 
56
- /**
57
- * Per-object configuration overrides
58
- *
59
- * Use this to configure specific objects differently from defaults.
60
- * Example:
61
- * AuditLog: { db: { url: process.env.AUDIT_DB_URL!, type: 'postgres' } }
62
- */
63
- const objectOverrides: Record<string, Partial<SmrtClassOptions>> = {
64
- // Add object-specific overrides here
65
- };
66
-
67
- /**
68
- * Get default configuration for SMRT objects
69
- */
70
- function getDefaultConfig(): SmrtClassOptions {
71
- return {
72
- db: getApplicationDatabaseConfig(),
73
- };
74
- }
77
+ export const runtime = createSmrtSvelteKitRuntime({
78
+ // Every local web writer (`pnpm dev`, `pnpm app:start`) holds the same
79
+ // single-writer lease as `smrt app` operations, so imports and backups
80
+ // fail closed while the app is running.
81
+ acquireWriterLease: (context) =>
82
+ acquireWriterLease(prepareApplicationStateRoot(context), {
83
+ operationInstance: process.env.SMRT_OPERATION_INSTANCE,
84
+ }),
85
+ // Deployed profiles prove authentication, asset, and secret readiness
86
+ // through the installed modules named by SMRT_*_READINESS_MODULE.
87
+ providerReadiness: createProviderReadinessProbe,
88
+ // Per-object overrides, for example:
89
+ // AuditLog: { db: { type: 'postgres', url: process.env.AUDIT_DB_URL! } }
90
+ classOverrides: {},
91
+ });
75
92
 
76
93
  /**
77
- * Get configuration for a specific SMRT class
94
+ * Collection options for a class. Call per request and never keep the result:
95
+ * under `database-rls` isolation it carries the request's transaction.
78
96
  */
79
97
  export function getSmrtConfig(className: string): SmrtClassOptions {
80
- const defaults = getDefaultConfig();
81
- const override = objectOverrides[className];
82
- return override ? { ...defaults, ...override } : defaults;
98
+ return runtime.classOptions(className);
83
99
  }
84
100
 
85
101
  /**
86
- * Get a collection instance for a SMRT class
102
+ * Get a collection for a SMRT class, scoped to the current request.
87
103
  *
88
- * Usage in routes:
89
- * const products = await getCollection<Product>('Product');
90
- * const items = await products.list();
104
+ * const items = await getCollection<Item>('Item');
91
105
  */
92
106
  export async function getCollection<T extends SmrtObject>(className: string) {
93
- const config = getSmrtConfig(className);
94
- const objectOverride = objectOverrides[className];
95
- const requestScopedDb = objectOverride?.db
96
- ? undefined
97
- : (getRequestScopedDatabase() as SmrtClassOptions['db'] | undefined);
98
-
99
- return await ObjectRegistry.getCollection<T>(className, {
100
- ...config,
101
- db: requestScopedDb ?? config.db,
102
- });
107
+ return await ObjectRegistry.getCollection<T>(
108
+ className,
109
+ runtime.classOptions(className),
110
+ );
103
111
  }
@@ -1,15 +1,7 @@
1
- import type { LayoutServerLoad } from './$types';
1
+ import { createSessionLayoutLoad } from '@happyvertical/smrt-app-runtime/sveltekit';
2
2
 
3
3
  /**
4
- * Keep session and tenant state server-owned. The selected tenant is displayed
5
- * separately from the tenant authorized by the active session.
4
+ * Server-owned session summary: whether a user is signed in, the tenant the
5
+ * session authorizes, and (display only) the URL-selected tenant candidate.
6
6
  */
7
- export const load: LayoutServerLoad = async ({ locals }) => {
8
- return {
9
- session: {
10
- authenticated: Boolean(locals.user),
11
- activeTenantId: locals.tenantId,
12
- selectedTenantSlug: locals.selectedTenantSlug,
13
- },
14
- };
15
- };
7
+ export const load = createSessionLayoutLoad();
@@ -1,113 +1,41 @@
1
1
  <script lang="ts">
2
2
  import { page } from '$app/state';
3
3
  import { webMcpToolDefinitions } from '@happyvertical/smrt-virt-web';
4
- import { Provider } from '@happyvertical/smrt-svelte';
5
- import {
6
- AdminShell,
7
- AppScopePanel,
8
- type ShellNavItem,
9
- TenantNav,
10
- } from '@happyvertical/smrt-svelte/workspace';
11
- import { ThemeProvider } from '@happyvertical/smrt-ui/themes';
12
- import '@happyvertical/smrt-ui/themes/styles/fonts.css';
13
- import RuntimeDiagnosticsWebMcp from '$lib/RuntimeDiagnosticsWebMcp.svelte';
4
+ import { AppShell } from '@happyvertical/smrt-svelte/app';
5
+ import type { ShellNavItem } from '@happyvertical/smrt-svelte/workspace';
14
6
  import type { LayoutProps } from './$types';
15
7
 
16
8
  let { data, children }: LayoutProps = $props();
17
9
 
18
- const currentHref = $derived(page.url.pathname);
19
- const activeTenantLabel = $derived(
20
- data.session.activeTenantId ? 'Authorized session tenant' : 'No active tenant',
21
- );
10
+ // Add application routes here. Generated REST routes live under /api and do
11
+ // not automatically imply a human-facing page.
12
+ const nav: ShellNavItem[] = [
13
+ { href: '/', label: 'Items', description: 'The example s-m-r-t object' },
14
+ { href: '/settings', label: 'Settings', description: 'Workspace layout and shortcuts' },
15
+ ];
22
16
 
23
- // Keep the optional data-plane runtime out of browsers that do not expose
24
- // WebMCP. SSR remains safe because the feature check is document-guarded.
17
+ // Generated read-only WebMCP tools, only in browsers that expose WebMCP.
25
18
  const webmcp = $derived(
26
19
  typeof document !== 'undefined' && 'modelContext' in document
27
- ? {
28
- definitions: webMcpToolDefinitions,
29
- basePath: '/api',
30
- effects: ['read'] as const,
31
- }
20
+ ? { definitions: webMcpToolDefinitions, basePath: '/api', effects: ['read'] as const }
32
21
  : false,
33
22
  );
34
-
35
- // Add application routes here. Generated REST routes live under /api and do
36
- // not automatically imply a human-facing page.
37
- const nav: ShellNavItem[] = [
38
- {
39
- href: '/',
40
- label: 'Items',
41
- description: 'The example s-m-r-t object',
42
- },
43
- {
44
- href: '/settings',
45
- label: 'Settings',
46
- description: 'Workspace layout and shortcuts',
47
- },
48
- ];
49
23
  </script>
50
24
 
51
- <Provider {webmcp}>
52
- <RuntimeDiagnosticsWebMcp enabled={data.session.authenticated} />
53
- <ThemeProvider preset="smrt" colorScheme="system" persist={true}>
54
- <AdminShell
55
- title="s-m-r-t app"
56
- subtitle="SvelteKit"
57
- storageKey="smrt-app-shell"
58
- >
59
- {#snippet appPanel()}
60
- <AppScopePanel
61
- appName="s-m-r-t app"
62
- tenantName={activeTenantLabel}
63
- environment="local"
64
- showSettings={false}
65
- >
66
- {#snippet docs()}
67
- {#if data.session.selectedTenantSlug}
68
- <p class="tenant-selection">
69
- Selected URL tenant: <strong>{data.session.selectedTenantSlug}</strong>
70
- </p>
71
- {/if}
72
- <a href="/settings">Shell settings</a>
73
- {/snippet}
74
- </AppScopePanel>
75
- {/snippet}
76
-
77
- {#snippet tenantPanel()}
78
- <TenantNav items={nav} {currentHref} />
79
- {/snippet}
80
-
81
- {@render children()}
82
- </AdminShell>
83
- </ThemeProvider>
84
- </Provider>
85
-
86
- <style>
87
- :global(*),
88
- :global(*::before),
89
- :global(*::after) {
90
- box-sizing: border-box;
91
- }
92
-
93
- :global(html),
94
- :global(body) {
95
- min-height: 100%;
96
- margin: 0;
97
- }
98
-
99
- :global(body) {
100
- background: var(--smrt-color-background);
101
- color: var(--smrt-color-on-background);
102
- font-family: var(--smrt-typography-body-font-family, Inter, system-ui, sans-serif);
103
- }
104
-
105
- .tenant-selection {
106
- margin: 0 0 var(--smrt-spacing-2);
107
- color: var(--smrt-color-on-surface-variant);
108
- }
109
-
110
- a {
111
- color: var(--smrt-color-primary);
112
- }
113
- </style>
25
+ <AppShell
26
+ title="s-m-r-t app"
27
+ subtitle="SvelteKit"
28
+ {webmcp}
29
+ {nav}
30
+ currentHref={page.url.pathname}
31
+ runtimeDiagnostics={data.session.authenticated}
32
+ tenantLabel={data.session.activeTenantId ? 'Authorized session tenant' : 'No active tenant'}
33
+ settingsHref="/settings"
34
+ >
35
+ {#snippet appPanelDocs()}
36
+ {#if data.session.selectedTenantSlug}
37
+ <p>Selected URL tenant: <strong>{data.session.selectedTenantSlug}</strong></p>
38
+ {/if}
39
+ {/snippet}
40
+ {@render children()}
41
+ </AppShell>
@@ -1,5 +1,19 @@
1
- import type { RequestHandler } from './$types';
1
+ import { createRuntimeDiagnosticsHandler } from '@happyvertical/smrt-app-runtime/sveltekit';
2
+ import { RUNTIME_DIAGNOSTICS_WEBMCP_TOOL_NAME } from '@happyvertical/smrt-svelte/app/runtime-diagnostics';
3
+ import { webMcpToolDefinitions } from '@happyvertical/smrt-virt-web';
2
4
 
3
- import { runtimeDiagnosticsGet } from '$lib/server/runtime-diagnostics';
5
+ import { runtime } from '$lib/server/smrt';
4
6
 
5
- export const GET: RequestHandler = runtimeDiagnosticsGet;
7
+ /**
8
+ * Authenticated, authorized (owner role or `runtime_diagnostics.read`) and
9
+ * redacted runtime diagnostics. Schema/migration status and the worker
10
+ * heartbeat stay `unknown` until the application installs real verifiers;
11
+ * process liveness is never substituted for them.
12
+ */
13
+ export const GET = createRuntimeDiagnosticsHandler({
14
+ runtime,
15
+ toolNames: () => [
16
+ ...webMcpToolDefinitions.map((definition) => definition.name),
17
+ RUNTIME_DIAGNOSTICS_WEBMCP_TOOL_NAME,
18
+ ],
19
+ });
@@ -1,30 +1,6 @@
1
- import { json } from '@sveltejs/kit';
2
- import type { RequestHandler } from './$types';
3
- import { resolveApplicationId } from '../../../../../scripts/smrt-runtime-identity.mjs';
4
- import {
5
- applicationRuntime,
6
- applicationRuntimeConfiguration,
7
- } from '$lib/server/application-runtime';
1
+ import { createRuntimeHealthHandler } from '@happyvertical/smrt-app-runtime/sveltekit';
8
2
 
9
- const applicationId = resolveApplicationId({
10
- sourceRoot: process.cwd(),
11
- explicitId: process.env.SMRT_APP_ID,
12
- });
3
+ import { runtime } from '$lib/server/smrt';
13
4
 
14
- export const GET: RequestHandler = async () => {
15
- const publicHealth = {
16
- schemaVersion: 1,
17
- status: 'ready',
18
- profile: applicationRuntime.profile,
19
- };
20
- return json(
21
- applicationRuntime.profile === 'local'
22
- ? {
23
- ...publicHealth,
24
- application: applicationId,
25
- instance: process.env.SMRT_PROCESS_INSTANCE || null,
26
- configuration: applicationRuntimeConfiguration,
27
- }
28
- : publicHealth,
29
- );
30
- };
5
+ /** Liveness; local responses add the identity `smrt app start` verifies. */
6
+ export const GET = createRuntimeHealthHandler(runtime);
@@ -1,41 +1,10 @@
1
1
  <script lang="ts">
2
- import { ShellSettingsPanel } from '@happyvertical/smrt-svelte/workspace';
3
-
4
- // This page renders inside the root layout's <AdminShell>, so the shell
5
- // context set by AdminShell is available to descendants — ShellSettingsPanel
6
- // reads it via useAdminShell() with no extra wiring. It lets users toggle
7
- // panel visibility and remap the WASD hotkeys; changes persist through the
8
- // shell's `storageKey` (localStorage), applied client-side after mount.
2
+ import { ShellSettingsPage } from '@happyvertical/smrt-svelte/app';
9
3
  </script>
10
4
 
11
5
  <svelte:head>
12
6
  <title>Settings · s-m-r-t app</title>
13
7
  </svelte:head>
14
8
 
15
- <section class="settings">
16
- <header>
17
- <h1>Settings</h1>
18
- <p>Adjust the workspace shell — panel layout and keyboard shortcuts.</p>
19
- </header>
20
-
21
- <ShellSettingsPanel />
22
- </section>
23
-
24
- <style>
25
- .settings {
26
- display: grid;
27
- gap: var(--smrt-spacing-6, 1.5rem);
28
- max-width: 720px;
29
- padding: var(--smrt-spacing-8, 2rem);
30
- }
31
-
32
- .settings header {
33
- display: grid;
34
- gap: var(--smrt-spacing-2, 0.5rem);
35
- }
36
-
37
- .settings h1,
38
- .settings p {
39
- margin: 0;
40
- }
41
- </style>
9
+ <!-- Panel layout and WASD hotkeys, persisted through the shell's storageKey. -->
10
+ <ShellSettingsPage />