@objectstack/runtime 7.9.0 → 8.0.1

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/dist/index.d.cts CHANGED
@@ -6,12 +6,11 @@ import { ClusterCapabilityConfigInput, ExecutionContext } from '@objectstack/spe
6
6
  import { z } from 'zod';
7
7
  import * as Contracts from '@objectstack/spec/contracts';
8
8
  import { ISeedLoaderService, IDataEngine, IMetadataService } from '@objectstack/spec/contracts';
9
- import { SeedLoaderRequest, SeedLoaderResult, ObjectDependencyGraph, Dataset, SeedLoaderConfigInput, ExpressionBody, ScriptBody, HookBody, Hook } from '@objectstack/spec/data';
9
+ import { SeedLoaderRequest, SeedLoaderResult, ObjectDependencyGraph, Seed, SeedLoaderConfigInput, ExpressionBody, ScriptBody, HookBody, Hook } from '@objectstack/spec/data';
10
10
  import { SchemaDiffEntry } from '@objectstack/spec/shared';
11
11
  import { MetricsRegistry, ErrorReporter } from '@objectstack/observability';
12
12
  export { CapturedError, ErrorReporter, InMemoryErrorReporter, InMemoryMetricsRegistry, MetricSample, MetricsRegistry, NoopErrorReporter, NoopMetricsRegistry, OBSERVABILITY_ERRORS_SERVICE, OBSERVABILITY_METRICS_SERVICE, RUNTIME_METRICS } from '@objectstack/observability';
13
13
  import { MiddlewareConfig, MiddlewareType } from '@objectstack/spec/system';
14
- import { EnvironmentArtifact } from '@objectstack/spec/cloud';
15
14
  export { RestApiPluginConfig, RestServer, RouteEntry, RouteGroupBuilder, RouteManager, createRestApiPlugin } from '@objectstack/rest';
16
15
  export { _resetEnvDeprecationWarnings, readEnvWithDeprecation } from '@objectstack/types';
17
16
 
