@happyvertical/smrt-template-sveltekit 0.53.2 → 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/AGENTS.md
CHANGED
|
@@ -17,6 +17,16 @@ It is the ground-up alternative to `smrt-saas-starter`.
|
|
|
17
17
|
images ship no Corepack, so the Dockerfile installs a pinned copy.
|
|
18
18
|
- `runtime.profile` is the canonical infrastructure selector. Generated apps
|
|
19
19
|
expose deterministic `app:*` operations and keep runtime state outside source.
|
|
20
|
+
- Every lifecycle script is a `smrt app <operation>` one-liner
|
|
21
|
+
(`packages/cli/agents/app-commands.md`); the template ships no `scripts/`.
|
|
22
|
+
Never reintroduce copied operator scripts: fix the CLI command instead.
|
|
23
|
+
- Server composition is `createSmrtSvelteKitRuntime()` from
|
|
24
|
+
`@happyvertical/smrt-app-runtime/sveltekit`, built once in
|
|
25
|
+
`src/lib/server/smrt.ts` with the writer lease
|
|
26
|
+
(`acquireWriterLease(prepareApplicationStateRoot(...))`) and provider
|
|
27
|
+
readiness (`createProviderReadinessProbe`) from app-runtime's root entry, so
|
|
28
|
+
the web process never imports the CLI. Health, diagnostics, layout session,
|
|
29
|
+
and owner setup routes mount that package's handlers.
|
|
20
30
|
- The production baseline uses adapter-node with separate web, task-worker, and
|
|
21
31
|
schedule-worker processes. Workers import the build-compiled
|
|
22
32
|
`.smrt/runtime/register.js` (#3117) before creating runners so app-defined
|
|
@@ -37,8 +47,9 @@ It is the ground-up alternative to `smrt-saas-starter`.
|
|
|
37
47
|
different or stale server is never accepted as this app. Deployed health
|
|
38
48
|
responses expose only generic status and profile.
|
|
39
49
|
- Directly used `@happyvertical/smrt-*` packages share one current release range.
|
|
40
|
-
- `@happyvertical/smrt-cli` is a direct dev dependency because
|
|
41
|
-
its binary
|
|
50
|
+
- `@happyvertical/smrt-cli` is a direct dev dependency because every
|
|
51
|
+
lifecycle script runs its binary; the runtime image keeps dev dependencies so
|
|
52
|
+
workers and operator commands can. The template includes `@happyvertical/smrt-web` because the root
|
|
42
53
|
Provider wires the generated read-only WebMCP definitions for every page.
|
|
43
54
|
- The generated MCP server resolves its imports from the scaffolded app, not
|
|
44
55
|
from the CLI, so every specifier `generate-mcp` emits must be declared here.
|
|
@@ -49,9 +60,10 @@ It is the ground-up alternative to `smrt-saas-starter`.
|
|
|
49
60
|
for the default worker and tenant surfaces. Do not assume pnpm exposes the
|
|
50
61
|
CLI's or core's transitive dependencies to the app root — its strict layout
|
|
51
62
|
does not (#2297).
|
|
52
|
-
- `
|
|
53
|
-
`
|
|
54
|
-
|
|
63
|
+
- `vite.config.ts` is `[sveltekit(), smrt()]`. The preset consumes exactly the
|
|
64
|
+
packages in `smrt.config.ts` `consumer.packages` (profiles, tenancy, users),
|
|
65
|
+
scans `src/lib/objects`, and generates Vite virtual definitions, SvelteKit
|
|
66
|
+
routes, runtime registration, and knowledge artifacts.
|
|
55
67
|
- `pnpm db:migrate` builds first to refresh generated artifacts, then the
|
|
56
68
|
migration wrapper holds the shared operation/writer exclusion for the full
|
|
57
69
|
manifest-driven migration command. Do not restore deprecated `smrt db:setup`
|
|
@@ -59,11 +71,13 @@ It is the ground-up alternative to `smrt-saas-starter`.
|
|
|
59
71
|
|
|
60
72
|
## Application patterns
|
|
61
73
|
|
|
62
|
-
- `src/hooks.server.ts`
|
|
63
|
-
|
|
64
|
-
|
|
74
|
+
- `src/hooks.server.ts` re-exports the runtime's `handle`/`init`: URL tenant
|
|
75
|
+
selection stays a separate candidate and only the verified session
|
|
76
|
+
establishes tenant context. Never turn an untrusted header into authority.
|
|
65
77
|
- `src/lib/server/smrt.ts` imports generated local registrations, loads the
|
|
66
|
-
generated manifest metadata,
|
|
78
|
+
generated manifest metadata, creates the runtime, and exposes
|
|
79
|
+
`getCollection()`/`getSmrtConfig()` over `runtime.classOptions()`, which is
|
|
80
|
+
request-scoped (RLS transaction) and must never be retained.
|
|
67
81
|
- `Item` is the single example object and demonstrates optional tenant scope,
|
|
68
82
|
a REST writable allowlist, shared CRUD action metadata, and the explicit
|
|
69
83
|
collection registration required by generated CLI/MCP runtime commands.
|
|
@@ -71,18 +85,25 @@ It is the ground-up alternative to `smrt-saas-starter`.
|
|
|
71
85
|
`depends('smrt:items')`, mutations call `invalidate('smrt:items')`, and
|
|
72
86
|
hand-written writes call `assertOperationPermission()` with the session's
|
|
73
87
|
exact permission snapshot.
|
|
74
|
-
- The root layout
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
page
|
|
88
|
+
- The root layout is `AppShell` from `@happyvertical/smrt-svelte/app` with a
|
|
89
|
+
small explicit `nav`; `/setup` and `/settings` mount that package's
|
|
90
|
+
`OwnerSetupForm` and `ShellSettingsPage`. Generated REST routes do not imply
|
|
91
|
+
page routes.
|
|
92
|
+
- AppShell's Provider registers generated WebMCP read tools with the
|
|
93
|
+
authenticated page session. Write/destructive effects require an explicit
|
|
94
|
+
page-owned policy.
|
|
78
95
|
Live browser collections remain opt-in per page and must seed from SSR
|
|
79
96
|
`initialData` to avoid a duplicate first request.
|
|
80
|
-
- `
|
|
81
|
-
`smrt.runtime.diagnostics.read
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
owner role or
|
|
85
|
-
|
|
97
|
+
- AppShell's `runtimeDiagnostics` (on for signed-in sessions) owns exactly one
|
|
98
|
+
additional read-only tool, `smrt.runtime.diagnostics.read`, which fetches the
|
|
99
|
+
mounted `/api/_runtime/diagnostics` route as the page user. That route
|
|
100
|
+
(`createRuntimeDiagnosticsHandler`) requires a direct active tenant membership
|
|
101
|
+
plus the owner role or `runtime_diagnostics.read` before any projection or
|
|
102
|
+
probe; it never calls principal-bound server tools.
|
|
103
|
+
- The opt-in `mcp-apps-template/` overlay is one `mountMcpAppRoute()` route,
|
|
104
|
+
the protected-resource metadata route, and the application-owned
|
|
105
|
+
`mcp-hosted-principal.ts` binding; `pnpm typecheck` checks it against the
|
|
106
|
+
template.
|
|
86
107
|
|
|
87
108
|
## Tests
|
|
88
109
|
|
|
@@ -126,11 +147,13 @@ own. Never add a mocked REST handler, an in-memory database, or DOM automation
|
|
|
126
147
|
presented as WebMCP execution.
|
|
127
148
|
|
|
128
149
|
- `@happyvertical/smrt-cli` is a devDependency **because the gate needs it**:
|
|
129
|
-
the app's `app
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
150
|
+
the harness runs the app's own `smrt app setup` and `smrt app dev` from the
|
|
151
|
+
linked CLI, and setup's `pnpm build` is `smrt app build`. The copied app
|
|
152
|
+
deliberately never runs `pnpm install`, so `smrt` has to arrive through the
|
|
153
|
+
linked `node_modules`. The harness pins the app's own `.bin` ahead of `PATH`
|
|
154
|
+
and fails with a named error if the binary is missing, so a host-global
|
|
155
|
+
`smrt` cannot make a local run pass where CI fails. Identity and state-root
|
|
156
|
+
values it checks come from `@happyvertical/smrt-app-runtime`.
|
|
134
157
|
- `pnpm test:e2e` runs the browser half.
|
|
135
158
|
- `pnpm test:m5` runs the whole aggregate gate through
|
|
136
159
|
`e2e/support/gate.mjs`, which requires a PostgreSQL service and fails when a
|
|
@@ -145,8 +168,12 @@ presented as WebMCP execution.
|
|
|
145
168
|
from `testInfo.project.outputDir` and asserts it is outside the repository.
|
|
146
169
|
The harness gives the copied app a real `node_modules` directory of
|
|
147
170
|
individually symlinked entries rather than one symlink to this package's, so
|
|
148
|
-
the served app's `node_modules/.vite` cache cannot land in the checkout
|
|
149
|
-
|
|
171
|
+
the served app's `node_modules/.vite` cache cannot land in the checkout.
|
|
172
|
+
Scope directories (`@happyvertical/`) and `.bin/` are real directories of
|
|
173
|
+
per-entry links too: the copy ships `pnpm-workspace.yaml` and setup runs
|
|
174
|
+
`pnpm build` in it, so anything pnpm relinks must land in the copy, never in
|
|
175
|
+
this package's `node_modules`. Never replace either loop with a
|
|
176
|
+
whole-directory symlink.
|
|
150
177
|
- `e2e/` is outside `biome.json`'s include globs, as every package's `e2e/` is,
|
|
151
178
|
so a green `Lint` job says nothing about this tree. `pnpm typecheck` covers
|
|
152
179
|
the `.ts` files (`tsconfig.fixture.json` includes `e2e/**/*.ts`) but NOT
|
package/README.md
CHANGED
|
@@ -52,17 +52,21 @@ fixtures.
|
|
|
52
52
|
|
|
53
53
|
## 2. Understand the generated files
|
|
54
54
|
|
|
55
|
-
The checked-in template contains authored objects,
|
|
56
|
-
|
|
55
|
+
The checked-in template contains authored objects, one server module
|
|
56
|
+
(`src/lib/server/smrt.ts`, the runtime and collection access), thin hooks and
|
|
57
|
+
routes that mount package handlers, pages, and configuration. It ships no
|
|
58
|
+
operator scripts. Vite generates `.smrt/manifest.json`,
|
|
57
59
|
`.smrt/smrt-knowledge.json`, `.smrt/register.js`,
|
|
58
60
|
`src/lib/server/smrt-register.ts`, `src/lib/types/smrt-generated/`, and
|
|
59
61
|
`src/routes/api/**/+server.ts`. All are ignored and excluded from scaffold
|
|
60
62
|
fixtures.
|
|
61
63
|
|
|
62
|
-
`
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
`vite.config.ts` is `plugins: [sveltekit(), smrt()]`: the
|
|
65
|
+
`@happyvertical/smrt-core/vite` preset applies the decorator transform, scans
|
|
66
|
+
the local object directory, generates local routes and types, and consumes the
|
|
67
|
+
packages `smrt.config.ts` lists under `consumer.packages` (profiles, tenancy,
|
|
68
|
+
users). `@happyvertical/smrt-cli` is a direct dev dependency because every
|
|
69
|
+
lifecycle script is `smrt app <operation>`.
|
|
66
70
|
|
|
67
71
|
## 3. Define the first object
|
|
68
72
|
|
|
@@ -85,25 +89,28 @@ pnpm db:migrate
|
|
|
85
89
|
```
|
|
86
90
|
|
|
87
91
|
The script builds first to refresh manifests, registrations, types, and API
|
|
88
|
-
routes, then runs `smrt
|
|
92
|
+
routes, then runs `smrt app migrate`, which holds the operation lock and runs
|
|
93
|
+
`smrt db:migrate`. The deprecated `smrt db:setup` command is
|
|
89
94
|
not documented or shipped.
|
|
90
95
|
|
|
91
96
|
## 5. Understand tenant context
|
|
92
97
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
98
|
+
`src/hooks.server.ts` mounts `createSmrtSvelteKitRuntime()` from
|
|
99
|
+
`@happyvertical/smrt-app-runtime/sveltekit`. It resolves a URL tenant candidate
|
|
100
|
+
into separate `selectedTenantId`/`selectedTenantSlug` locals and does not
|
|
101
|
+
establish query context from that selection. The session step runs afterward
|
|
102
|
+
and only its membership-authorized tenant enters the s-m-r-t tenancy context.
|
|
97
103
|
|
|
98
|
-
Untrusted tenant headers are ignored. Applications may
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
104
|
+
Untrusted tenant headers are ignored. Applications may pass `selectTenant` for
|
|
105
|
+
a path, signed-cookie, or trusted-gateway selector, but selection must remain
|
|
106
|
+
separate from authorization and session switches must use
|
|
107
|
+
`switchSessionTenant()`.
|
|
102
108
|
|
|
103
109
|
## 6. Understand users, profiles, memberships, roles, and permissions
|
|
104
110
|
|
|
105
111
|
The generated project consumes the current profiles and users manifests and
|
|
106
|
-
|
|
112
|
+
loads the signed session through the runtime, which enters tenant context only
|
|
113
|
+
for a verified active membership. A User is the auth
|
|
107
114
|
identity, Profile is person-facing metadata, Membership joins a User to a
|
|
108
115
|
Tenant and Role, Role maps to Permission records, and Session publishes the
|
|
109
116
|
resolved permission snapshot.
|
package/mcp-apps-template/src/routes/.well-known/oauth-protected-resource/api/mcp/+server.ts
CHANGED
|
@@ -1,7 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createHostedMcpResourceAuth } from '@happyvertical/smrt-app-mcp/auth';
|
|
2
|
+
import { mountMcpProtectedResourceMetadataRoute } from '@happyvertical/smrt-app-mcp/sveltekit';
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
import { resolveHostedMcpPrincipal } from '$lib/server/mcp-hosted-principal';
|
|
5
|
+
import { runtime } from '$lib/server/smrt';
|
|
6
|
+
|
|
7
|
+
const { profile } = await runtime.resolvedRuntime();
|
|
8
|
+
|
|
9
|
+
/** RFC 9728 metadata at exactly the URL hosted bearer challenges advertise (404 locally). */
|
|
10
|
+
export const GET = mountMcpProtectedResourceMetadataRoute(
|
|
11
|
+
createHostedMcpResourceAuth({
|
|
12
|
+
profile,
|
|
13
|
+
resolvePrincipal: resolveHostedMcpPrincipal,
|
|
14
|
+
}),
|
|
15
|
+
);
|
|
@@ -1,15 +1,72 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import type { McpWorkflowToolDefinition } from '@happyvertical/smrt-app-mcp';
|
|
2
|
+
import { createHostedMcpResourceAuth } from '@happyvertical/smrt-app-mcp/auth';
|
|
3
|
+
import { mountMcpAppRoute } from '@happyvertical/smrt-app-mcp/sveltekit';
|
|
4
|
+
import { openAiDisplayMetadata, withOpenAiEntrypoints } from '@happyvertical/smrt-mcp-openai';
|
|
2
5
|
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
6
|
+
import { Item } from '$lib/objects/Item';
|
|
7
|
+
import { resolveHostedMcpPrincipal } from '$lib/server/mcp-hosted-principal';
|
|
8
|
+
import { runtime } from '$lib/server/smrt';
|
|
5
9
|
|
|
6
|
-
|
|
7
|
-
const
|
|
10
|
+
const ITEM_RESOURCE_URI = 'ui://smrt-app/v1/items.html';
|
|
11
|
+
const itemResourceHtml = `<!doctype html><html lang="en"><head><title>Items</title></head><body><main tabindex="-1"><h1>Items</h1><p>Use the host controls to review your authorized items.</p></main></body></html>`;
|
|
8
12
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
const itemsOverview: McpWorkflowToolDefinition = withOpenAiEntrypoints(
|
|
14
|
+
{
|
|
15
|
+
name: 'items_overview',
|
|
16
|
+
description: 'Open the authorized item overview.',
|
|
17
|
+
title: 'Items',
|
|
18
|
+
inputSchema: { type: 'object', properties: {}, additionalProperties: false },
|
|
19
|
+
outputSchema: {
|
|
20
|
+
type: 'object',
|
|
21
|
+
properties: { state: { type: 'string' } },
|
|
22
|
+
required: ['state'],
|
|
23
|
+
additionalProperties: false,
|
|
24
|
+
},
|
|
25
|
+
effect: 'read',
|
|
26
|
+
idempotent: true,
|
|
27
|
+
openWorld: false,
|
|
28
|
+
ui: { resourceUri: ITEM_RESOURCE_URI },
|
|
29
|
+
async execute() {
|
|
30
|
+
return {
|
|
31
|
+
content: [{ type: 'text', text: 'Item overview is ready.' }],
|
|
32
|
+
structuredContent: { state: 'ready' },
|
|
33
|
+
};
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
['global'],
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const { profile } = await runtime.resolvedRuntime();
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Stateless SDK-v2 Streamable HTTP MCP endpoint for this app's own `Item`.
|
|
43
|
+
* Locally the principal is the signed session (user, authorized tenant, and
|
|
44
|
+
* permission slugs); hosted profiles verify a bearer token and map it through
|
|
45
|
+
* the application-owned `resolveHostedMcpPrincipal`, and the verified bearer
|
|
46
|
+
* principal is bound into the request permission context (under
|
|
47
|
+
* `database-rls`, its own RLS transaction). Every principal needs
|
|
48
|
+
* `items.read`, so only read-only tools are published until per-operation
|
|
49
|
+
* authorization exists; browser requests from a foreign `Origin` are refused.
|
|
50
|
+
*/
|
|
51
|
+
export const POST = mountMcpAppRoute({
|
|
52
|
+
models: [Item],
|
|
53
|
+
requiredScopes: ['items.read'],
|
|
54
|
+
effects: ['read'],
|
|
55
|
+
smrtOptions: () => ({ db: runtime.databaseConfig() }),
|
|
56
|
+
auth: createHostedMcpResourceAuth({
|
|
57
|
+
profile,
|
|
58
|
+
resolvePrincipal: resolveHostedMcpPrincipal,
|
|
59
|
+
}),
|
|
60
|
+
bindPrincipal: runtime.runAsPrincipal,
|
|
61
|
+
workflowTools: [itemsOverview],
|
|
62
|
+
resources: [
|
|
63
|
+
{
|
|
64
|
+
uri: ITEM_RESOURCE_URI,
|
|
65
|
+
version: 'v1',
|
|
66
|
+
name: 'Items',
|
|
67
|
+
html: itemResourceHtml,
|
|
68
|
+
csp: {},
|
|
69
|
+
metadata: openAiDisplayMetadata({ availableDisplayModes: ['inline'] }),
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@happyvertical/smrt-template-sveltekit",
|
|
3
|
-
"version": "0.53.
|
|
3
|
+
"version": "0.53.4",
|
|
4
4
|
"description": "Minimal SvelteKit project template with s-m-r-t framework integration",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -32,22 +32,24 @@
|
|
|
32
32
|
"directory": "packages/template-sveltekit"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
|
-
"@happyvertical/smrt-core": "0.53.
|
|
35
|
+
"@happyvertical/smrt-core": "0.53.4"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@happyvertical/smrt-app-
|
|
39
|
-
"@happyvertical/smrt-
|
|
40
|
-
"@happyvertical/smrt-
|
|
41
|
-
"@happyvertical/smrt-
|
|
42
|
-
"@happyvertical/smrt-
|
|
43
|
-
"@happyvertical/smrt-
|
|
44
|
-
"@happyvertical/smrt-
|
|
45
|
-
"@happyvertical/smrt-
|
|
46
|
-
"@happyvertical/smrt-
|
|
47
|
-
"@happyvertical/smrt-
|
|
48
|
-
"@happyvertical/smrt-
|
|
49
|
-
"@happyvertical/smrt-
|
|
50
|
-
"@happyvertical/smrt-
|
|
38
|
+
"@happyvertical/smrt-app-mcp": "0.53.4",
|
|
39
|
+
"@happyvertical/smrt-app-runtime": "0.53.4",
|
|
40
|
+
"@happyvertical/smrt-assets": "0.53.4",
|
|
41
|
+
"@happyvertical/smrt-cli": "0.53.4",
|
|
42
|
+
"@happyvertical/smrt-config": "0.53.4",
|
|
43
|
+
"@happyvertical/smrt-jobs": "0.53.4",
|
|
44
|
+
"@happyvertical/smrt-mcp-openai": "0.53.4",
|
|
45
|
+
"@happyvertical/smrt-playbooks": "0.53.4",
|
|
46
|
+
"@happyvertical/smrt-profiles": "0.53.4",
|
|
47
|
+
"@happyvertical/smrt-scanner": "0.53.4",
|
|
48
|
+
"@happyvertical/smrt-svelte": "0.53.4",
|
|
49
|
+
"@happyvertical/smrt-tenancy": "0.53.4",
|
|
50
|
+
"@happyvertical/smrt-ui": "0.53.4",
|
|
51
|
+
"@happyvertical/smrt-users": "0.53.4",
|
|
52
|
+
"@happyvertical/smrt-web": "0.53.4",
|
|
51
53
|
"@happyvertical/sql": "^0.89.12",
|
|
52
54
|
"@modelcontextprotocol/server": "^2.0.0",
|
|
53
55
|
"@playwright/test": "^1.63.0",
|
package/template/AGENTS.md
CHANGED
|
@@ -18,7 +18,16 @@ tools, WebMCP definitions, and agent/developer knowledge artifacts.
|
|
|
18
18
|
return only the schema-versioned allowlist; never expose private runtime
|
|
19
19
|
objects, identity fields, paths, URLs, secrets, PII, raw errors, or logs.
|
|
20
20
|
- Never improvise setup internals. Run `pnpm app:install`; use individual
|
|
21
|
-
`app:*` commands only for recovery or explicit operator control.
|
|
21
|
+
`app:*` commands only for recovery or explicit operator control. They call
|
|
22
|
+
`smrt app <operation>` from `@happyvertical/smrt-cli`; do not copy operator
|
|
23
|
+
scripts into this app.
|
|
24
|
+
- Server composition (profile, storage custody, owner bootstrap, tenant
|
|
25
|
+
selection, session, health, diagnostics) comes from
|
|
26
|
+
`@happyvertical/smrt-app-runtime/sveltekit`, configured once in
|
|
27
|
+
`src/lib/server/smrt.ts`. Pass app-specific behaviour as options
|
|
28
|
+
(`selectTenant`, `classOverrides`, `session`, `prepareDatabase`); do not
|
|
29
|
+
reimplement hooks. `getCollection()`/`getSmrtConfig()` are request-scoped:
|
|
30
|
+
never keep their result beyond the request.
|
|
22
31
|
- Recover an interrupted unclaimed owner invitation with `pnpm app:stop`, then
|
|
23
32
|
`pnpm app:recover`, `pnpm app:start`, and `pnpm app:open`; never print or copy
|
|
24
33
|
its token.
|
|
@@ -48,8 +57,9 @@ tools, WebMCP definitions, and agent/developer knowledge artifacts.
|
|
|
48
57
|
- Keep object relationship metadata close to `@smrt()`.
|
|
49
58
|
- Add human-facing routes explicitly; generated REST routes do not create nav
|
|
50
59
|
pages.
|
|
51
|
-
- The root layout
|
|
52
|
-
|
|
60
|
+
- The root layout is `AppShell` from `@happyvertical/smrt-svelte/app`; it
|
|
61
|
+
includes `@happyvertical/smrt-web` and registers generated WebMCP read tools
|
|
62
|
+
through its Provider when the browser exposes `modelContext`.
|
|
53
63
|
Keep the guard in place for SSR and seed live collections with SSR
|
|
54
64
|
`initialData`.
|
|
55
65
|
- Do not enable knowledge HTTP routes in production without explicit admin auth.
|
|
@@ -57,8 +67,9 @@ tools, WebMCP definitions, and agent/developer knowledge artifacts.
|
|
|
57
67
|
imports `.smrt/runtime/register.js` (compiled by `pnpm build`) before any
|
|
58
68
|
runner starts, so jobs can target this app's own `@smrt()` objects; keep that
|
|
59
69
|
import ahead of runner creation and keep domain objects free of SvelteKit
|
|
60
|
-
virtual modules (`$env/*`, `$app/*`), which a worker cannot load.
|
|
61
|
-
portability adapter
|
|
70
|
+
virtual modules (`$env/*`, `$app/*`), which a worker cannot load. Add a
|
|
71
|
+
`scripts/smrt-portability.mjs` adapter (it overrides the CLI's built-in one)
|
|
72
|
+
instead of converting database files. Deployed provider
|
|
62
73
|
readiness must come from installed modules that probe the real backing
|
|
63
74
|
services; environment booleans are not readiness evidence.
|
|
64
75
|
- Every supported local web entry point (`app:start` or `pnpm dev`) holds the
|
package/template/Dockerfile
CHANGED
|
@@ -17,7 +17,6 @@ ENV NODE_ENV=production
|
|
|
17
17
|
ENV SMRT_RUNTIME_PROFILE=self-hosted
|
|
18
18
|
COPY --from=build /app/build ./build
|
|
19
19
|
COPY --from=build /app/.smrt ./.smrt
|
|
20
|
-
COPY --from=build /app/scripts ./scripts
|
|
21
20
|
COPY --from=build /app/smrt.config.ts ./smrt.config.ts
|
|
22
21
|
RUN chown -R node:node /app
|
|
23
22
|
USER node
|
|
@@ -7,5 +7,5 @@
|
|
|
7
7
|
> `pnpm app:doctor`, show me its secret-free recovery findings, and ask before
|
|
8
8
|
> changing provider configuration.
|
|
9
9
|
|
|
10
|
-
The prompt is intentionally thin. `
|
|
11
|
-
recovery.
|
|
10
|
+
The prompt is intentionally thin. `smrt app` (from `@happyvertical/smrt-cli`)
|
|
11
|
+
owns operations and recovery.
|
package/template/README.md
CHANGED
|
@@ -22,7 +22,9 @@ re-running install, setup, or recovery; each operation is repeatable from that
|
|
|
22
22
|
stopped state. `pnpm app:doctor`
|
|
23
23
|
prints secret-free JSON diagnostics and recovery steps. Individual
|
|
24
24
|
setup/start/doctor/open/stop/backup/export/import operations are available as
|
|
25
|
-
`pnpm app:<operation>`.
|
|
25
|
+
`pnpm app:<operation>`. Every lifecycle script is a one-line call to
|
|
26
|
+
`smrt app <operation>` from `@happyvertical/smrt-cli`; the app ships no copied
|
|
27
|
+
operator scripts, so upgrading the CLI upgrades them.
|
|
26
28
|
|
|
27
29
|
`app:start` defaults adapter-node `ORIGIN` to its loopback HTTP URL (including
|
|
28
30
|
the selected `PORT`) and preserves an explicitly configured `ORIGIN`. Origin
|
|
@@ -58,9 +60,12 @@ The source of truth is `src/lib/objects`. Running `pnpm dev`, `pnpm build`, or
|
|
|
58
60
|
| `src/lib/types/smrt-generated/` | Virtual-module and consumer declarations | No |
|
|
59
61
|
| `src/routes/api/**/+server.ts` | Generated SvelteKit REST routes | No |
|
|
60
62
|
|
|
61
|
-
Do not edit generated files. `
|
|
62
|
-
types,
|
|
63
|
-
|
|
63
|
+
Do not edit generated files. The `smrt()` plugin in `vite.config.ts` owns
|
|
64
|
+
local scanning, manifests, types, routes, and the decorator transform. It
|
|
65
|
+
consumes exactly the packages listed in `smrt.config.ts` under
|
|
66
|
+
`consumer.packages` (profiles, tenancy, and users) so those models are
|
|
67
|
+
available to setup and tooling; add a SMRT package there when the app uses its
|
|
68
|
+
objects.
|
|
64
69
|
|
|
65
70
|
## 3. Define the first object
|
|
66
71
|
|
|
@@ -130,22 +135,31 @@ migration command, not by adding schema creation to a request handler.
|
|
|
130
135
|
|
|
131
136
|
## 5. Understand tenant context
|
|
132
137
|
|
|
133
|
-
`src/hooks.server.ts`
|
|
138
|
+
`src/hooks.server.ts` mounts the runtime configured in `src/lib/server/smrt.ts`
|
|
139
|
+
(`createSmrtSvelteKitRuntime()` from `@happyvertical/smrt-app-runtime/sveltekit`).
|
|
140
|
+
It keeps tenant selection separate from authorization:
|
|
134
141
|
|
|
135
|
-
1.
|
|
136
|
-
|
|
137
|
-
`locals.selectedTenantSlug`.
|
|
142
|
+
1. A subdomain slug is looked up as an active Tenant UUID and stored only as a
|
|
143
|
+
candidate in `locals.selectedTenantId` and `locals.selectedTenantSlug`.
|
|
138
144
|
2. That candidate does not enter AsyncLocalStorage and cannot scope queries.
|
|
139
|
-
3.
|
|
140
|
-
|
|
141
|
-
`locals.tenantId
|
|
142
|
-
4.
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
145
|
+
3. The signed session is loaded. Its tenant is accepted only for an active (or
|
|
146
|
+
legitimately inherited) membership, which establishes the authorized
|
|
147
|
+
`locals.tenantId`, the permission set, and the tenant context.
|
|
148
|
+
4. The runtime enables tenancy, so `@TenantScoped` collections honor that
|
|
149
|
+
context.
|
|
150
|
+
|
|
151
|
+
The default selector ignores `x-tenant-id`. To select tenants from a path,
|
|
152
|
+
signed cookie, or trusted gateway, pass `selectTenant` to
|
|
153
|
+
`createSmrtSvelteKitRuntime()`; selection must still never establish
|
|
154
|
+
authorization. Validate a gateway's identity/signature before mapping its
|
|
155
|
+
header to a tenant, and use `switchSessionTenant()` for browser session
|
|
156
|
+
changes. That helper checks active membership and rotates the session ID; never
|
|
157
|
+
copy an untrusted header directly into `locals.tenantId` or
|
|
158
|
+
`enterTenantContext()`.
|
|
159
|
+
|
|
160
|
+
`getCollection()` and `getSmrtConfig()` in `$lib/server/smrt` return
|
|
161
|
+
request-scoped options (under `database-rls` isolation they carry the request
|
|
162
|
+
transaction): call them inside a request and never keep the result.
|
|
149
163
|
|
|
150
164
|
Set `TENANT_BASE_DOMAIN` for deployed subdomain routing. The fallback parser is
|
|
151
165
|
only for local shapes such as `acme.demo.local`.
|
|
@@ -234,10 +248,14 @@ non-object or malformed manifests, credential-shaped JSON fields and credential
|
|
|
234
248
|
URL userinfo, non-portable schemas, and non-loopback HTTP server URLs. Diagnostics never
|
|
235
249
|
include malformed manifest content.
|
|
236
250
|
|
|
237
|
-
The option stages `src/routes/api/mcp/+server.ts`,
|
|
238
|
-
|
|
239
|
-
`
|
|
240
|
-
|
|
251
|
+
The option stages `src/routes/api/mcp/+server.ts`, one `mountMcpAppRoute()`
|
|
252
|
+
call from `@happyvertical/smrt-app-mcp/sveltekit` that publishes `Item` to
|
|
253
|
+
principals holding `items.read` (the signed session's permissions locally),
|
|
254
|
+
refuses browser requests from a foreign `Origin`, and serves a bounded static
|
|
255
|
+
resource with a restrictive CSP and the optional OpenAI display metadata. It
|
|
256
|
+
also stages the protected-resource metadata route. For a view, mount
|
|
257
|
+
`McpAppsBridge` from `@happyvertical/smrt-svelte/mcp-apps` with an
|
|
258
|
+
application-configured trusted host origin.
|
|
241
259
|
The scaffold does not enable remote MCP tasks, so a deployment that adds them
|
|
242
260
|
must supply the durable worker's live authorization callback before publication.
|
|
243
261
|
|
|
@@ -300,7 +318,7 @@ narrower tool set.
|
|
|
300
318
|
```svelte
|
|
301
319
|
<script lang="ts">
|
|
302
320
|
import { webMcpToolDefinitions } from '@happyvertical/smrt-virt-web';
|
|
303
|
-
import {
|
|
321
|
+
import { AppShell } from '@happyvertical/smrt-svelte/app';
|
|
304
322
|
|
|
305
323
|
const webmcp = $derived(
|
|
306
324
|
typeof document !== 'undefined' && 'modelContext' in document
|
|
@@ -309,11 +327,17 @@ narrower tool set.
|
|
|
309
327
|
);
|
|
310
328
|
</script>
|
|
311
329
|
|
|
312
|
-
|
|
330
|
+
<!-- AppShell passes `webmcp` to its Provider unchanged. -->
|
|
331
|
+
<AppShell title="s-m-r-t app" {webmcp} {nav}>
|
|
313
332
|
{@render children()}
|
|
314
|
-
</
|
|
333
|
+
</AppShell>
|
|
315
334
|
```
|
|
316
335
|
|
|
336
|
+
The root layout is `AppShell` from `@happyvertical/smrt-svelte/app` (Provider,
|
|
337
|
+
theme and its CSS, the admin shell, and navigation); the app supplies its
|
|
338
|
+
navigation and content. `/setup` and `/settings` use the same package's
|
|
339
|
+
`OwnerSetupForm` and `ShellSettingsPage`.
|
|
340
|
+
|
|
317
341
|
`registerWebMcpTools()` feature-detects browser support and uses the current
|
|
318
342
|
authenticated page session. Omitted policy exposes all `read`-effect tools:
|
|
319
343
|
intrinsic `list`/`get` plus custom actions explicitly declared as reads. To
|
|
@@ -399,8 +423,9 @@ Every supported local web
|
|
|
399
423
|
entry point (`app:start` or `pnpm dev`) holds a shared writer lease. Direct
|
|
400
424
|
production startup must set an explicit loopback `HOST`, and `app:start` is the
|
|
401
425
|
recommended entry point. Stop the app before backup/import. For deployed import, stop
|
|
402
|
-
web/workers and set `SMRT_MAINTENANCE_MODE=true`.
|
|
403
|
-
`scripts/smrt-portability.mjs`
|
|
426
|
+
web/workers and set `SMRT_MAINTENANCE_MODE=true`. For domain-specific
|
|
427
|
+
transformations, add a `scripts/smrt-portability.mjs` adapter; when present it
|
|
428
|
+
replaces `smrt app export`/`import`'s built-in one.
|
|
404
429
|
|
|
405
430
|
### Hosted MCP authorization
|
|
406
431
|
|
package/template/compose.yaml
CHANGED
|
@@ -16,7 +16,7 @@ services:
|
|
|
16
16
|
build: .
|
|
17
17
|
env_file: .env.self-hosted
|
|
18
18
|
environment: *runtime-environment
|
|
19
|
-
command:
|
|
19
|
+
command: pnpm exec smrt app worker task
|
|
20
20
|
depends_on:
|
|
21
21
|
migrate:
|
|
22
22
|
condition: service_completed_successfully
|
|
@@ -24,7 +24,7 @@ services:
|
|
|
24
24
|
build: .
|
|
25
25
|
env_file: .env.self-hosted
|
|
26
26
|
environment: *runtime-environment
|
|
27
|
-
command:
|
|
27
|
+
command: pnpm exec smrt app worker schedule
|
|
28
28
|
depends_on:
|
|
29
29
|
migrate:
|
|
30
30
|
condition: service_completed_successfully
|