@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.
- package/AGENTS.md +53 -26
- package/README.md +23 -16
- package/mcp-apps-template/package.dependencies.json +2 -2
- package/mcp-apps-template/src/routes/.well-known/oauth-protected-resource/api/mcp/+server.ts +14 -6
- package/mcp-apps-template/src/routes/api/mcp/+server.ts +69 -12
- package/package.json +17 -15
- package/template/AGENTS.md +16 -5
- package/template/Dockerfile +0 -1
- package/template/INSTALL_PROMPT.md +2 -2
- package/template/README.md +52 -27
- package/template/compose.yaml +2 -2
- package/template/package.json +28 -27
- package/template/smrt.config.ts +17 -4
- package/template/src/app.d.ts +4 -11
- package/template/src/hooks.server.ts +10 -51
- package/template/src/lib/server/smrt.ts +70 -62
- package/template/src/routes/+layout.server.ts +4 -12
- package/template/src/routes/+layout.svelte +27 -99
- package/template/src/routes/api/_runtime/diagnostics/+server.ts +17 -3
- package/template/src/routes/api/_runtime/health/+server.ts +4 -28
- package/template/src/routes/settings/+page.svelte +3 -34
- package/template/src/routes/setup/+page.server.ts +15 -88
- package/template/src/routes/setup/+page.svelte +3 -23
- package/template/vite.config.ts +5 -43
- package/template.config.js +11 -11
- package/mcp-apps-template/src/lib/McpAppsBridge.svelte +0 -37
- package/mcp-apps-template/src/lib/server/mcp-hosted.ts +0 -28
- package/mcp-apps-template/src/lib/server/mcp.ts +0 -85
- package/template/scripts/smrt-app.mjs +0 -885
- package/template/scripts/smrt-mcp-apps.mjs +0 -18
- package/template/scripts/smrt-operation-lock.mjs +0 -102
- package/template/scripts/smrt-portability-assets.mjs +0 -707
- package/template/scripts/smrt-portability.mjs +0 -470
- package/template/scripts/smrt-prepare-migration.mjs +0 -93
- package/template/scripts/smrt-process.mjs +0 -82
- package/template/scripts/smrt-provider-readiness.mjs +0 -33
- package/template/scripts/smrt-runtime-identity.mjs +0 -245
- package/template/scripts/smrt-vite.mjs +0 -45
- package/template/scripts/smrt-web.mjs +0 -10
- package/template/scripts/smrt-worker.mjs +0 -100
- package/template/scripts/smrt-writer-lease.mjs +0 -199
- package/template/src/lib/RuntimeDiagnosticsWebMcp.svelte +0 -11
- package/template/src/lib/runtime-diagnostics-webmcp.ts +0 -108
- package/template/src/lib/server/application-runtime.ts +0 -262
- package/template/src/lib/server/runtime-diagnostics.ts +0 -114
- package/template/src/lib/server/tenancy.ts +0 -114
- package/template/src/smrt-webmcp.d.ts +0 -6
package/template/package.json
CHANGED
|
@@ -9,25 +9,26 @@
|
|
|
9
9
|
"pnpm": "11.25.0"
|
|
10
10
|
},
|
|
11
11
|
"scripts": {
|
|
12
|
-
"dev": "
|
|
13
|
-
"build": "
|
|
14
|
-
"preview": "
|
|
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
|
-
"
|
|
18
|
-
"
|
|
19
|
-
"app:
|
|
20
|
-
"app:
|
|
21
|
-
"app:
|
|
22
|
-
"app:
|
|
23
|
-
"app:
|
|
24
|
-
"app:
|
|
25
|
-
"app:
|
|
26
|
-
"app:
|
|
27
|
-
"app:
|
|
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": "
|
|
30
|
-
"worker: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.
|
|
42
|
+
"@happyvertical/smrt-cli": "^0.53.4"
|
|
42
43
|
},
|
|
43
44
|
"dependencies": {
|
|
44
|
-
"@happyvertical/smrt-app-runtime": "^0.53.
|
|
45
|
-
"@happyvertical/smrt-config": "^0.53.
|
|
46
|
-
"@happyvertical/smrt-core": "^0.53.
|
|
47
|
-
"@happyvertical/smrt-jobs": "^0.53.
|
|
48
|
-
"@happyvertical/smrt-profiles": "^0.53.
|
|
49
|
-
"@happyvertical/smrt-svelte": "^0.53.
|
|
50
|
-
"@happyvertical/smrt-tenancy": "^0.53.
|
|
51
|
-
"@happyvertical/smrt-ui": "^0.53.
|
|
52
|
-
"@happyvertical/smrt-users": "^0.53.
|
|
53
|
-
"@happyvertical/smrt-web": "^0.53.
|
|
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
|
}
|
package/template/smrt.config.ts
CHANGED
|
@@ -5,8 +5,10 @@
|
|
|
5
5
|
* See: https://github.com/happyvertical/smrt
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
|
|
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
|
-
//
|
|
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
|
|
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'
|
package/template/src/app.d.ts
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
|
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
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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 {
|
|
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
|
|
53
|
-
tenantSelectionHandle,
|
|
54
|
-
sessionHandle,
|
|
55
|
-
authorizedTenantLocalsHandle,
|
|
56
|
-
);
|
|
15
|
+
export const { handle, init } = runtime;
|
|
@@ -1,103 +1,111 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* The application's SMRT runtime and collection access.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
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.
|
|
27
|
-
// (it is gitignored, like the generated API routes); guard the
|
|
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
|
|
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 `
|
|
34
|
-
//
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
//
|
|
47
|
-
//
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
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
|
|
102
|
+
* Get a collection for a SMRT class, scoped to the current request.
|
|
87
103
|
*
|
|
88
|
-
*
|
|
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
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
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
|
|
1
|
+
import { createSessionLayoutLoad } from '@happyvertical/smrt-app-runtime/sveltekit';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
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
|
|
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 {
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
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
|
-
//
|
|
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
|
-
<
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
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
|
|
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 {
|
|
5
|
+
import { runtime } from '$lib/server/smrt';
|
|
4
6
|
|
|
5
|
-
|
|
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 {
|
|
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
|
-
|
|
10
|
-
sourceRoot: process.cwd(),
|
|
11
|
-
explicitId: process.env.SMRT_APP_ID,
|
|
12
|
-
});
|
|
3
|
+
import { runtime } from '$lib/server/smrt';
|
|
13
4
|
|
|
14
|
-
|
|
15
|
-
|
|
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 {
|
|
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
|
-
|
|
16
|
-
|
|
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 />
|