@@ -316,7 +315,7 @@ interface Logger {
316
315
  * - Topological dependency ordering (parents before children)
317
316
  * - Multi-pass loading for circular references
318
317
  * - Dry-run validation mode
319
- * - Upsert support honoring DatasetSchema mode
318
+ * - Upsert support honoring SeedSchema mode
320
319
  * - Actionable error reporting
321
320
  */
322
321
  declare class SeedLoaderService implements ISeedLoaderService {
@@ -326,7 +325,7 @@ declare class SeedLoaderService implements ISeedLoaderService {
326
325
  constructor(engine: IDataEngine, metadata: IMetadataService, logger: Logger);
327
326
  load(request: SeedLoaderRequest): Promise<SeedLoaderResult>;
328
327
  buildDependencyGraph(objectNames: string[]): Promise<ObjectDependencyGraph>;
329
- validate(datasets: Dataset[], config?: SeedLoaderConfigInput): Promise<SeedLoaderResult>;
328
+ validate(datasets: Seed[], config?: SeedLoaderConfigInput): Promise<SeedLoaderResult>;
330
329
  private loadDataset;
331
330
  private resolveFromDatabase;
332
331
  private resolveDeferredUpdates;
@@ -893,97 +892,54 @@ declare class HttpServer implements IHttpServer {
893
892
  }
894
893
 
895
894
  /**
896
- * Factory contract for instantiating a per-project {@link ObjectKernel}.
895
+ * Per-project driver registry contract.
896
+ *
897
+ * Resolves a project (by hostname or ID) and produces an instantiated
898
+ * `IDataDriver` bound to that project's physical database. Concrete
899
+ * implementations sit on top of either:
900
+ * - the ObjectStack Cloud HTTP API (see {@link ArtifactEnvironmentRegistry})
901
+ * - a local file artifact (see {@link FileArtifactApiClient} +
902
+ * {@link ArtifactEnvironmentRegistry})
897
903
  *
898
- * Given a `environmentId`, the factory is expected to:
899
- * 1. Read control-plane metadata (`sys_environment` + credentials + subscribed packages).
900
- * 2. Construct a fresh `ObjectKernel` with project-scoped driver + plugins + Apps.
901
- * 3. Return a **bootstrapped** kernel ready to serve requests.
904
+ * The contract lives in `@objectstack/runtime` so the runtime can
905
+ * express "fetch artifact + boot per-project kernel" without taking a
906
+ * dependency on the cloud control plane.
902
907
  */
903
- interface EnvironmentKernelFactory {
904
- create(environmentId: string): Promise<ObjectKernel>;
905
- }
906
- interface KernelManagerConfig {
907
- factory: EnvironmentKernelFactory;
908
- /** Maximum number of kernels to keep resident. Defaults to 32. */
909
- maxSize?: number;
910
- /**
911
- * Time-to-live (ms). Kernels idle longer than this are evicted on next
912
- * access. `0` disables TTL expiry. Defaults to 15 minutes.
913
- */
914
- ttlMs?: number;
915
- /**
916
- * Optional logger (duck-typed). Falls back to `console` when omitted.
917
- */
918
- logger?: {
919
- info?: (...a: any[]) => void;
920
- warn?: (...a: any[]) => void;
921
- error?: (...a: any[]) => void;
922
- };
923
- /**
924
- * Optional upstream-change detector. When set, every cache hit older
925
- * than `staleCheckIntervalMs` triggers this probe before returning the
926
- * cached kernel. Returning `true` evicts the kernel and forces a
927
- * rebuild, so changes to the control-plane state that don't reach
928
- * this process via push (marketplace installs, artifact republish,
929
- * etc.) become visible without waiting for the LRU TTL to expire.
930
- *
931
- * The probe should be cheap (single small GET). Errors thrown here
932
- * are caught and treated as "still fresh" so a brief upstream
933
- * outage doesn't churn every cached kernel — the worst case is
934
- * stale-by-`ttlMs`, which is what we had before adding the probe.
935
- *
936
- * `builtAtMs` is the kernel's `createdAt` time so the probe can
937
- * compare against an upstream "last changed at" timestamp.
938
- */
939
- freshnessProbe?: (environmentId: string, builtAtMs: number) => Promise<boolean>;
940
- /**
941
- * Minimum gap between successive freshness probes for the same env.
942
- * Defaults to 10 seconds — enough to avoid hammering the control
943
- * plane on tight render loops while still keeping the user's
944
- * post-install refresh perceived as immediate.
945
- */
946
- staleCheckIntervalMs?: number;
947
- }
908
+
909
+ type IDataDriver = Contracts.IDataDriver;
948
910
  /**
949
- * LRU + TTL cache of per-project {@link ObjectKernel} instances.
911
+ * Multi-tenant kernel router contract.
950
912
  *
951
- * Implements ADR-0003 multi-kernel scheduling: each project gets an
952
- * isolated kernel (App/plugin/metadata namespaces) that is lazily built
953
- * on first request and evicted under memory / idle pressure. Concurrent
954
- * `getOrCreate()` calls for the same environmentId share a single in-flight
955
- * factory invocation (singleflight).
913
+ * The HTTP dispatcher uses this optional seam to resolve a per-environment
914
+ * kernel when serving a multi-tenant runtime. The framework only depends on
915
+ * the *interface* the concrete LRU/TTL implementation (and everything else
916
+ * multi-tenant: hostname resolution, artifact fetching, per-env kernel
917
+ * construction) lives in the cloud distribution
918
+ * (`@objectstack/objectos-runtime`), not here.
956
919
  */
957
- declare class KernelManager {
958
- private readonly factory;
959
- private readonly maxSize;
960
- private readonly ttlMs;
961
- private readonly logger;
962
- private readonly cache;
963
- private readonly pending;
964
- private readonly freshnessProbe?;
965
- private readonly staleCheckIntervalMs;
966
- constructor(config: KernelManagerConfig);
967
- /** Returns the currently cached environmentIds (ordered by insertion). */
968
- keys(): string[];
969
- /** Cache size for diagnostics. */
970
- get size(): number;
971
- /**
972
- * Resolve or construct the kernel for `environmentId`.
973
- *
974
- * - Cache hit (fresh): bumps `lastAccess` and returns immediately.
975
- * - Cache hit (TTL expired): evicts then falls through to factory.
976
- * - Cache miss: dedupes concurrent callers through `pending`.
977
- */
920
+ interface KernelManager {
921
+ /** Resolve (building + caching on first use) the kernel for an environment. */
978
922
  getOrCreate(environmentId: string): Promise<ObjectKernel>;
923
+ }
924
+ interface EnvironmentDriverRegistry {
925
+ /** Resolve a project by hostname. Returns `null` when unknown. */
926
+ resolveByHostname(host: string): Promise<{
927
+ environmentId: string;
928
+ driver: IDataDriver;
929
+ } | null>;
930
+ /** Resolve a project's driver by ID. Returns `null` when unknown. */
931
+ resolveById(environmentId: string): Promise<IDataDriver | null>;
979
932
  /**
980
- * Evict the kernel for `environmentId` and invoke `kernel.shutdown()`.
981
- * No-op when the entry is absent.
933
+ * Look up the cached project row + driver by ID without triggering a
934
+ * remote/file fetch. Returns the full cached entry when fresh.
982
935
  */
983
- evict(environmentId: string): Promise<void>;
984
- /** Evict all resident kernels. Used on runtime shutdown. */
985
- evictAll(): Promise<void>;
986
- private enforceMaxSize;
936
+ peekById(environmentId: string): {
937
+ environmentId: string;
938
+ driver: IDataDriver;
939
+ project: any;
940
+ } | null;
941
+ /** Drop cached entries for the given project. */
942
+ invalidate(environmentId: string): void;
987
943
  }
988
944
 
989
945
  /** Minimal local interface — full EnvironmentScopeManager was removed in Phase R. */
@@ -1083,6 +1039,62 @@ declare class HttpDispatcher {
1083
1039
  * @param scopeId - Optional project ID for scoped service resolution (SharedProjectPlugin mode)
1084
1040
  */
1085
1041
  private callData;
1042
+ /**
1043
+ * Handle an MCP request over the Streamable HTTP transport (`/mcp`).
1044
+ *
1045
+ * Gating + auth (fail-closed):
1046
+ * - **opt-in**: only served when `OS_MCP_SERVER_ENABLED=true` (single-env
1047
+ * runtime). Multi-tenant cloud overrides this gate per env. When off we
1048
+ * return 404 so the surface isn't advertised.
1049
+ * - **auth**: requires a principal already resolved by
1050
+ * `resolveExecutionContext` (the `sys_api_key` Bearer/header path or a
1051
+ * session). Anonymous → 401.
1052
+ *
1053
+ * Execution: the MCP runtime builds a stateless per-request server whose
1054
+ * object-CRUD tools run through {@link callData} bound to THIS request's
1055
+ * ExecutionContext — i.e. the exact permission + RLS path the REST API
1056
+ * uses. An external agent can never exceed the key's authority.
1057
+ */
1058
+ handleMcp(body: any, context: HttpProtocolContext): Promise<HttpDispatcherResult>;
1059
+ /** Whether the MCP HTTP surface is opted in for this single-env runtime. */
1060
+ private static isMcpEnabled;
1061
+ /**
1062
+ * Normalise the inbound request into a Web-standard `Request` for the MCP
1063
+ * transport. Accepts an already-Web `Request`, or a node/Hono-style req
1064
+ * (plain `headers` object, path-only `url`). Returns undefined only if the
1065
+ * shape is unusable. The body is carried separately via `parsedBody`, so a
1066
+ * GET/DELETE (no body) and a POST (JSON-RPC) both normalise cleanly.
1067
+ */
1068
+ private toMcpWebRequest;
1069
+ /**
1070
+ * Build a principal-bound {@link McpDataBridge}: every method runs AS the
1071
+ * request's ExecutionContext through {@link callData} (RLS/permissions) and
1072
+ * the per-env metadata service. Keeps the MCP tool layer free of any direct
1073
+ * engine access.
1074
+ */
1075
+ private buildMcpBridge;
1076
+ /**
1077
+ * Generate a `sys_api_key` and return the raw secret EXACTLY ONCE
1078
+ * (`POST /keys`). This is the only mint path — the raw key is never stored
1079
+ * (only its sha256 hash) and never re-displayable.
1080
+ *
1081
+ * Security (zero-tolerance):
1082
+ * - Requires an authenticated principal; `user_id` is PINNED to that
1083
+ * caller and is NEVER read from the request body (no impersonation).
1084
+ * - Body is whitelisted to `name` (+ optional `expires_at`); any
1085
+ * `key` / `id` / `user_id` / `revoked` in the body is ignored, so a
1086
+ * caller cannot forge a known-secret or escalate.
1087
+ * - `scopes` are intentionally NOT accepted from the body in v1: the
1088
+ * verify path ADDS scopes to the principal's permissions, so honouring
1089
+ * arbitrary body scopes would be an escalation vector. A generated key
1090
+ * therefore acts exactly AS the caller (via `user_id` resolution).
1091
+ * Narrowing/scoped keys need subset-enforcement — deferred.
1092
+ * - The raw key and its hash never enter logs or error messages.
1093
+ * - The row is written with an elevated `{ isSystem: true }` context
1094
+ * because `sys_api_key` is protection-locked; safe because the row's
1095
+ * contents are fully server-controlled (user_id pinned to caller).
1096
+ */
1097
+ handleKeys(method: string, body: any, context: HttpProtocolContext): Promise<HttpDispatcherResult>;
1086
1098
  /**
1087
1099
  * Parse a project UUID out of a scoped URL path such as
1088
1100
  * `/api/v1/environments/abc-123/data/task` or `/projects/abc-123/meta`.
@@ -1158,6 +1170,7 @@ declare class HttpDispatcher {
1158
1170
  notifications: string | undefined;
1159
1171
  ai: string | undefined;
1160
1172
  i18n: string | undefined;
1173
+ mcp: string | undefined;
1161
1174
  };
1162
1175
  endpoints: {
1163
1176
  data: string;
@@ -1174,6 +1187,7 @@ declare class HttpDispatcher {
1174
1187
  notifications: string | undefined;
1175
1188
  ai: string | undefined;
1176
1189
  i18n: string | undefined;
1190
+ mcp: string | undefined;
1177
1191
  };
1178
1192
  features: {
1179
1193
  graphql: boolean;
@@ -1523,6 +1537,18 @@ declare class HttpDispatcher {
1523
1537
  * Physical database addressing (database_url, database_driver, etc.)
1524
1538
  * is stored directly on the sys_environment row.
1525
1539
  */
1540
+ /**
1541
+ * Apply just-published `seed` metadata: load each seed's rows into its
1542
+ * target object so publishing a seed draft makes the data live (the runtime
1543
+ * counterpart to staging it). Reads each seed body via the protocol, then
1544
+ * runs the {@link SeedLoaderService} for the active org. Best-effort and
1545
+ * idempotent (upsert) — callers must never let this fail the publish.
1546
+ *
1547
+ * Lives at the runtime layer (not in the objectql publish primitive)
1548
+ * because the seed loader needs the data engine + metadata service, which
1549
+ * objectql cannot depend on without a layering cycle.
1550
+ */
1551
+ private applyPublishedSeeds;
1526
1552
  /**
1527
1553
  * Resolve the calling user id from the request session, if any.
1528
1554
  * Returns `undefined` for anonymous calls or when auth is not wired up.
@@ -1737,316 +1763,6 @@ declare function readArtifactSource(pathOrUrl: string, opts?: {
1737
1763
  declare function loadArtifactBundle(absArtifactPath: string, opts?: LoadArtifactBundleOptions): Promise<any | null>;
1738
1764
  declare function mergeRuntimeModule(bundle: any, artifactAbsPath: string, tag?: string): Promise<void>;
1739
1765
 
1740
- /**
1741
- * Artifact API client.
1742
- *
1743
- * HTTP client that talks to the ObjectStack control plane (e.g.
1744
- * `apps/cloud`) to resolve hostnames to projects and to download a
1745
- * project's compiled artifact.
1746
- *
1747
- * The control plane is expected to expose two endpoints:
1748
- *
1749
- * GET {controlPlaneUrl}/api/v1/cloud/resolve-hostname?host={hostname}
1750
- * → { environmentId: string, organizationId?: string, runtime?: EnvironmentRuntimeConfig }
1751
- *
1752
- * GET {controlPlaneUrl}/api/v1/cloud/environments/:environmentId/artifact
1753
- * → EnvironmentArtifactResponse (EnvironmentArtifact + optional `runtime` block)
1754
- *
1755
- * Both endpoints accept an optional `Authorization: Bearer <apiKey>`.
1756
- *
1757
- * Responses are cached in-memory with a TTL so each kernel-manager
1758
- * miss does not produce an extra HTTP round trip. Concurrent callers
1759
- * for the same key share a single in-flight promise (singleflight).
1760
- */
1761
-
1762
- /**
1763
- * Per-project runtime config injected by the control plane alongside
1764
- * the artifact. Carries the physical database URL the runtime should
1765
- * connect to (this is *not* part of the developer-authored compiled
1766
- * artifact — the control plane mints it when serving the API).
1767
- */
1768
- interface EnvironmentRuntimeConfig {
1769
- organizationId?: string;
1770
- hostname?: string;
1771
- /** Driver type — e.g. `sqlite`, `postgres`, `turso`, `memory`. */
1772
- databaseDriver: string;
1773
- /** Driver-specific connection URL. */
1774
- databaseUrl: string;
1775
- /** Optional auth token (e.g. for libSQL/Turso). */
1776
- databaseAuthToken?: string;
1777
- /**
1778
- * Project-level metadata captured by the control plane at create time
1779
- * (e.g. `ownerSeed`, `orgSeed`). Forwarded to the runtime so cold-boot
1780
- * seed replay can mirror the cloud org + owner into the project DB
1781
- * before the user's first SSO callback arrives.
1782
- */
1783
- metadata?: Record<string, unknown>;
1784
- }
1785
- /**
1786
- * Hostname resolution response.
1787
- */
1788
- interface ResolvedHostname {
1789
- environmentId: string;
1790
- organizationId?: string;
1791
- /** Optional runtime config — when present, callers can skip the artifact fetch's runtime block. */
1792
- runtime?: EnvironmentRuntimeConfig;
1793
- }
1794
- /**
1795
- * Artifact response wrapping the spec's `EnvironmentArtifact` envelope plus
1796
- * an optional `runtime` block carrying the project's database
1797
- * connection details.
1798
- */
1799
- interface EnvironmentArtifactResponse extends EnvironmentArtifact {
1800
- runtime?: EnvironmentRuntimeConfig;
1801
- }
1802
- interface ArtifactApiClientConfig {
1803
- /** Control-plane base URL (no trailing slash). */
1804
- controlPlaneUrl: string;
1805
- /** Optional bearer token. */
1806
- apiKey?: string;
1807
- /** Cache TTL in ms. Default: 5 min. */
1808
- cacheTtlMs?: number;
1809
- /** Timeout for control-plane HTTP calls in ms. Default: 10s. */
1810
- requestTimeoutMs?: number;
1811
- /** Optional fetch override (testing). */
1812
- fetch?: typeof fetch;
1813
- /** Optional logger. */
1814
- logger?: {
1815
- info?: (...a: any[]) => void;
1816
- warn?: (...a: any[]) => void;
1817
- error?: (...a: any[]) => void;
1818
- };
1819
- }
1820
- declare class ArtifactApiClient {
1821
- private readonly base;
1822
- private readonly apiKey?;
1823
- private readonly cacheTtlMs;
1824
- private readonly requestTimeoutMs;
1825
- private readonly fetchImpl;
1826
- private readonly logger;
1827
- private readonly hostnameCache;
1828
- private readonly artifactCache;
1829
- private readonly pendingHostname;
1830
- private readonly pendingArtifact;
1831
- constructor(config: ArtifactApiClientConfig);
1832
- /**
1833
- * Resolve a hostname to its project. Returns `null` on 404 or
1834
- * malformed responses. Errors (network / 5xx) are thrown so
1835
- * upstream callers can retry.
1836
- */
1837
- resolveHostname(host: string): Promise<ResolvedHostname | null>;
1838
- /**
1839
- * Fetch the compiled artifact for a project.
1840
- *
1841
- * When `opts.commit` is set, requests that specific revision via the
1842
- * existing `?commit=` query param. Different commits are cached
1843
- * independently (the cache key includes the commit id) so the preview
1844
- * runtime can hold multiple versions in memory simultaneously.
1845
- */
1846
- fetchArtifact(environmentId: string, opts?: {
1847
- commit?: string;
1848
- }): Promise<EnvironmentArtifactResponse | null>;
1849
- /**
1850
- * Resolve an 8-hex project short id (first 8 hex chars of the UUID,
1851
- * dashes stripped) to the full environmentId. Used by the preview
1852
- * runtime, which encodes project ids in subdomains.
1853
- *
1854
- * Returns `null` on 404 or ambiguity (the control plane returns 409
1855
- * if the prefix matches more than one project).
1856
- */
1857
- lookupProjectByShortId(shortId: string): Promise<{
1858
- environmentId: string;
1859
- organizationId?: string;
1860
- } | null>;
1861
- /**
1862
- * Fetch the head commit of a branch. Returns the commit id (and the
1863
- * matching revision row's `published_at` for cache-validity checks).
1864
- * Reuses the existing `GET /cloud/environments/:id/branches` endpoint.
1865
- */
1866
- fetchBranchHead(environmentId: string, branchName: string): Promise<{
1867
- commitId: string;
1868
- publishedAt?: string | null;
1869
- } | null>;
1870
- /**
1871
- * Cheap freshness probe — returns the env's `last_published_at`
1872
- * (and best-effort current commit) without rebuilding the artifact.
1873
- * Used by `KernelManager` on cache hits to detect when a per-env
1874
- * kernel has been invalidated by an upstream change (marketplace
1875
- * install/uninstall, artifact publish) so it can be rebuilt
1876
- * without waiting for the 15-minute LRU TTL to expire.
1877
- *
1878
- * Returns `null` on definitive 404 / unknown env. Errors propagate
1879
- * (caller decides whether to treat unreachable cloud as fresh or
1880
- * stale — typically fresh, so a brief outage doesn't churn every
1881
- * cached kernel).
1882
- */
1883
- getFreshness(environmentId: string): Promise<{
1884
- environmentId: string;
1885
- lastPublishedAt: string | null;
1886
- commitId: string | null;
1887
- } | null>;
1888
- /** Drop cached entries for a project (and any matching hostname). */
1889
- invalidate(environmentId: string): void;
1890
- /** Drop everything. Used on shutdown / hot-reload. */
1891
- clear(): void;
1892
- private request;
1893
- private buildHeaders;
1894
- }
1895
-
1896
- interface FileArtifactApiClientConfig {
1897
- /**
1898
- * Path to a compiled artifact JSON file (`dist/objectstack.json`).
1899
- * Resolved against `process.cwd()` when relative. Defaults to
1900
- * `<cwd>/dist/objectstack.json`.
1901
- */
1902
- artifactPath?: string;
1903
- /**
1904
- * Project id every hostname maps to. Defaults to
1905
- * `process.env.OS_ENVIRONMENT_ID` or `'proj_local'`.
1906
- */
1907
- environmentId?: string;
1908
- /**
1909
- * Organization id surfaced alongside the project. Defaults to
1910
- * `process.env.OS_ORGANIZATION_ID` or `'org_local'`.
1911
- */
1912
- organizationId?: string;
1913
- /**
1914
- * Override runtime config. When unset, the client tries to derive
1915
- * one from the artifact's `datasources` array; if that fails it
1916
- * falls back to a local-file SQLite DB at
1917
- * `<cwd>/.objectstack/data/<environmentId>.db`.
1918
- */
1919
- runtime?: EnvironmentRuntimeConfig;
1920
- /**
1921
- * Reload the artifact on every fetch instead of caching the first
1922
- * read. Useful when iterating on a project's metadata without
1923
- * restarting objectos. Defaults to `true` for dev ergonomics.
1924
- */
1925
- watch?: boolean;
1926
- /** Optional logger. */
1927
- logger?: {
1928
- info?: (...a: any[]) => void;
1929
- warn?: (...a: any[]) => void;
1930
- error?: (...a: any[]) => void;
1931
- };
1932
- }
1933
- declare class FileArtifactApiClient {
1934
- private readonly artifactPath;
1935
- private readonly environmentId;
1936
- private readonly organizationId;
1937
- private readonly overrideRuntime?;
1938
- private readonly watch;
1939
- private readonly logger;
1940
- private cached?;
1941
- constructor(config?: FileArtifactApiClientConfig);
1942
- resolveHostname(_host: string): Promise<ResolvedHostname | null>;
1943
- fetchArtifact(_environmentId: string, _opts?: {
1944
- commit?: string;
1945
- }): Promise<EnvironmentArtifactResponse | null>;
1946
- lookupProjectByShortId(_shortId: string): Promise<{
1947
- environmentId: string;
1948
- organizationId?: string;
1949
- } | null>;
1950
- fetchBranchHead(_environmentId: string, _branchName: string): Promise<{
1951
- commitId: string;
1952
- publishedAt?: string | null;
1953
- } | null>;
1954
- invalidate(_environmentId: string): void;
1955
- clear(): void;
1956
- private loadArtifact;
1957
- private readRuntimeFromArtifact;
1958
- private deriveRuntimeFromMetadata;
1959
- private defaultLocalSqliteRuntime;
1960
- }
1961
-
1962
- /**
1963
- * createObjectOSStack
1964
- *
1965
- * ObjectOS pure-runtime stack — no control-plane database, no auth /
1966
- * security / audit / tenant plugins. The host kernel registers:
1967
- *
1968
- * - A minimal engine triplet (ObjectQL + in-memory DriverPlugin +
1969
- * MetadataPlugin) so CLI auto-injected plugins (Setup, Studio,
1970
- * Dispatcher, REST) and the runtime can boot. The host kernel itself
1971
- * never reads or writes business data — every record query is routed
1972
- * to a per-project kernel built from a remote artifact.
1973
- * - The `env-registry` and `kernel-manager` services, so the runtime's
1974
- * HTTP dispatcher can resolve hostnames and dispatch every request
1975
- * to the matching project kernel.
1976
- *
1977
- * Invoked by `createRuntimeStack()` whenever `OS_CLOUD_URL`
1978
- * (or `config.controlPlaneUrl`) is set. The same plugin shape is returned
1979
- * as `createCloudStack()` so host configs can swap stacks transparently.
1980
- */
1981
-
1982
- interface ObjectOSStackConfig {
1983
- /**
1984
- * Control-plane base URL (HTTP) or a sentinel of `'file'` for the
1985
- * local file-backed dev mode. Required unless `client` is supplied.
1986
- *
1987
- * - `http(s)://…` — talk to a real ObjectStack Cloud control plane
1988
- * over HTTP and resolve hostnames via its `/cloud/*` API.
1989
- * - `'file'` — load a single project from a local
1990
- * `dist/objectstack.json` (or `fileConfig.artifactPath`). Every
1991
- * request, regardless of hostname, resolves to the same project.
1992
- * Intended for `pnpm dev` / smoke tests where standing up a
1993
- * separate control plane is overkill.
1994
- */
1995
- controlPlaneUrl?: string;
1996
- /** Optional bearer token for the control-plane API. */
1997
- controlPlaneApiKey?: string;
1998
- /**
1999
- * Override the artifact client entirely. When supplied,
2000
- * `controlPlaneUrl` is ignored — useful for tests or custom transports.
2001
- */
2002
- client?: ArtifactApiClient | FileArtifactApiClient;
2003
- /** Config for the file-backed mode (used when `controlPlaneUrl === 'file'`). */
2004
- fileConfig?: FileArtifactApiClientConfig;
2005
- /** KernelManager LRU size. Default: 32. */
2006
- kernelCacheSize?: number;
2007
- /** KernelManager idle TTL (ms). Default: 15 min. */
2008
- kernelTtlMs?: number;
2009
- /** EnvironmentDriverRegistry cache TTL (ms). Default: 5 min. */
2010
- envCacheTtlMs?: number;
2011
- /** Artifact / hostname response cache TTL (ms). Default: 5 min. */
2012
- artifactCacheTtlMs?: number;
2013
- /** API prefix (carried for parity with cloud-stack). Default: /api/v1. */
2014
- apiPrefix?: string;
2015
- /**
2016
- * Host-supplied runtime plugins appended to the stack's default plugin
2017
- * list. This is the official seam for a host (e.g. the ObjectStack Cloud
2018
- * repo) to add **product/policy** plugins — marketplace install, cloud-
2019
- * account binding, set-initial-password — to the otherwise-neutral
2020
- * framework runtime, WITHOUT a framework release and without reaching into
2021
- * the returned array by hand.
2022
- *
2023
- * They are appended last, so they mount their routes after the framework
2024
- * plugins and can override/augment behaviour (e.g. supply a credentialled
2025
- * install path that the browse-only MarketplaceProxyPlugin deliberately
2026
- * does not). See docs/design/cloud-account-binding-marketplace-install.md
2027
- * (ADR §5.2 — "framework exposes seams; cloud supplies metadata + policy").
2028
- */
2029
- extraPlugins?: Plugin[];
2030
- /**
2031
- * Capability tokens force-mounted on EVERY per-environment kernel, in
2032
- * addition to whatever the app artifact declares in `requires`. Merged and
2033
- * de-duped with `bundle.requires` before the capability loader runs. This
2034
- * is the host seam for a cloud operator to make a capability ubiquitous
2035
- * across all tenants without editing each app — e.g. `['ai','aiStudio']`
2036
- * so every cloud environment supports AI-driven online development.
2037
- */
2038
- defaultRequires?: string[];
2039
- }
2040
- interface ObjectOSStackResult {
2041
- plugins: any[];
2042
- api: {
2043
- enableProjectScoping: true;
2044
- projectResolution: 'auto';
2045
- requireAuth: true;
2046
- };
2047
- }
2048
- declare function createObjectOSStack(config: ObjectOSStackConfig): Promise<ObjectOSStackResult>;
2049
-
2050
1766
  /**
2051
1767
  * MarketplaceProxyPlugin
2052
1768
  *
@@ -2315,265 +2031,6 @@ declare const DEFAULT_CLOUD_URL = "https://cloud.objectos.ai";
2315
2031
  */
2316
2032
  declare function resolveCloudUrl(explicit?: string | null): string;
2317
2033
 
2318
- /**
2319
- * Per-project driver registry contract.
2320
- *
2321
- * Resolves a project (by hostname or ID) and produces an instantiated
2322
- * `IDataDriver` bound to that project's physical database. Concrete
2323
- * implementations sit on top of either:
2324
- * - the ObjectStack Cloud HTTP API (see {@link ArtifactEnvironmentRegistry})
2325
- * - a local file artifact (see {@link FileArtifactApiClient} +
2326
- * {@link ArtifactEnvironmentRegistry})
2327
- *
2328
- * The contract lives in `@objectstack/runtime` so the runtime can
2329
- * express "fetch artifact + boot per-project kernel" without taking a
2330
- * dependency on the cloud control plane.
2331
- */
2332
-
2333
- type IDataDriver$1 = Contracts.IDataDriver;
2334
- interface EnvironmentDriverRegistry {
2335
- /** Resolve a project by hostname. Returns `null` when unknown. */
2336
- resolveByHostname(host: string): Promise<{
2337
- environmentId: string;
2338
- driver: IDataDriver$1;
2339
- } | null>;
2340
- /** Resolve a project's driver by ID. Returns `null` when unknown. */
2341
- resolveById(environmentId: string): Promise<IDataDriver$1 | null>;
2342
- /**
2343
- * Look up the cached project row + driver by ID without triggering a
2344
- * remote/file fetch. Returns the full cached entry when fresh.
2345
- */
2346
- peekById(environmentId: string): {
2347
- environmentId: string;
2348
- driver: IDataDriver$1;
2349
- project: any;
2350
- } | null;
2351
- /** Drop cached entries for the given project. */
2352
- invalidate(environmentId: string): void;
2353
- }
2354
-
2355
- /**
2356
- * EnvironmentDriverRegistry implementation that talks to the control plane
2357
- * over HTTP via {@link ArtifactApiClient}.
2358
- *
2359
- * Mirrors {@link DefaultEnvironmentDriverRegistry} from `environment-registry.ts`
2360
- * but does **not** read from a local control-plane database. Hostname →
2361
- * environmentId resolution and per-project runtime config (database URL /
2362
- * driver) come from the control plane API.
2363
- *
2364
- * The cached `project` payload exposed by `peekById()` is shaped to look
2365
- * like a `sys_environment` row so callers downstream (notably
2366
- * `ArtifactKernelFactory`) can read `id`, `organization_id`,
2367
- * `database_url` and `database_driver` without branching.
2368
- */
2369
-
2370
- type IDataDriver = Contracts.IDataDriver;
2371
- interface ArtifactEnvironmentRegistryConfig {
2372
- client: ArtifactApiClient;
2373
- /** Cache TTL for resolved drivers in ms. Default: 5 min. */
2374
- cacheTtlMs?: number;
2375
- /** Optional logger. */
2376
- logger?: {
2377
- info?: (...a: any[]) => void;
2378
- warn?: (...a: any[]) => void;
2379
- error?: (...a: any[]) => void;
2380
- };
2381
- }
2382
- declare class ArtifactEnvironmentRegistry implements EnvironmentDriverRegistry {
2383
- private readonly client;
2384
- private readonly cacheTTL;
2385
- private readonly logger;
2386
- private readonly hostnameCache;
2387
- private readonly idCache;
2388
- private readonly pending;
2389
- constructor(config: ArtifactEnvironmentRegistryConfig);
2390
- resolveByHostname(host: string): Promise<{
2391
- environmentId: string;
2392
- driver: IDataDriver;
2393
- } | null>;
2394
- resolveById(environmentId: string): Promise<IDataDriver | null>;
2395
- peekById(environmentId: string): {
2396
- environmentId: string;
2397
- driver: IDataDriver;
2398
- project: any;
2399
- } | null;
2400
- invalidate(environmentId: string): void;
2401
- private buildCacheEntry;
2402
- }
2403
-
2404
- interface ArtifactKernelFactoryConfig {
2405
- client: ArtifactApiClient;
2406
- envRegistry: EnvironmentDriverRegistry;
2407
- /** Optional logger. */
2408
- logger?: {
2409
- info?: (...a: any[]) => void;
2410
- warn?: (...a: any[]) => void;
2411
- error?: (...a: any[]) => void;
2412
- };
2413
- /** Optional kernel constructor config. */
2414
- kernelConfig?: ConstructorParameters<typeof ObjectKernel>[0];
2415
- /**
2416
- * Base secret used to derive per-project AuthPlugin secrets via
2417
- * HKDF-style HMAC-SHA256(baseSecret, environmentId). Falls back to
2418
- * `process.env.OS_AUTH_SECRET` / `AUTH_SECRET` at construction time.
2419
- */
2420
- authBaseSecret?: string;
2421
- /**
2422
- * Capability tokens force-mounted on every per-environment kernel, merged
2423
- * (and de-duped by the loader) with the artifact's own `requires`. Lets a
2424
- * host make a capability ubiquitous across tenants — e.g. `['ai','aiStudio']`.
2425
- */
2426
- defaultRequires?: string[];
2427
- }
2428
- declare class ArtifactKernelFactory implements EnvironmentKernelFactory {
2429
- private readonly client;
2430
- private readonly envRegistry;
2431
- private readonly logger;
2432
- private readonly kernelConfig?;
2433
- private readonly authBaseSecret;
2434
- private readonly defaultRequires;
2435
- constructor(config: ArtifactKernelFactoryConfig);
2436
- create(environmentId: string): Promise<ObjectKernel>;
2437
- }
2438
-
2439
- /**
2440
- * AuthProxyPlugin
2441
- *
2442
- * Mounts a single `/api/v1/auth/*` wildcard route on the host's Hono server
2443
- * that forwards every request to the per-project `AuthManager` registered
2444
- * by `ArtifactKernelFactory`.
2445
- *
2446
- * Why a dedicated plugin: AuthPlugin (better-auth) registers its routes by
2447
- * grabbing the host's `http-server` service from its own `PluginContext`.
2448
- * In objectos runtime mode AuthPlugin lives on a per-project kernel — it
2449
- * has no access to the host's HTTP server, so its `kernel:ready` route
2450
- * registration is a no-op. The dispatcher plugin's wildcard registration
2451
- * via `IHttpServer.post('/auth/*', …)` proved unreliable in practice,
2452
- * so this plugin uses Hono's raw app directly (same path AuthPlugin took
2453
- * historically) which is rock solid.
2454
- *
2455
- * Routing:
2456
- * 1. Resolve the project from the request hostname via `env-registry`.
2457
- * 2. Acquire the project's kernel via `kernel-manager`.
2458
- * 3. Look up the `auth` service on that kernel — this is the better-auth
2459
- * handler injected by `ArtifactKernelFactory`.
2460
- * 4. Build a Web `Request` using the project's canonical baseUrl and
2461
- * hand it to the better-auth handler.
2462
- * 5. Stream the response back through Hono.
2463
- */
2464
-
2465
- declare class AuthProxyPlugin implements Plugin {
2466
- readonly name = "com.objectstack.runtime.auth-proxy";
2467
- readonly version = "1.0.0";
2468
- init: (_ctx: PluginContext) => Promise<void>;
2469
- start: (ctx: PluginContext) => Promise<void>;
2470
- }
2471
-
2472
- /**
2473
- * Provider id used in better-auth's `genericOAuth` and as part of the
2474
- * callback URL: `/api/v1/auth/oauth2/callback/<PROVIDER_ID>`. Keep stable —
2475
- * changing it invalidates every registered redirect_uri.
2476
- */
2477
- declare const PLATFORM_SSO_PROVIDER_ID = "objectstack-cloud";
2478
- /**
2479
- * Derive the per-project OAuth client_id used in `sys_oauth_application`
2480
- * (cloud side) and {@link genericOAuth} config (project side).
2481
- */
2482
- declare function derivePlatformSsoClientId(environmentId: string): string;
2483
- /**
2484
- * Derive the per-project OAuth client_secret deterministically from the
2485
- * shared master secret. HMAC-SHA256(baseSecret, 'oauth-client:' + environmentId)
2486
- * yields a 64-char hex string that is:
2487
- * - stable across container cold-starts (no DB lookup needed)
2488
- * - independent per project (compromising one does not compromise others)
2489
- * - rotatable via OS_AUTH_SECRET rotation (invalidates all SSO clients)
2490
- *
2491
- * This is the **plaintext** value the RP must present at the token endpoint.
2492
- * The cloud-side `sys_oauth_application.client_secret` column instead stores
2493
- * {@link hashPlatformSsoClientSecret}(plaintext) — better-auth's oauth-provider
2494
- * defaults to `storeClientSecret: 'hashed'` (SHA-256 + base64url) when the JWT
2495
- * plugin is enabled, and looks up the row by hashing the presented secret.
2496
- */
2497
- declare function derivePlatformSsoClientSecret(baseSecret: string, environmentId: string): string;
2498
- /**
2499
- * Build the redirect_uri better-auth's `genericOAuth` plugin will use
2500
- * when the project kernel mounts the provider with id
2501
- * {@link PLATFORM_SSO_PROVIDER_ID}. MUST be one of the URIs registered
2502
- * on the cloud-side oauth client or the authorization server will reject
2503
- * the callback with `invalid_request`.
2504
- */
2505
- declare function buildPlatformSsoRedirectUri(hostname: string, basePath?: string): string;
2506
- interface SeedPlatformSsoClientOptions {
2507
- /**
2508
- * Cloud control-plane ObjectQL engine. Must expose `find(object, query)`,
2509
- * `insert(object, data)`, and `update(object, data, {where})`. Both the
2510
- * `apps/cloud` boot kernel (via `kernel.getService('objectql')`) and the
2511
- * dispatcher's local `ql` reference satisfy this shape.
2512
- */
2513
- ql: {
2514
- find: (object: string, query: any, opts?: any) => Promise<any>;
2515
- insert: (object: string, data: any, opts?: any) => Promise<any>;
2516
- update: (object: string, data: any, where: any, opts?: any) => Promise<any>;
2517
- };
2518
- /** Project id (also used to derive client_id + client_secret). */
2519
- environmentId: string;
2520
- /**
2521
- * Project hostname (e.g. `acme-crm.objectos.ai`). Optional — projects
2522
- * may be created before a hostname is assigned, in which case no
2523
- * redirect_uri is registered yet and the row is upserted with an
2524
- * empty `redirect_uris` array. Calling this function again once the
2525
- * hostname is known will merge the new URI in.
2526
- */
2527
- hostname?: string | null;
2528
- /** Master secret shared between cloud and project containers. */
2529
- baseSecret: string;
2530
- /** Optional logger for diagnostics. */
2531
- logger?: {
2532
- info?: (...a: any[]) => void;
2533
- warn?: (...a: any[]) => void;
2534
- error?: (...a: any[]) => void;
2535
- };
2536
- /** When true, rethrow insert/update errors instead of swallowing them.
2537
- * Backfill uses this to surface real failures via the admin endpoint. */
2538
- throwOnError?: boolean;
2539
- }
2540
- /**
2541
- * Idempotently upsert a `sys_oauth_application` row for the given project.
2542
- * Re-running with the same `environmentId` is a no-op (the deterministic
2543
- * `client_id` is uniquely indexed and the secret derivation is stable).
2544
- * Re-running with a new `hostname` adds the new redirect_uri to the
2545
- * existing row's JSON array.
2546
- */
2547
- declare function seedPlatformSsoClient(opts: SeedPlatformSsoClientOptions): Promise<void>;
2548
- interface BackfillPlatformSsoClientsOptions {
2549
- ql: SeedPlatformSsoClientOptions['ql'];
2550
- baseSecret: string;
2551
- logger?: {
2552
- info?: (...a: any[]) => void;
2553
- warn?: (...a: any[]) => void;
2554
- error?: (...a: any[]) => void;
2555
- };
2556
- /** Hard cap on rows scanned (default: 1000). */
2557
- limit?: number;
2558
- }
2559
- /**
2560
- * Scan `sys_environment` and ensure every active project has a corresponding
2561
- * `sys_oauth_application` row. Intended to run once at cloud boot — the
2562
- * happy path is dominated by the project-create hook
2563
- * ({@link seedPlatformSsoClient}); the backfill exists so projects
2564
- * created before this feature shipped also get SSO support without an
2565
- * out-of-band migration.
2566
- */
2567
- declare function backfillPlatformSsoClients(opts: BackfillPlatformSsoClientsOptions): Promise<{
2568
- scanned: number;
2569
- seeded: number;
2570
- alreadyExisted: number;
2571
- failures: Array<{
2572
- environmentId: string;
2573
- error: string;
2574
- }>;
2575
- }>;
2576
-
2577
2034
  /**
2578
2035
  * # Hook & Action Body Sandbox
2579
2036
  *
@@ -2804,4 +2261,4 @@ declare function actionBodyRunnerFactory(runner: ScriptRunner, opts: FactoryOpti
2804
2261
  timeoutMs?: number;
2805
2262
  }) => ((actionCtx: any) => Promise<unknown>) | undefined;
2806
2263
 
2807
- export { AppPlugin, ArtifactApiClient, type ArtifactApiClientConfig, ArtifactEnvironmentRegistry, type ArtifactEnvironmentRegistryConfig, ArtifactKernelFactory, type ArtifactKernelFactoryConfig, AuthProxyPlugin, type BackfillPlatformSsoClientsOptions, DEFAULT_CLOUD_URL, DEFAULT_RATE_LIMITS, type DefaultHostConfigOptions, type DefaultHostConfigResult, type DispatcherPluginConfig, DriverPlugin, type EnvironmentArtifactResponse, type EnvironmentDriverRegistry, type EnvironmentKernelFactory, type EnvironmentRuntimeConfig, type ExternalSchemaDriftEvent, ExternalValidationPlugin, FileArtifactApiClient, type FileArtifactApiClientConfig, HttpDispatcher, type HttpDispatcherResult, type HttpProtocolContext, HttpServer, KernelManager, type KernelManagerConfig, type LoadArtifactBundleOptions, MarketplaceInstallLocalPlugin, type MarketplaceInstallLocalPluginConfig, MarketplaceProxyPlugin, type MarketplaceProxyPluginConfig, MiddlewareManager, type ObjectOSStackConfig, type ObjectOSStackResult, ObservabilityServicePlugin, type ObservabilityServicePluginOptions, PLATFORM_SSO_PROVIDER_ID, QuickJSScriptRunner, type QuickJSScriptRunnerOptions, type RateLimitBucketConfig, type RateLimitDecision, type RateLimitDefaults, type RateLimitStore, RateLimiter, type ResolvedHostname, Runtime, type RuntimeConfig, RuntimeConfigPlugin, type RuntimeConfigPluginConfig, SYSTEM_ENVIRONMENT_ID, SandboxError, type ScriptContext, type ScriptOrigin, type ScriptResult, type ScriptRunOptions, type ScriptRunner, type SecurityHeadersOptions, SeedLoaderService, type SeedPlatformSsoClientOptions, type StandaloneStackConfig, type StandaloneStackResult, type SystemEnvironmentPluginConfig, type TraceContext, UnimplementedScriptRunner, actionBodyRunnerFactory, backfillPlatformSsoClients, buildPlatformSsoRedirectUri, buildSecurityHeaders, collectBundleActions, collectBundleFunctions, collectBundleHooks, createDefaultHostConfig, createDispatcherPlugin, createExternalValidationPlugin, createObjectOSStack, createStandaloneStack, createSystemEnvironmentPlugin, derivePlatformSsoClientId, derivePlatformSsoClientSecret, extractRequestId, formatTraceparent, generateRequestId, hookBodyRunnerFactory, isHttpUrl, loadArtifactBundle, mergeRuntimeModule, parseTraceparent, readArtifactSource, resolveCloudUrl, resolveDefaultArtifactPath, resolveErrorReporter, resolveMetrics, resolveObjectStackHome, resolveRequestId, seedPlatformSsoClient };
2264
+ export { AppPlugin, DEFAULT_CLOUD_URL, DEFAULT_RATE_LIMITS, type DefaultHostConfigOptions, type DefaultHostConfigResult, type DispatcherPluginConfig, DriverPlugin, type EnvironmentDriverRegistry, type ExternalSchemaDriftEvent, ExternalValidationPlugin, HttpDispatcher, type HttpDispatcherResult, type HttpProtocolContext, HttpServer, type KernelManager, type LoadArtifactBundleOptions, MarketplaceInstallLocalPlugin, type MarketplaceInstallLocalPluginConfig, MarketplaceProxyPlugin, type MarketplaceProxyPluginConfig, MiddlewareManager, ObservabilityServicePlugin, type ObservabilityServicePluginOptions, QuickJSScriptRunner, type QuickJSScriptRunnerOptions, type RateLimitBucketConfig, type RateLimitDecision, type RateLimitDefaults, type RateLimitStore, RateLimiter, Runtime, type RuntimeConfig, RuntimeConfigPlugin, type RuntimeConfigPluginConfig, SYSTEM_ENVIRONMENT_ID, SandboxError, type ScriptContext, type ScriptOrigin, type ScriptResult, type ScriptRunOptions, type ScriptRunner, type SecurityHeadersOptions, SeedLoaderService, type StandaloneStackConfig, type StandaloneStackResult, type SystemEnvironmentPluginConfig, type TraceContext, UnimplementedScriptRunner, actionBodyRunnerFactory, buildSecurityHeaders, collectBundleActions, collectBundleFunctions, collectBundleHooks, createDefaultHostConfig, createDispatcherPlugin, createExternalValidationPlugin, createStandaloneStack, createSystemEnvironmentPlugin, extractRequestId, formatTraceparent, generateRequestId, hookBodyRunnerFactory, isHttpUrl, loadArtifactBundle, mergeRuntimeModule, parseTraceparent, readArtifactSource, resolveCloudUrl, resolveDefaultArtifactPath, resolveErrorReporter, resolveMetrics, resolveObjectStackHome, resolveRequestId };