@happyvertical/smrt-template-sveltekit 0.53.4 → 0.53.6

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 CHANGED
@@ -22,11 +22,17 @@ It is the ground-up alternative to `smrt-saas-starter`.
22
22
  Never reintroduce copied operator scripts: fix the CLI command instead.
23
23
  - Server composition is `createSmrtSvelteKitRuntime()` from
24
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.
25
+ `src/lib/server/smrt.ts`, which holds only `runtime` and its options
26
+ (provider readiness via `createProviderReadinessProbe`), so the web process
27
+ never imports the CLI. The runtime takes the local writer lease by default,
28
+ the `smrt()` plugin injects the generated registration, and each generated
29
+ route embeds a prelude that imports `$lib/server/smrt` and resolves
30
+ collections through its exported `runtime.getCollection()` (#3416). A legacy
31
+ `getCollection`/`getSmrtConfig` export would take precedence (deprecated,
32
+ honoured for one release): never re-add one, a register import, guard, or
33
+ manifest hydration.
34
+ Health, diagnostics, layout session, and owner setup routes mount that
35
+ package's handlers.
30
36
  - The production baseline uses adapter-node with separate web, task-worker, and
31
37
  schedule-worker processes. Workers import the build-compiled
32
38
  `.smrt/runtime/register.js` (#3117) before creating runners so app-defined
@@ -74,9 +80,7 @@ It is the ground-up alternative to `smrt-saas-starter`.
74
80
  - `src/hooks.server.ts` re-exports the runtime's `handle`/`init`: URL tenant
75
81
  selection stays a separate candidate and only the verified session
76
82
  establishes tenant context. Never turn an untrusted header into authority.
77
- - `src/lib/server/smrt.ts` imports generated local registrations, loads the
78
- generated manifest metadata, creates the runtime, and exposes
79
- `getCollection()`/`getSmrtConfig()` over `runtime.classOptions()`, which is
83
+ - App code uses `runtime.getCollection()`/`runtime.classOptions()`, which are
80
84
  request-scoped (RLS transaction) and must never be retained.
81
85
  - `Item` is the single example object and demonstrates optional tenant scope,
82
86
  a REST writable allowlist, shared CRUD action metadata, and the explicit
@@ -100,10 +104,12 @@ It is the ground-up alternative to `smrt-saas-starter`.
100
104
  (`createRuntimeDiagnosticsHandler`) requires a direct active tenant membership
101
105
  plus the owner role or `runtime_diagnostics.read` before any projection or
102
106
  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.
107
+ - The opt-in `mcp-apps-template/` overlay is one `mountMcpAppRoute()` route
108
+ and the protected-resource metadata route, both using
109
+ `createHostedMcpResourceAuth({ profile, runtime })` (local owner tokens,
110
+ hosted membership-backed principals); `pnpm typecheck` checks it against
111
+ the template. `__tests__/localMcpToken.test.ts` proves mint → real
112
+ `smrt-mcp-bridge` stdio → route against the real runtime.
107
113
 
108
114
  ## Tests
109
115
 
package/README.md CHANGED
@@ -53,7 +53,7 @@ fixtures.
53
53
  ## 2. Understand the generated files
54
54
 
55
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
56
+ (`src/lib/server/smrt.ts`, the runtime and its options), thin hooks and
57
57
  routes that mount package handlers, pages, and configuration. It ships no
58
58
  operator scripts. Vite generates `.smrt/manifest.json`,
59
59
  `.smrt/smrt-knowledge.json`, `.smrt/register.js`,
@@ -1,4 +1,4 @@
1
1
  {
2
- "@happyvertical/smrt-app-mcp": "^0.53.4",
3
- "@happyvertical/smrt-mcp-openai": "^0.53.4"
2
+ "@happyvertical/smrt-app-mcp": "^0.53.6",
3
+ "@happyvertical/smrt-mcp-openai": "^0.53.6"
4
4
  }
@@ -1,15 +1,11 @@
1
1
  import { createHostedMcpResourceAuth } from '@happyvertical/smrt-app-mcp/auth';
2
2
  import { mountMcpProtectedResourceMetadataRoute } from '@happyvertical/smrt-app-mcp/sveltekit';
3
3
 
4
- import { resolveHostedMcpPrincipal } from '$lib/server/mcp-hosted-principal';
5
4
  import { runtime } from '$lib/server/smrt';
6
5
 
7
6
  const { profile } = await runtime.resolvedRuntime();
8
7
 
9
8
  /** RFC 9728 metadata at exactly the URL hosted bearer challenges advertise (404 locally). */
10
9
  export const GET = mountMcpProtectedResourceMetadataRoute(
11
- createHostedMcpResourceAuth({
12
- profile,
13
- resolvePrincipal: resolveHostedMcpPrincipal,
14
- }),
10
+ createHostedMcpResourceAuth({ profile, runtime }),
15
11
  );
@@ -4,7 +4,6 @@ import { mountMcpAppRoute } from '@happyvertical/smrt-app-mcp/sveltekit';
4
4
  import { openAiDisplayMetadata, withOpenAiEntrypoints } from '@happyvertical/smrt-mcp-openai';
5
5
 
6
6
  import { Item } from '$lib/objects/Item';
7
- import { resolveHostedMcpPrincipal } from '$lib/server/mcp-hosted-principal';
8
7
  import { runtime } from '$lib/server/smrt';
9
8
 
10
9
  const ITEM_RESOURCE_URI = 'ui://smrt-app/v1/items.html';
@@ -41,22 +40,22 @@ const { profile } = await runtime.resolvedRuntime();
41
40
  /**
42
41
  * Stateless SDK-v2 Streamable HTTP MCP endpoint for this app's own `Item`.
43
42
  * 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.
43
+ * permission slugs), or an owner-minted bearer token from `smrt app token`
44
+ * for a local MCP client such as the `smrt-mcp-bridge` stdio bridge. Hosted
45
+ * profiles verify a bearer access token and map it through the runtime's
46
+ * membership-backed `resolveMcpPrincipal` (pass `resolvePrincipal` to
47
+ * override it). A bearer principal is bound into the request permission
48
+ * context (under `database-rls`, its own RLS transaction) with its scopes
49
+ * capped to live permissions. Every principal needs `items.read`, so only
50
+ * read-only tools are published until per-operation authorization exists;
51
+ * browser requests from a foreign `Origin` are refused.
50
52
  */
51
53
  export const POST = mountMcpAppRoute({
52
54
  models: [Item],
53
55
  requiredScopes: ['items.read'],
54
56
  effects: ['read'],
55
57
  smrtOptions: () => ({ db: runtime.databaseConfig() }),
56
- auth: createHostedMcpResourceAuth({
57
- profile,
58
- resolvePrincipal: resolveHostedMcpPrincipal,
59
- }),
58
+ auth: createHostedMcpResourceAuth({ profile, runtime }),
60
59
  bindPrincipal: runtime.runAsPrincipal,
61
60
  workflowTools: [itemsOverview],
62
61
  resources: [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@happyvertical/smrt-template-sveltekit",
3
- "version": "0.53.4",
3
+ "version": "0.53.6",
4
4
  "description": "Minimal SvelteKit project template with s-m-r-t framework integration",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -32,25 +32,27 @@
32
32
  "directory": "packages/template-sveltekit"
33
33
  },
34
34
  "peerDependencies": {
35
- "@happyvertical/smrt-core": "0.53.4"
35
+ "@happyvertical/smrt-core": "0.53.6"
36
36
  },
37
37
  "devDependencies": {
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",
38
+ "@happyvertical/smrt-app-cli": "0.53.6",
39
+ "@happyvertical/smrt-app-mcp": "0.53.6",
40
+ "@happyvertical/smrt-app-runtime": "0.53.6",
41
+ "@happyvertical/smrt-assets": "0.53.6",
42
+ "@happyvertical/smrt-cli": "0.53.6",
43
+ "@happyvertical/smrt-config": "0.53.6",
44
+ "@happyvertical/smrt-jobs": "0.53.6",
45
+ "@happyvertical/smrt-mcp-openai": "0.53.6",
46
+ "@happyvertical/smrt-playbooks": "0.53.6",
47
+ "@happyvertical/smrt-profiles": "0.53.6",
48
+ "@happyvertical/smrt-scanner": "0.53.6",
49
+ "@happyvertical/smrt-svelte": "0.53.6",
50
+ "@happyvertical/smrt-tenancy": "0.53.6",
51
+ "@happyvertical/smrt-ui": "0.53.6",
52
+ "@happyvertical/smrt-users": "0.53.6",
53
+ "@happyvertical/smrt-web": "0.53.6",
53
54
  "@happyvertical/sql": "^0.89.12",
55
+ "@modelcontextprotocol/client": "2.0.0",
54
56
  "@modelcontextprotocol/server": "^2.0.0",
55
57
  "@playwright/test": "^1.63.0",
56
58
  "@sveltejs/adapter-node": "^5.5.7",
@@ -24,10 +24,14 @@ tools, WebMCP definitions, and agent/developer knowledge artifacts.
24
24
  - Server composition (profile, storage custody, owner bootstrap, tenant
25
25
  selection, session, health, diagnostics) comes from
26
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.
27
+ `src/lib/server/smrt.ts`, which holds only `runtime` and its options. Pass
28
+ app-specific behaviour as options (`selectTenant`, `classOverrides`,
29
+ `session`, `prepareDatabase`); do not reimplement hooks, re-add the
30
+ `smrt-register` import (the `smrt()` plugin injects it), or export
31
+ `getCollection` (generated routes import this module and use its
32
+ `runtime.getCollection()`; a legacy export would win, deprecated).
33
+ `runtime.getCollection()`/`runtime.classOptions()` are request-scoped: never
34
+ keep their result beyond the request.
31
35
  - Recover an interrupted unclaimed owner invitation with `pnpm app:stop`, then
32
36
  `pnpm app:recover`, `pnpm app:start`, and `pnpm app:open`; never print or copy
33
37
  its token.
@@ -157,8 +157,14 @@ changes. That helper checks active membership and rotates the session ID; never
157
157
  copy an untrusted header directly into `locals.tenantId` or
158
158
  `enterTenantContext()`.
159
159
 
160
- `getCollection()` and `getSmrtConfig()` in `$lib/server/smrt` return
161
- request-scoped options (under `database-rls` isolation they carry the request
160
+ `src/lib/server/smrt.ts` holds only the runtime and its options. The `smrt()`
161
+ plugin registers the generated objects before it runs, and each generated API
162
+ route imports this module and resolves collections through its exported
163
+ `runtime.getCollection()` (a `getCollection` export here would take precedence,
164
+ deprecated for one release, so do not add one). In the local profile
165
+ the runtime holds the single-writer lease (`acquireWriterLease: false` opts
166
+ out). `runtime.getCollection()` and `runtime.classOptions()` are
167
+ request-scoped (under `database-rls` isolation they carry the request
162
168
  transaction): call them inside a request and never keep the result.
163
169
 
164
170
  Set `TENANT_BASE_DOMAIN` for deployed subdomain routing. The fallback parser is
@@ -185,10 +191,10 @@ import {
185
191
  RoleCollection,
186
192
  syncPermissionCatalog,
187
193
  } from '@happyvertical/smrt-users';
188
- import { getSmrtConfig } from '$lib/server/smrt';
194
+ import { runtime } from '$lib/server/smrt';
189
195
 
190
- await syncPermissionCatalog(getSmrtConfig('Permission'));
191
- const roles = await RoleCollection.create(getSmrtConfig('Role'));
196
+ await syncPermissionCatalog(runtime.classOptions('Permission'));
197
+ const roles = await RoleCollection.create(runtime.classOptions('Role'));
192
198
  await roles.seedSystemRoles({ seedPermissions: true });
193
199
  ```
194
200
 
@@ -219,7 +225,7 @@ export const load: PageServerLoad = async ({ depends, locals }) => {
219
225
  return { items: [] };
220
226
  }
221
227
 
222
- const items = await getCollection<Item>('Item');
228
+ const items = await runtime.getCollection<Item>('Item');
223
229
  const rows = await items.list({ limit: 50 });
224
230
  return {
225
231
  items: rows.flatMap((item) =>
@@ -427,15 +433,30 @@ web/workers and set `SMRT_MAINTENANCE_MODE=true`. For domain-specific
427
433
  transformations, add a `scripts/smrt-portability.mjs` adapter; when present it
428
434
  replaces `smrt app export`/`import`'s built-in one.
429
435
 
430
- ### Hosted MCP authorization
436
+ ### MCP authorization
437
+
438
+ In the `local` profile, the opt-in MCP route accepts two credentials. One is
439
+ the signed browser session. The other is an owner-minted bearer token for a
440
+ local MCP client such as Claude Desktop through `smrt-mcp-bridge`:
441
+
442
+ ```bash
443
+ pnpm exec smrt app token --scopes items.read --label "Claude Desktop"
444
+ pnpm exec smrt app token list
445
+ pnpm exec smrt app token revoke <id>
446
+ ```
447
+
448
+ The token is printed once and only its hash is stored. It is bound to the
449
+ owner and the owner's workspace. It expires after 30 days unless you pass
450
+ `--expires`, up to 365 days. It never carries a permission the owner no longer
451
+ holds. Point the bridge at `/api/mcp` with `<PREFIX>_SERVER_URL` and
452
+ `<PREFIX>_TOKEN`; see the `@happyvertical/smrt-app-cli` README.
431
453
 
432
- The opt-in MCP route uses the local signed session only in the `local` profile.
433
454
  For `self-hosted` and `cloud`, configure HTTPS `SMRT_MCP_RESOURCE`,
434
455
  `SMRT_MCP_ISSUER`, `SMRT_MCP_JWKS_URI`, and space-separated
435
- `SMRT_MCP_SCOPES` values. Bind `resolveHostedMcpPrincipal` in
436
- `src/lib/server/mcp-hosted-principal.ts` to an application-owned lookup that
437
- checks the current account and active tenant membership on every request. It
438
- must return `null` for disabled, revoked, or unmapped identities. The route
439
- does not derive tenant authority from JWT claims, request headers, or tool
440
- arguments; missing configuration or a missing binding fails closed before MCP
441
- dispatch.
456
+ `SMRT_MCP_SCOPES` values. On every request, the runtime maps the verified
457
+ issuer and subject to the user linked through OIDC login. That user must have
458
+ exactly one active tenant membership. Disabled, unmapped, or multi-tenant
459
+ identities are denied. To use a different lookup, pass `resolvePrincipal` to
460
+ `createHostedMcpResourceAuth`. The route never derives tenant authority from
461
+ JWT claims, request headers, or tool arguments. Missing configuration fails
462
+ closed before MCP dispatch.
@@ -39,19 +39,19 @@
39
39
  "svelte-check": "^4.7.6",
40
40
  "typescript": "6.0.3",
41
41
  "vite": "8.3.0",
42
- "@happyvertical/smrt-cli": "^0.53.4"
42
+ "@happyvertical/smrt-cli": "^0.53.6"
43
43
  },
44
44
  "dependencies": {
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",
45
+ "@happyvertical/smrt-app-runtime": "^0.53.6",
46
+ "@happyvertical/smrt-config": "^0.53.6",
47
+ "@happyvertical/smrt-core": "^0.53.6",
48
+ "@happyvertical/smrt-jobs": "^0.53.6",
49
+ "@happyvertical/smrt-profiles": "^0.53.6",
50
+ "@happyvertical/smrt-svelte": "^0.53.6",
51
+ "@happyvertical/smrt-tenancy": "^0.53.6",
52
+ "@happyvertical/smrt-ui": "^0.53.6",
53
+ "@happyvertical/smrt-users": "^0.53.6",
54
+ "@happyvertical/smrt-web": "^0.53.6",
55
55
  "@happyvertical/sql": "^0.89.12",
56
56
  "@modelcontextprotocol/server": "^2.0.0"
57
57
  }
@@ -5,27 +5,10 @@
5
5
  * See: https://github.com/happyvertical/smrt
6
6
  */
7
7
 
8
- import {
9
- resolveApplicationId,
10
- resolveLocalRuntimePaths,
11
- } from '@happyvertical/smrt-app-runtime';
12
-
13
8
  const profile = (process.env.SMRT_RUNTIME_PROFILE || 'local') as
14
9
  | 'local'
15
10
  | 'self-hosted'
16
11
  | 'cloud';
17
- const appId = resolveApplicationId({
18
- sourceRoot: process.cwd(),
19
- explicitId: process.env.SMRT_APP_ID,
20
- });
21
- const localDatabase =
22
- profile === 'local'
23
- ? resolveLocalRuntimePaths({
24
- appId,
25
- dataDirectory: process.env.SMRT_DATA_DIR,
26
- sourceRoot: process.cwd(),
27
- }).database
28
- : undefined;
29
12
 
30
13
  export default {
31
14
  // This is the single infrastructure selector for the application. Domain
@@ -58,22 +41,8 @@ export default {
58
41
  },
59
42
  },
60
43
 
61
- // Package-specific configuration
62
- packages: {
63
- // CLI configuration
64
- cli: {
65
- database: {
66
- type:
67
- profile === 'local' ? 'sqlite' : 'postgres',
68
- // `smrt app` sets DATABASE_URL to the profile-owned data path before
69
- // migrations. Deployed profiles require an operator-owned URL.
70
- url:
71
- profile === 'local'
72
- ? process.env.DATABASE_URL || localDatabase
73
- : process.env.DATABASE_URL,
74
- },
75
- verbose: false,
76
- },
77
-
78
- },
44
+ // No `packages.cli.database`: `smrt app setup` / `smrt app migrate` hand
45
+ // `smrt db:migrate` the profile-owned database through DATABASE_URL and
46
+ // DATABASE_TYPE (local: the SQLite file in the user data root; deployed: the
47
+ // operator's URL). See the smrt-cli README for the precedence.
79
48
  };
@@ -1,87 +1,23 @@
1
1
  /**
2
- * The application's SMRT runtime and collection access.
2
+ * The application's SMRT runtime: app options only.
3
3
  *
4
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.
5
+ * PostgreSQL), storage custody, the owner bootstrap, tenant selection, the
6
+ * signed session, and (local profile) the single-writer lease shared with
7
+ * `smrt app` operations; `src/hooks.server.ts` mounts its `handle` and `init`.
8
+ * The smrt() Vite plugin registers the generated objects before this module
9
+ * runs, and generated API routes resolve collections through
10
+ * `runtime.getCollection()`.
11
+ *
12
+ * `runtime.getCollection()` and `runtime.classOptions()` are request-scoped:
13
+ * under `database-rls` isolation they carry the request's transaction, so
14
+ * call them per request and never keep the result.
8
15
  */
9
16
 
10
- import { existsSync } from 'node:fs';
11
- import { join } from 'node:path';
12
-
13
- import {
14
- acquireWriterLease,
15
- createProviderReadinessProbe,
16
- prepareApplicationStateRoot,
17
- } from '@happyvertical/smrt-app-runtime';
17
+ import { createProviderReadinessProbe } from '@happyvertical/smrt-app-runtime';
18
18
  import { createSmrtSvelteKitRuntime } from '@happyvertical/smrt-app-runtime/sveltekit';
19
- import {
20
- ObjectRegistry,
21
- type SmrtClassOptions,
22
- type SmrtObject,
23
- } from '@happyvertical/smrt-core';
24
- import { loadManifestFromPathSync } from '@happyvertical/smrt-core/manifest';
25
-
26
- // Import all SMRT objects to register them.
27
- import '../objects/index.js';
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
-
54
- // Upgrade those registrations to package-qualified identities
55
- // (`<package>:<Class>`) — required to link each class to its scanned field
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.
59
- try {
60
- // @ts-ignore generated by the smrt() Vite plugin before a build starts
61
- await import('./smrt-register.js');
62
- } catch (error) {
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;
67
- }
68
-
69
- // Hydrate field metadata for this app's own objects from the local manifest
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.
72
- const localManifestPath = join(process.cwd(), '.smrt', 'manifest.json');
73
- if (existsSync(localManifestPath)) {
74
- loadManifestFromPathSync(localManifestPath);
75
- }
76
19
 
77
20
  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
21
  // Deployed profiles prove authentication, asset, and secret readiness
86
22
  // through the installed modules named by SMRT_*_READINESS_MODULE.
87
23
  providerReadiness: createProviderReadinessProbe,
@@ -89,23 +25,3 @@ export const runtime = createSmrtSvelteKitRuntime({
89
25
  // AuditLog: { db: { type: 'postgres', url: process.env.AUDIT_DB_URL! } }
90
26
  classOverrides: {},
91
27
  });
92
-
93
- /**
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.
96
- */
97
- export function getSmrtConfig(className: string): SmrtClassOptions {
98
- return runtime.classOptions(className);
99
- }
100
-
101
- /**
102
- * Get a collection for a SMRT class, scoped to the current request.
103
- *
104
- * const items = await getCollection<Item>('Item');
105
- */
106
- export async function getCollection<T extends SmrtObject>(className: string) {
107
- return await ObjectRegistry.getCollection<T>(
108
- className,
109
- runtime.classOptions(className),
110
- );
111
- }
@@ -2,7 +2,7 @@ import { assertOperationPermission } from '@happyvertical/smrt-users';
2
2
  import { fail } from '@sveltejs/kit';
3
3
 
4
4
  import type { Item } from '$lib/objects/Item';
5
- import { getCollection, getSmrtConfig } from '$lib/server/smrt';
5
+ import { runtime } from '$lib/server/smrt';
6
6
  import type { Actions, PageServerLoad } from './$types';
7
7
 
8
8
  const READ_PERMISSION = 'items.read';
@@ -38,7 +38,7 @@ export const load: PageServerLoad = async ({ depends, locals }) => {
38
38
  }
39
39
 
40
40
  try {
41
- const items = await getCollection<Item>('Item');
41
+ const items = await runtime.getCollection<Item>('Item');
42
42
  const rows = await items.list({
43
43
  orderBy: 'created_at DESC',
44
44
  limit: 50,
@@ -72,7 +72,7 @@ export const actions: Actions = {
72
72
  }
73
73
 
74
74
  const decision = await assertOperationPermission({
75
- ...getSmrtConfig('Item'),
75
+ ...runtime.classOptions('Item'),
76
76
  action: 'create',
77
77
  collection: 'items',
78
78
  onDeny: 'return',
@@ -91,7 +91,7 @@ export const actions: Actions = {
91
91
  }
92
92
 
93
93
  try {
94
- const items = await getCollection<Item>('Item');
94
+ const items = await runtime.getCollection<Item>('Item');
95
95
  await items.create({ title });
96
96
  return { created: title };
97
97
  } catch (error) {
@@ -1,23 +1,9 @@
1
- import { rmSync } from 'node:fs';
2
- import { join } from 'node:path';
3
-
4
- import { resolveApplicationStateRoot } from '@happyvertical/smrt-app-runtime';
5
1
  import { createOwnerSetupPage } from '@happyvertical/smrt-app-runtime/sveltekit';
6
2
 
7
3
  import { runtime } from '$lib/server/smrt';
8
4
 
9
- /** Local-profile, loopback-only, single-use owner bootstrap. */
10
- export const { load, actions } = createOwnerSetupPage(runtime, {
11
- // `pnpm app:setup` leaves the invitation in mode-0600 handoff files; once
12
- // the owner exists they only hold a token that now fails closed.
13
- onOwnerClaimed() {
14
- const stateRoot = resolveApplicationStateRoot({
15
- appId: runtime.applicationId(),
16
- dataDirectory: process.env.SMRT_DATA_DIR,
17
- sourceRoot: process.cwd(),
18
- });
19
- for (const file of ['onboarding.json', 'onboarding-launch.html']) {
20
- rmSync(join(stateRoot, file), { force: true });
21
- }
22
- },
23
- });
5
+ /**
6
+ * Local-profile, loopback-only, single-use owner bootstrap. The runtime
7
+ * removes the `pnpm app:setup` onboarding hand-off files once the claim commits.
8
+ */
9
+ export const { load, actions } = createOwnerSetupPage(runtime);
@@ -19,22 +19,22 @@ export default {
19
19
 
20
20
  // Dependencies to add to generated project
21
21
  dependencies: {
22
- '@happyvertical/smrt-app-runtime': '^0.53.4',
23
- '@happyvertical/smrt-config': '^0.53.4',
24
- '@happyvertical/smrt-core': '^0.53.4',
25
- '@happyvertical/smrt-jobs': '^0.53.4',
26
- '@happyvertical/smrt-profiles': '^0.53.4',
27
- '@happyvertical/smrt-svelte': '^0.53.4',
28
- '@happyvertical/smrt-tenancy': '^0.53.4',
29
- '@happyvertical/smrt-ui': '^0.53.4',
30
- '@happyvertical/smrt-users': '^0.53.4',
31
- '@happyvertical/smrt-web': '^0.53.4',
22
+ '@happyvertical/smrt-app-runtime': '^0.53.6',
23
+ '@happyvertical/smrt-config': '^0.53.6',
24
+ '@happyvertical/smrt-core': '^0.53.6',
25
+ '@happyvertical/smrt-jobs': '^0.53.6',
26
+ '@happyvertical/smrt-profiles': '^0.53.6',
27
+ '@happyvertical/smrt-svelte': '^0.53.6',
28
+ '@happyvertical/smrt-tenancy': '^0.53.6',
29
+ '@happyvertical/smrt-ui': '^0.53.6',
30
+ '@happyvertical/smrt-users': '^0.53.6',
31
+ '@happyvertical/smrt-web': '^0.53.6',
32
32
  '@happyvertical/sql': '^0.89.12',
33
33
  '@modelcontextprotocol/server': '^2.0.0',
34
34
  },
35
35
 
36
36
  devDependencies: {
37
- '@happyvertical/smrt-cli': '^0.53.4',
37
+ '@happyvertical/smrt-cli': '^0.53.6',
38
38
  '@sveltejs/adapter-node': '^5.5.7',
39
39
  '@sveltejs/kit': '2.70.3',
40
40
  '@sveltejs/vite-plugin-svelte': '^7.3.0',
@@ -1,12 +0,0 @@
1
- import type { McpVerifiedIdentity, McpPrincipalMapping } from '@happyvertical/smrt-app-mcp/auth';
2
-
3
- /**
4
- * Deployments must replace this binding with a database-backed current-account
5
- * and active-membership lookup. Token claims never select a tenant.
6
- */
7
- export type HostedMcpPrincipalResolver = (
8
- identity: McpVerifiedIdentity,
9
- ) => Promise<McpPrincipalMapping | null>;
10
-
11
- export const resolveHostedMcpPrincipal: HostedMcpPrincipalResolver | undefined =
12
- undefined;